[Worker data plane]

Script library API: /api/scripts, D1 schema, drafts, revisions, and in-memory fallback.

Worker data plane

Abstract

The Worker data plane for user scripts is frontend/worker/src/scripts.ts — REST under /api/scripts with Bearer auth (auth). Persistence prefers D1 (env.DB); without D1, per-isolate in-memory maps support local wrangler dev.

The PWA cloud storage plugin is the primary client.

Conceptual model

Interface surface

All routes require valid API key unless noted.

PathMethodsBehavior
/api/scriptsGETList meta for userId
/api/scriptsPOSTCreate script
/api/scripts/:idGETRead full document
/api/scripts/:idPUTUpsert; honors If-Match revision
/api/scripts/:idDELETERemove
/api/scripts/_draftGET/PUTPer-user draft buffer

Document fields

FieldNotes
idClient or server assigned
name, description, pathMeta
contentPine source
revisionOpaque string (rev_…) for concurrency
created_at / updated_atms epochs (API may camelCase in JSON)

Concurrency

PUT with If-Match: <revision> rejects mismatched revisions (409 path covered in security tests). Fresh writes mint newRevision().

Internals

D1 schema (schemas/scripts.sql)

CREATE TABLE scripts (
  user_id TEXT NOT NULL,
  id TEXT NOT NULL,
  name TEXT NOT NULL,
  description TEXT,
  path TEXT,
  content TEXT NOT NULL,
  revision TEXT NOT NULL,
  created_at INTEGER NOT NULL,
  updated_at INTEGER NOT NULL,
  PRIMARY KEY (user_id, id)
);

CREATE TABLE script_drafts (
  user_id TEXT PRIMARY KEY,
  content TEXT NOT NULL,
  name TEXT,
  updated_at INTEGER NOT NULL
);

Apply:

wrangler d1 execute pynescript --remote --file=schemas/scripts.sql
wrangler d1 execute pynescript --local  --file=schemas/scripts.sql

Binding name in wrangler.toml must be DB (code uses env.DB). Database name can be pynescript.

Partitioning

userId = SHA-256(api_key).hex.slice(0, 32) — raw keys are not stored as D1 partition ids.

Health feature flags

{
  "status": "healthy",
  "service": "pynescript-axis-worker",
  "features": { "scripts": true, "d1": true/false, "keys": true/false }
}

Optional R2

BUNDLES R2 is reserved for indicator / pynescript wheels (RUNTIME.md). Not required for scripts CRUD.

Invariants & edge cases

  1. Memory store is not multi-isolate durable — production needs D1.
  2. Drafts ≠ commits — drafts are crash buffers; library list is separate.
  3. CORS — scripts handler sets allow headers including If-Match.
  4. Cloud plugin dual-write — browser still saves drafts to local storage.

Failure modes

Code / symptomMeaning
401Missing/invalid Bearer
404Unknown script id for user
409Revision conflict
Empty list after deploySchema not applied / wrong DB id

See also