@oddington/sdk 0.1.11 → 0.1.12

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,103 @@ 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. State is `initializing`, `signed_out`, `signed_in`, `expired`
97
+ or `unavailable`; actions complete without returning a session. Invitation-only
98
+ code delivery never creates accounts or reveals account existence. Keep form
99
+ selection, entered values, copy and navigation in the app. The handle exposes no
100
+ provider, configuration, membership list or access/refresh credential. Consumers
101
+ never construct bearer headers. Multiple handles share provider coordination on
102
+ the same origin; each owns its subscription/disposal independently.
103
+
104
+ Only trusted workspace host startup passes that exact registered handle to
105
+ `os.init`. Copying its public shape grants no authority. Fresh per-operation
106
+ authority fails closed on sign-out, replacement, expiry or disposal. Existing
107
+ hosts may continue supplying their shared refreshing session source:
19
108
 
20
109
  ```ts
21
110
  import { os, data, files, DataOperationError, FileOperationError } from '@oddington/sdk';
@@ -49,6 +138,15 @@ session subscription. Workspace existence and member access are checked by the
49
138
  OS service. App modules use capability namespaces; routines and handlers receive
50
139
  runtime context without calling `os.init`.
51
140
 
141
+ First-party Auth uses the handle without initializing workspace context. Its
142
+ bounded `browserAuth.authorization.get(id)`, `approve(id)` and `deny(id)` actions
143
+ support only the OS CLI request and fixed callback. Inspection/restoration never
144
+ approves a fresh request. Results are validated consent labels, safe status or a
145
+ completed `returnUrl`; Auth owns navigation. Decisions require prior inspection
146
+ and remain bound to the current request and account. The reserved auth origin
147
+ cannot obtain workspace capabilities even if a caller invokes `os.init` there.
148
+ Existing Inbox/customer apps and local browser sessions are separate migrations.
149
+
52
150
  The same `os.init({ auth })` call also consumes authenticated bootstrap state on
53
151
  an OS-launched IPv4 loopback origin. It checks `/_os/app/context` on that exact
54
152
  origin for every protected operation, without reading hosted access credentials.
@@ -164,6 +262,25 @@ The newer Local Host app imports are bundled and exercised separately under the
164
262
  same strict compatibility audit, so they cannot weaken or inflate the hosted
165
263
  Data/Files size comparison.
166
264
 
265
+ Auth uses a separately pinned installed fixture and **150,000 SDK-byte** ceiling.
266
+ The private `@supabase/auth-js` **2.116.0** build dependency is bundled into the
267
+ tarball, so installation needs no runtime provider dependency. Browser handle
268
+ creation loads its internal provider chunk lazily; unused auth/provider code
269
+ disappears from the Data/Files and Local Host fixture outputs. The audit counts
270
+ both shipped SDK inputs and every emitted helper, rejects unexpected inputs and
271
+ imports, and retains the full credential/server/customer leak scan. Node retains
272
+ the complete package-root exports; provider classes are never public.
273
+
274
+ Current auth measurement is **148,677 minified bytes**, including **4,799 fixture
275
+ bytes** and **143,878 SDK bytes**, with **38,758 total gzip bytes**. The provider
276
+ accounts for 104,577 emitted bytes. Current unchanged Data/Files fixture measures
277
+ **46,178 total / 5,893 fixture / 40,285 SDK bytes**, with **14,338 total gzip bytes**.
278
+ Both fit their explicitly reviewed ceilings: **41,000 SDK bytes** for the pinned
279
+ Data/Files fixture and **150,000 SDK bytes** for the separately pinned auth fixture.
280
+ Those ceilings are fixed review decisions, not values regenerated from later
281
+ measurements. The installed auth realm/host-handoff proof passes alongside the
282
+ Node exports, declarations, tarball leak scan and browser compatibility audits.
283
+
167
284
  The browser budget uses pinned esbuild 0.28.1, browser platform, bundled ESM,
168
285
  `es2022`, minification, default tree shaking and inline legal comments, with no
169
286
  source map, aliases, polyfills, external modules or consumer defines. Its fixed
@@ -173,17 +290,17 @@ bytes; generated helpers count toward the SDK. Gzip is diagnostic only.
173
290
 
174
291
  | Same named-import fixture | Legacy diagnostic | Current strict proof |
175
292
  | --- | ---: | ---: |
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 |
293
+ | Total minified JavaScript bytes | 488,539 | 46,178 |
294
+ | Fixture-attributed bytes | 5,954 | 5,893 |
295
+ | SDK bytes | 482,585 | 40,285 |
296
+ | Total gzip bytes | 198,612 | 14,338 |
180
297
 
181
298
  The legacy diagnostic externalized three unresolved Node built-ins and failed
182
299
  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
300
+ and reduces SDK bytes by 91.7%. Its literal ceiling is **41,000 SDK bytes**:
301
+ 715 bytes of deliberately limited headroom. The reviewed measurement includes
302
+ the Local Host bootstrap and opaque browser-auth handoff that `os.init` must
303
+ select at runtime. The verifier pins the fixture
187
304
  hash and bundler version; changing either requires a reviewed measurement
188
305
  comparison. Future ceiling increases require explicit review of added browser
189
306
  capability and must never be automatically regenerated from an oversized output.
@@ -221,10 +338,10 @@ configured root fails with `unsupported_context`. Files have one durable home;
221
338
  `both` is invalid. Local retention establishes no hosted registration.
222
339
  Fresh runs recover the same creator's File by its request key, and verify
223
340
  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.
341
+ closed. `os routine run <name>` stages newly created Files locally for that Run,
342
+ including local-placement Files, and defers promotion, enrichments and explicit
343
+ uploads until `os routine publish <run>`. Legacy Inbox file capture remains
344
+ additive. Existing hosted Files are read-only during local execution.
228
345
  `upload(id)` preserves the File descriptor, creator access and existing Source
229
346
  references. Local bytes remain authoritative until all remote objects verify;
230
347
  retrying a completed upload returns the same File. Later reads and enrichments
@@ -248,6 +365,22 @@ Response without consuming its body. Local transfer is bounded and buffered
248
365
  before constructing that Response. Creation grants private creator access;
249
366
  workspace membership alone never grants another creator's File.
250
367
 
368
+ `await files.delete(fileId)` removes an unreferenced File's original and all
369
+ retained enrichments under its current member or installed-routine creator.
370
+ It returns `void` only after physical cleanup and durable completion; a completed
371
+ retry succeeds. Independent retention returns `conflict` without changing the
372
+ File. `access_denied` and `integrity` remain terminal; `unavailable` and
373
+ `capacity` are retryable. Pending deletion denies reads, writes, new uses and
374
+ creation-key replay while the same current creator retries cleanup. Deletion
375
+ uses the authenticated parent for managed local Files and uploaded caches.
376
+ R2 cleanup verifies the registered immutable key and native revision, removes
377
+ only that object and confirms absence. Each physical writer owns a distinct
378
+ candidate key, so a delayed writer cannot recreate the deleted representation.
379
+ Pending admission is never success.
380
+ When a completion reply is lost, retry `files.delete` with the same File ID and
381
+ current creator authority. Keep stable creation keys separate from deletion
382
+ recovery: a completed deleted key is denied and cannot recreate the File.
383
+
251
384
  `enrich` accepts a string encoded as UTF-8 or exact `Uint8Array` bytes, a safe
252
385
  flat filename, and an optional MIME hint. OS verifies text/Markdown/JSON or
253
386
  reduces the content to generic binary metadata; it performs no extraction or
@@ -287,8 +420,8 @@ if (next) await general.records.select({
287
420
  Binding is inert. Each operation resolves current workspace, endpoint and
288
421
  short-lived authority from the trusted app host or managed routine context.
289
422
  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
423
+ rejects; a named handle does not expand repository access. During
424
+ `os routine run <name>`, the same calls use the matched checkout's committed
292
425
  version through a private parent transport. Dirty files are ignored. An OS
293
426
  cache outside the checkout reuses derived data; reads require no remote access
294
427
  or preparation storage. Later accepted record effects advance the run's view
@@ -354,8 +487,8 @@ projection, highlighting, embedding or historical version option.
354
487
  Matches have deterministic order and complete canonical Records. Empty matches
355
488
  succeed with the exact searched version. Members search latest published data;
356
489
  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,
490
+ publication. Hosted and app children publish immediately. During
491
+ `os routine run <name>`, search uses the same committed local Index as select,
359
492
  ignores dirty/untracked files and advances only to that run's exact proposed
360
493
  version after its own Changes publication. Validation does not advance reads;
361
494
  another publisher does not move the captured view. Search Records can supply
@@ -389,7 +522,7 @@ For reads, only `capacity` and `unavailable` are retryable; an error never inclu
389
522
  server details. Server boundaries own query bounds and authorization.
390
523
 
391
524
  Schema-driven creates, guarded updates and guarded deletions use the same handle
392
- in immediate and prepared routines:
525
+ in immediate and captured local routines:
393
526
 
394
527
  ```ts
