@oddington/sdk 0.1.11 → 0.1.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -8,14 +8,112 @@ Data and Files APIs work in browsers. Installing the package grants no authority
8
8
  npm install @oddington/sdk
9
9
  ```
10
10
 
11
+ An authored routine exports an inert definition from its conventional `run.ts`:
12
+
13
+ ```ts
14
+ import { defineRoutine, file, schema } from '@oddington/sdk';
15
+
16
+ export default defineRoutine({
17
+ input: schema.object({ document: file() }),
18
+ steps: { read: null, extract: schema.object({ pages: schema.integer() }) },
19
+ result: schema.object({ recordsCreated: schema.integer() }),
20
+ async run(input, os) {
21
+ await os.step('read');
22
+ await os.step('extract', { pages: 3 });
23
+ // Perform customer-owned work with the supplied installed capabilities.
24
+ return { recordsCreated: 1 };
25
+ },
26
+ });
27
+ ```
28
+
29
+ Importing or defining performs no business work. Input, step summaries and returned
30
+ results are inferred from serializable declarations and checked at execution.
31
+ Omitted input accepts only `{}`; omitted steps permit no public report; omitted
32
+ result permits no returned value. `file()` declares a complete OS File descriptor;
33
+ the trusted launcher separately verifies current File-use authority. The supplied
34
+ runtime contains `run.id`, `data`, `files`, `connections`, `inbox`, and `step`.
35
+ Accepted input and reported values are immutable snapshots.
36
+
37
+ The bounded grammar supports closed objects with required properties, arrays with
38
+ an explicit `maxItems` from 1 to 64, booleans, strings (at most 4096 code points),
39
+ finite numbers and safe integers with optional inclusive bounds, and Files.
40
+ Definitions allow at most 64 steps, 64 total object properties across their schemas,
41
+ and eight schema levels. Input, each summary and the result accept at most 256 KiB
42
+ of serialized JSON; functions, cycles and non-JSON values reject. No name, entry,
43
+ workspace, credentials or placement are authored in this definition.
44
+
45
+ The named CLI local launcher requires native Node TypeScript stripping and a
46
+ module `routines/package.json` whose `engines.node` range matches that executable.
47
+ Before inspection it pins the selected entry's reachable TypeScript/JavaScript
48
+ modules, checkout-local runtime packages, governing package metadata and static
49
+ assets referenced by `new URL('./asset', import.meta.url)`. Literal imports,
50
+ dynamic imports with literal specifiers, and literal CommonJS `require` are
51
+ supported; computed loaders/assets, loader aliases, `createRequire`, evaluated
52
+ code, `__dirname` assets, custom export conditions and export fallback arrays
53
+ are refused. This first slice accepts at most 20,000 reachable files and 256 MiB;
54
+ reachable symlinks, native addons and imports outside the pinned copy are refused.
55
+ Historical `runs` output, unrelated routines, unused packages and files, package
56
+ manager `.bin` links, caches and logs are excluded. Run evidence stays with the
57
+ durable Run; artifacts are explicit File outputs. Dependency setup remains an
58
+ explicit workspace action. JSON input transport is bounded to 256 KiB.
59
+ The launcher renews its own 90-second ownership lease while queued or running;
60
+ each execution retains the existing 15-minute temporary capability/connection
61
+ bound. Lost ownership fails the Attempt without repeating customer code.
62
+
11
63
  ```ts
12
64
  import { defineHandler, resolveAsk, routinePublicationClient } from '@oddington/sdk';
13
65
 
14
66
  const inbox = routinePublicationClient();
