@oddington/sdk 0.1.6 → 0.1.8

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
@@ -18,9 +18,29 @@ The OS-owned browser entry point initializes the existing shared refreshing
18
18
  session source before app code runs:
19
19
 
20
20
  ```ts
21
- import { os } from '@oddington/sdk';
21
+ import { os, data, files, DataOperationError, FileOperationError } from '@oddington/sdk';
22
22
 
23
- os.init({ auth });
23
+ os.init({ auth }); // OS-owned startup, once before app modules run.
24
+
25
+ // App code uses the installed capabilities directly.
26
+ const general = data.repository('general');
27
+ try {
28
+ const selected = await general.records.select({ invoices: { $: { limit: 20 } } });
29
+ const found = await general.records.search('acme overdue', {
30
+ collections: ['invoices'], limit: 20,
31
+ });
32
+ const statement = await files.create({
33
+ file: selectedFile, name: 'September statement.csv', path: 'HSC/2026/09',
34
+ });
35
+ const content = await files.read(statement.id);
36
+ // selected and found contain canonical Records and their opaque version guards.
37
+ // content is an unconsumed Web Response.
38
+ } catch (error) {
39
+ if (error instanceof DataOperationError || error instanceof FileOperationError) {
40
+ console.error(error.code, error.retryable); // Bounded public failure information.
41
+ }
42
+ throw error;
43
+ }
24
44
  ```
25
45
 
26
46
  This returns `void` and derives context from the current HTTPS workspace origin
@@ -29,6 +49,132 @@ session subscription. Workspace existence and member access are checked by the
29
49
  OS service. App modules use capability namespaces; routines and handlers receive
30
50
  runtime context without calling `os.init`.
31
51
 