395
528
  const changes = general.changes();
@@ -418,8 +551,8 @@ only after its exact index is ready. Later current reads discover that version.
418
551
  Deleted identities keep their captured versions; deletion creates no new Record
419
552
  version or tombstone. A Source deletion removes only that guarded Record, retaining
420
553
  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
554
+ During `os routine run <name>`, publication retains the exact proposal and
555
+ advances only run-local reads. `os routine publish <run>` later applies and indexes
423
556
  that immutable proposal; unrelated staged and unstaged work is preserved.
424
557
 
425
558
  `invalid_change` and known-unapplied `conflict` reject the batch.
@@ -468,14 +601,16 @@ The defaults read `ODDINGTON_ROUTINE_WORKSPACE`, `ODDINGTON_ROUTINE_RUN` and
468
601
  `ODDINGTON_INBOX_WORKING_ROOT` (or the current directory). Callers may supply the
469
602
  existing `workspace`, `capability` and `workingRoot` options. Runtime context
470
603
  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.
604
+ `os routine run <name> [--input <file|->]` executes the conventional inert
605
+ definition with declared JSON input and captures OS-owned changes.
606
+ `os routine publish <run>` sends those exact changes and recovers confirmed
607
+ receipts without invoking business code again. Filesystem, model and installed
608
+ connection effects occur immediately, including before a later failure.
609
+ No-change success retains Run evidence without requiring publish. `--json`
610
+ returns typed Run state/counts/result, or confirmed publish receipts/links.
611
+ The former `prepare`, `run --local`, command/argv and authority-selection
612
+ flags are rejected. Remote execution and deployment are not implemented here.
613
+ Checkout-backed app children retain immediate publication.
479
614
 
480
615
  Retain evidence with `inbox.files.add({ path: 'evidence.txt' })`. Paths stay within
481
616
  the invocation's working root and uploaded bytes are verified against the retained