15
67
  ```
16
68
 
17
- The OS-owned browser entry point initializes the existing shared refreshing
18
- session source before app code runs:
69
+ The SDK owns browser provider setup, email/passcode actions, persisted restoration,
70
+ refresh and expiry. Create one opaque handle outside rendering and bind the app's
71
+ presentation to its state:
72
+
73
+ ```ts
74
+ import { auth, os, BrowserAuthError } from '@oddington/sdk';
75
+
76
+ const browserAuth = auth.create();
77
+ const unsubscribe = browserAuth.subscribe(state => {
78
+ // Render status, account.email/displayName, pending action and safe error reason.
79
+ });
80
+ const current = browserAuth.getState();
81
+ await browserAuth.sendCode(enteredEmail);
82
+ try {
83
+ await browserAuth.verifyCode(enteredEmail, enteredCode);
84
+ } catch (error) {
85
+ if (error instanceof BrowserAuthError) showFailure(error.reason);
86
+ }
87
+
88
+ os.init({ auth: browserAuth }); // Trusted workspace host startup only.
89
+ // Later: await browserAuth.signOut(); // Ends only this browser's session.
90
+ // On host teardown:
91
+ unsubscribe();
92
+ browserAuth.dispose();
93
+ ```
94
+
95
+ `auth.create()` accepts no options. Trusted same-origin OS setup selects the
96
+ provider internally through exact-origin `GET /api/auth`, owned by OS. The SDK
97
+ uses same-origin credentials, refuses redirects and bypasses caching. Missing or
98
+ invalid setup makes authentication unavailable without a legacy endpoint or
99
+ app-configured provider fallback; ordinary app modules do not consume this
100
+ internal bootstrap. State is `initializing`, `signed_out`, `signed_in`, `expired`
101
+ or `unavailable`; actions complete without returning a session. Invitation-only
102
+ code delivery never creates accounts or reveals account existence. Keep form
103
+ selection, entered values, copy and navigation in the app. The handle exposes no
104
+ provider, configuration, membership list or access/refresh credential. Consumers
105
+ never construct bearer headers. Multiple handles share provider coordination on
106
+ the same origin; each owns its subscription/disposal independently.
107
+
108
+ The hosted runtime routes that exact capability path directly to OS while the
109
+ app keeps its independently deployed interface and other paths. Operators must
110
+ activate and verify the OS route before releasing a consumer that depends on
111
+ it; installing this SDK does not deploy routes or migrate an app's sign-in UI.
112
+
113
+ Only trusted workspace host startup passes that exact registered handle to
114
+ `os.init`. Copying its public shape grants no authority. Fresh per-operation
115
+ authority fails closed on sign-out, replacement, expiry or disposal. Existing
116
+ hosts may continue supplying their shared refreshing session source:
19
117
 
20
118
  ```ts
21
119
  import { os, data, files, DataOperationError, FileOperationError } from '@oddington/sdk';
@@ -49,6 +147,15 @@ session subscription. Workspace existence and member access are checked by the
49
147
  OS service. App modules use capability namespaces; routines and handlers receive
50
148
  runtime context without calling `os.init`.
51
149
 
150
+ First-party Auth uses the handle without initializing workspace context. Its
151
+ bounded `browserAuth.authorization.get(id)`, `approve(id)` and `deny(id)` actions
152
+ support only the OS CLI request and fixed callback. Inspection/restoration never
153
+ approves a fresh request. Results are validated consent labels, safe status or a
154
+ completed `returnUrl`; Auth owns navigation. Decisions require prior inspection
155
+ and remain bound to the current request and account. The reserved auth origin
156
+ cannot obtain workspace capabilities even if a caller invokes `os.init` there.
157
+ Existing Inbox/customer apps and local browser sessions are separate migrations.
158
+
52
159
  The same `os.init({ auth })` call also consumes authenticated bootstrap state on
53
160
  an OS-launched IPv4 loopback origin. It checks `/_os/app/context` on that exact
54
161
  origin for every protected operation, without reading hosted access credentials.
@@ -164,6 +271,25 @@ The newer Local Host app imports are bundled and exercised separately under the
164
271
  same strict compatibility audit, so they cannot weaken or inflate the hosted
165
272
  Data/Files size comparison.
166
273
 
274
+ Auth uses a separately pinned installed fixture and **150,000 SDK-byte** ceiling.
275
+ The private `@supabase/auth-js` **2.116.0** build dependency is bundled into the
276
+ tarball, so installation needs no runtime provider dependency. Browser handle
277
+ creation loads its internal provider chunk lazily; unused auth/provider code
278
+ disappears from the Data/Files and Local Host fixture outputs. The audit counts
279
+ both shipped SDK inputs and every emitted helper, rejects unexpected inputs and
280
+ imports, and retains the full credential/server/customer leak scan. Node retains
281
+ the complete package-root exports; provider classes are never public.
282
+
283
+ Current auth measurement is **149,045 minified bytes**, including **5,177 fixture
284
+ bytes** and **143,868 SDK bytes**, with **38,870 total gzip bytes**. Current
285
+ unchanged Data/Files fixture measures **46,164 total / 5,893 fixture / 40,271 SDK
286
+ bytes**, with **14,316 total gzip bytes**.
287
+ Both fit their explicitly reviewed ceilings: **41,000 SDK bytes** for the pinned
288
+ Data/Files fixture and **150,000 SDK bytes** for the separately pinned auth fixture.
289
+ Those ceilings are fixed review decisions, not values regenerated from later
290
+ measurements. The installed auth realm/host-handoff proof passes alongside the
291
+ Node exports, declarations, tarball leak scan and browser compatibility audits.
292
+
167
293
  The browser budget uses pinned esbuild 0.28.1, browser platform, bundled ESM,
