@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 +173 -29
- package/dist/auth-provider.js +3909 -0
- package/dist/browser.js +1295 -642
- package/dist/index.js +62 -24399
- package/dist/types/packages/sdk/src/auth-types.d.ts +40 -0
- package/dist/types/packages/sdk/src/public.d.ts +14 -1
- package/dist/types/packages/sdk/src/routine-definition-types.d.ts +56 -0
- package/dist/types/shared/app-routine-contract.d.ts +43 -0
- package/dist/types/shared/files-contract.d.ts +8 -0
- package/dist/types/shared/inbox-contract.d.ts +1 -0
- package/package.json +2 -1
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
|
|
18
|
-
|
|
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 |
|
|
177
|
-
| Fixture-attributed bytes | 5,954 | 5,
|
|
178
|
-
| SDK bytes | 482,585 |
|
|
179
|
-
| Total gzip bytes | 198,612 |
|
|
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
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
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
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
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
|
|
291
|
-
`os routine
|
|
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.
|
|
358
|
-
`os routine
|
|
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
|
|
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
|
|
422
|
-
advances only run-local reads. `os routine publish
|
|
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
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
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
|