52
+ The same `os.init({ auth })` call also consumes authenticated bootstrap state on
53
+ an OS-launched IPv4 loopback origin. It checks `/_os/app/context` on that exact
54
+ origin for every protected operation, without reading hosted access credentials.
55
+ It accepts no workspace, endpoint or token option. Missing, forged, expired or
56
+ changed local context fails closed, as do sign-out and initialization replacement.
57
+ Local Data and Files currently return `context_unavailable` and
58
+ `unsupported_context` respectively after checking context; they do not return
59
+ empty data, upload locally or fall back to hosted services. Local app routine
60
+ execution uses the installed routine's immediate-publication authority.
61
+
62
+ Apps can assess a declared routine without starting it:
63
+
64
+ ```ts
65
+ import { app, type AppRoutineBinding } from '@oddington/sdk';
66
+
67
+ declare module '@oddington/sdk' {
68
+ interface AppRoutineTypes {
69
+ reconcile: AppRoutineBinding<
70
+ { includeArchived?: boolean; limit: number },
71
+ { processed: number },
72
+ { read: undefined; write: { changed: number } }
73
+ >;
74
+ }
75
+ }
76
+
77
+ const status = await app.routines.reconcile.availability();
78
+ if (status.available) {
79
+ const steps: ('read' | 'write')[] = status.steps;
80
+ }
81
+ const run = await app.routines.reconcile.run({ limit: 2 });
82
+ for await (const event of run.events()) {
83
+ if (event.type === 'step' && event.id === 'write') console.log(event.summary.changed);
84
+ if (event.type === 'finished' && event.state === 'succeeded') console.log(event.result.processed);
85
+ }
86
+ // After refresh or reopening the same live origin:
87
+ const recovered = await app.routines.reconcile.recover();
88
+ // await recovered[0]?.cancel(); // Resolves only after host acknowledgement.
89
+ ```
90
+
91
+ Keep these business types in a customer-owned contract module. Augmentation
92
+ types the property but grants no runtime authority: the local host checks its
93
+ retained `app.json` mapping, current member and installed routine authority,
94
+ and fixed local implementation on each assessment. Available status supplies
95
+ only the declaration's ordered public step IDs. Unavailability supplies only
96
+ `authority_unavailable` or `execution_unavailable`, without steps.
97
+ Availability and recovery accept no arguments; run accepts only declared input.
98
+ Bindings accept no endpoint, token, path, run selector or routine name. Hosted
99
+ bindings report `execution_unavailable` after checking current context, with no
100
+ remote request or local fallback. Availability is no reservation or guarantee
101
+ of later execution. `run` rejects with `AppRoutineError.reason`:
102
+ `authority_unavailable`, `execution_unavailable`, or `invalid_input`.
103
+ Only event reads reconnect, with bounded backoff and duplicate suppression;
104
+ launch and cancellation requests are never automatically retried. Failed
105
+ cancellation requests do not acknowledge cancellation. Handles expose no IDs,
106
+ cursors, raw output or process controls. Local sign-out, expiry, context
107
+ replacement and origin/credential changes invalidate pending work.
108
+
109
+ The exact-origin browser credential survives reload and tab closure in local
110
+ storage. Host expiry and current membership remain authoritative; stored state
111
+ cannot revive a stopped host. Recovery covers only the operation's current
112
+ session and bounded terminal grace, without relaunching or durable history.
113
+ Supplied File inputs currently reject `execution_unavailable` before launch;
114
+ File-use transfer and local Data/Files adapters are separate work.
115
+
116
+ The fixed child imports `appRun` from the same package and shares the app's
117
+ customer-owned input/result/step types:
118
+
119
+ ```ts
120
+ import { appRun } from '@oddington/sdk';
121
+ import type { Input, Result, Steps } from './contract.ts';
122
+
123
+ const context = appRun.current<Input, Result, Steps>();
124
+ if (context) {
125
+ await context.step('read');
126
+ await context.step('write', { changed: 1 });
127
+ await context.finish({ processed: context.input.limit });
128
+ }
129
+ ```
130
+
131
+ `appRun.current()` is absent in ordinary Node and browser execution. Child
132
+ input is deeply immutable; declared reports await host acceptance. Success
133
+ requires accepted finish and zero exit. Cancellation or later failure does not
134
+ undo already confirmed shared effects.
135
+
136
+ Browser bundlers select the standard `browser` condition at the package root.
137
+ Named imports retain the selected browser capabilities without Node built-ins,
138
+ compatibility shims or unused routine, filesystem, Inbox and connection code.
139
+ Use the same `@oddington/sdk` import and declarations in both environments;
140
+ there is no browser subpath or required alias, polyfill or consumer build define.
141
+ Node imports retain the complete supported routine and managed-handler runtime
142
+ on Node.js 22.12.0 or later. Browser operations requiring a Node working root
143
+ remain unsupported.
144
+
145
+ Verify the installed tarball with `bun run --filter @oddington/sdk verify:package`
146
+ from the repository. This checks Node runtime exports and declarations, all public
147
+ text for leaks, and the named browser imports in a realm without Node globals or
148
+ a module loader. It rejects bundler warnings, external dependencies, Node/shim
149
+ code, unused implementations and increases beyond the reviewed SDK byte ceiling.
150
+ The newer Local Host app imports are bundled and exercised separately under the
151
+ same strict compatibility audit, so they cannot weaken or inflate the hosted
152
+ Data/Files size comparison.
153
+
154
+ The browser budget uses pinned esbuild 0.28.1, browser platform, bundled ESM,
155
+ `es2022`, minification, default tree shaking and inline legal comments, with no
156
+ source map, aliases, polyfills, external modules or consumer defines. Its fixed
157
+ named-import fixture exercises hosted Data selection/search and Files operations.
158
+ SDK bytes include all emitted JavaScript except the bundler-attributed fixture
159
+ bytes; generated helpers count toward the SDK. Gzip is diagnostic only.
160
+
161
+ | Same named-import fixture | Legacy diagnostic | Current strict proof |
162
+ | --- | ---: | ---: |
163
+ | Total minified JavaScript bytes | 488,539 | 40,534 |
164
+ | Fixture-attributed bytes | 5,954 | 5,897 |
165
+ | SDK bytes | 482,585 | 34,637 |
166
+ | Total gzip bytes | 198,612 | 12,414 |
167
+
168
+ The legacy diagnostic externalized three unresolved Node built-ins and failed
169
+ browser compatibility. The current strict proof has no imports, shims or warnings
170
+ and reduces SDK bytes by 92.8%. Its literal ceiling is **40,000 SDK bytes**:
171
+ 5,363 bytes (15.5%) of headroom for bounded client fixes. The increase from the
172
+ original 24,000-byte measurement accounts for the subsequently added Local Host
173
+ bootstrap that `os.init` must select at runtime. The verifier pins the fixture
174
+ hash and bundler version; changing either requires a reviewed measurement
175
+ comparison. Future ceiling increases require explicit review of added browser
176
+ capability and must never be automatically regenerated from an oversized output.
177
+
32
178
  Create and recover a private immutable original through the same host context:
33
179
 
34
180
  ```ts