168
294
  `es2022`, minification, default tree shaking and inline legal comments, with no
169
295
  source map, aliases, polyfills, external modules or consumer defines. Its fixed
@@ -173,17 +299,17 @@ bytes; generated helpers count toward the SDK. Gzip is diagnostic only.
173
299
 
174
300
  | Same named-import fixture | Legacy diagnostic | Current strict proof |
175
301
  | --- | ---: | ---: |
176
- | Total minified JavaScript bytes | 488,539 | 40,534 |
177
- | Fixture-attributed bytes | 5,954 | 5,897 |
178
- | SDK bytes | 482,585 | 34,637 |
179
- | Total gzip bytes | 198,612 | 12,414 |
302
+ | Total minified JavaScript bytes | 488,539 | 46,164 |
303
+ | Fixture-attributed bytes | 5,954 | 5,893 |
304
+ | SDK bytes | 482,585 | 40,271 |
305
+ | Total gzip bytes | 198,612 | 14,316 |
180
306
 
181
307
  The legacy diagnostic externalized three unresolved Node built-ins and failed
182
308
  browser compatibility. The current strict proof has no imports, shims or warnings
183
- and reduces SDK bytes by 92.8%. Its literal ceiling is **40,000 SDK bytes**:
184
- 5,363 bytes (15.5%) of headroom for bounded client fixes. The increase from the
185
- original 24,000-byte measurement accounts for the subsequently added Local Host
186
- bootstrap that `os.init` must select at runtime. The verifier pins the fixture
309
+ and reduces SDK bytes by 91.7%. Its literal ceiling is **41,000 SDK bytes**:
310
+ 729 bytes of deliberately limited headroom. The reviewed measurement includes
311
+ the Local Host bootstrap and opaque browser-auth handoff that `os.init` must
312
+ select at runtime. The verifier pins the fixture
187
313
  hash and bundler version; changing either requires a reviewed measurement
188
314
  comparison. Future ceiling increases require explicit review of added browser
189
315
  capability and must never be automatically regenerated from an oversized output.
@@ -221,10 +347,10 @@ configured root fails with `unsupported_context`. Files have one durable home;
221
347
  `both` is invalid. Local retention establishes no hosted registration.
222
348
  Fresh runs recover the same creator's File by its request key, and verify
223
349
  retained bytes before returning a descriptor or content. A dead parent fails
224
- closed. `os routine prepare` seals checked local File references and defers
225
- new remote Files, their enrichments and explicit uploads until `os routine publish`; legacy Inbox
226
- file preparation remains additive. Existing hosted Files are read-only during
227
- preparation.
350
+ closed. `os routine run <name>` stages newly created Files locally for that Run,
351
+ including local-placement Files, and defers promotion, enrichments and explicit
352
+ uploads until `os routine publish <run>`. Legacy Inbox file capture remains
353
+ additive. Existing hosted Files are read-only during local execution.
228
354
  `upload(id)` preserves the File descriptor, creator access and existing Source
229
355
  references. Local bytes remain authoritative until all remote objects verify;
230
356
  retrying a completed upload returns the same File. Later reads and enrichments
@@ -248,6 +374,22 @@ Response without consuming its body. Local transfer is bounded and buffered
248
374
  before constructing that Response. Creation grants private creator access;
249
375
  workspace membership alone never grants another creator's File.
250
376
 
377
+ `await files.delete(fileId)` removes an unreferenced File's original and all
378
+ retained enrichments under its current member or installed-routine creator.
379
+ It returns `void` only after physical cleanup and durable completion; a completed
380
+ retry succeeds. Independent retention returns `conflict` without changing the
381
+ File. `access_denied` and `integrity` remain terminal; `unavailable` and
382
+ `capacity` are retryable. Pending deletion denies reads, writes, new uses and
383
+ creation-key replay while the same current creator retries cleanup. Deletion
384
+ uses the authenticated parent for managed local Files and uploaded caches.
385
+ R2 cleanup verifies the registered immutable key and native revision, removes
386
+ only that object and confirms absence. Each physical writer owns a distinct
387
+ candidate key, so a delayed writer cannot recreate the deleted representation.
388
+ Pending admission is never success.
389
+ When a completion reply is lost, retry `files.delete` with the same File ID and
390
+ current creator authority. Keep stable creation keys separate from deletion
391
+ recovery: a completed deleted key is denied and cannot recreate the File.
392
+
251
393
  `enrich` accepts a string encoded as UTF-8 or exact `Uint8Array` bytes, a safe
252
394
  flat filename, and an optional MIME hint. OS verifies text/Markdown/JSON or
253
395
  reduces the content to generic binary metadata; it performs no extraction or
@@ -287,8 +429,8 @@ if (next) await general.records.select({
287
429
  Binding is inert. Each operation resolves current workspace, endpoint and
288
430
  short-lived authority from the trusted app host or managed routine context.
289
431
  Customer calls cannot configure those values. Missing or expired context
290
- rejects; a named handle does not expand repository access. During explicit
291
- `os routine prepare`, the same calls use the matched checkout's committed
432
+ rejects; a named handle does not expand repository access. During
433
+ `os routine run <name>`, the same calls use the matched checkout's committed
292
434
  version through a private parent transport. Dirty files are ignored. An OS
293
435
  cache outside the checkout reuses derived data; reads require no remote access
294
436
  or preparation storage. Later accepted record effects advance the run's view
@@ -354,8 +496,8 @@ projection, highlighting, embedding or historical version option.
354
496
  Matches have deterministic order and complete canonical Records. Empty matches
355
497
  succeed with the exact searched version. Members search latest published data;
356
498
  routines search their captured version, advancing only through their own
357
- publication. Immediate `os routine run` uses hosted search. During
358
- `os routine prepare`, search uses the same committed local Index as select,
499
+ publication. Hosted and app children publish immediately. During
500
+ `os routine run <name>`, search uses the same committed local Index as select,
359
501
  ignores dirty/untracked files and advances only to that run's exact proposed
360
502
  version after its own Changes publication. Validation does not advance reads;
361
503
  another publisher does not move the captured view. Search Records can supply
@@ -389,7 +531,7 @@ For reads, only `capacity` and `unavailable` are retryable; an error never inclu
389
531
  server details. Server boundaries own query bounds and authorization.
390
532
 
391
533
  Schema-driven creates, guarded updates and guarded deletions use the same handle
392
- in immediate and prepared routines:
534
+ in immediate and captured local routines:
393
535
 
394
536
  ```ts
395
537
  const changes = general.changes();
@@ -418,8 +560,8 @@ only after its exact index is ready. Later current reads discover that version.
418
560
  Deleted identities keep their captured versions; deletion creates no new Record
419
561
  version or tombstone. A Source deletion removes only that guarded Record, retaining
420
562
  its File, enrichments and existing durable uses.
421
- During `os routine prepare`, publication retains the exact proposal and
422
- advances only run-local reads. `os routine publish` later applies and indexes
563
+ During `os routine run <name>`, publication retains the exact proposal and
564
+ advances only run-local reads. `os routine publish <run>` later applies and indexes
423
565
  that immutable proposal; unrelated staged and unstaged work is preserved.
424
566
 
425
567
  `invalid_change` and known-unapplied `conflict` reject the batch.
@@ -468,14 +610,16 @@ The defaults read `ODDINGTON_ROUTINE_WORKSPACE`, `ODDINGTON_ROUTINE_RUN` and
468
610
  `ODDINGTON_INBOX_WORKING_ROOT` (or the current directory). Callers may supply the
469
611
  existing `workspace`, `capability` and `workingRoot` options. Runtime context
470
612
  provides authority; callers cannot select repository remotes or expand grants.
471
- `os routine run` publishes as capabilities are called. The same calls prepare
472
- work when launched with `os routine prepare`, and `os routine publish` owns
473
- explicit publication and recovery of that prepared run. Preparation may contact
474
- OS for authentication and reads, and declared connections may contact their
475
- providers; it does not promise offline execution. Execution location and File
476
- storage are independent of the publication lifecycle. `os routine run --local`
477
- remains a deprecated compatibility alias for `os routine prepare` and prints
478
- the replacement command.
613
+ `os routine run <name> [--input <file|->]` executes the conventional inert
614
+ definition with declared JSON input and captures OS-owned changes.
615
+ `os routine publish <run>` sends those exact changes and recovers confirmed
616
+ receipts without invoking business code again. Filesystem, model and installed
617
+ connection effects occur immediately, including before a later failure.
618
+ No-change success retains Run evidence without requiring publish. `--json`
619
+ returns typed Run state/counts/result, or confirmed publish receipts/links.
620
+ The former `prepare`, `run --local`, command/argv and authority-selection
621
+ flags are rejected. Remote execution and deployment are not implemented here.
622
+ Checkout-backed app children retain immediate publication.
479
623
 
480
624
  Retain evidence with `inbox.files.add({ path: 'evidence.txt' })`. Paths stay within
481
625
  the invocation's working root and uploaded bytes are verified against the retained