@oddington/sdk 0.1.45 → 0.1.46

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
@@ -246,13 +246,45 @@ try {
246
246
  }
247
247
 
248
248
  os.init({ auth: browserAuth }); // Trusted workspace host startup only.
249
- // Later: await browserAuth.signOut(); // Ends only this browser's session.
249
+ // Later: await browserAuth.signOut(); // Ends the active session.
250
250
  // On document teardown (refresh, navigation or close):
251
251
  unsubscribe();
252
252
  browserAuth.dispose();
253
253
  ```
254
254
 
255
- `auth.create()` accepts no options. Trusted same-origin OS setup selects the
255
+ In a local app started by `os app dev` or `os app launch`, the same handle
256
+ uses the CLI-backed app connection and presents the signed-in viewer:
257
+
258
+ ```ts
259
+ const { status, account } = browserAuth.getState();
260
+ if (status === "signed_in") {
261
+ // account.email and account.displayName identify this app's current viewer.
262
+ }
263
+ ```
264
+
265
+ Use this account for viewer-aware presentation such as a Mine filter. Subscribe
266
+ to keep that presentation current; it is not an authorization decision. There
267
+ is no Inbox identity call, extra hosted lookup, browser provider session or CLI
268
+ token. The handle and `os.init` share one exact-origin connection. Expiry clears
269
+ account presentation, and same-session renewal restores it. Local sign-out ends
270
+ the host session for its browsers; a failed confirmation reports unavailable.
271
+ Disposal preserves the connection's stored proofs for document refresh. Local
272
+ passcode and consent actions are unavailable: sign in with `os login` first.
273
+
274
+ In React, consume the handle as an external store so an early auth update cannot
275
+ be missed between rendering and subscribing:
276
+
277
+ ```ts
278
+ import { useSyncExternalStore } from "react";
279
+
280
+ const authState = useSyncExternalStore(browserAuth.subscribe, browserAuth.getState);
281
+ ```
282
+
283
+ Derive viewer-aware UI from the current `authState.account`; account changes do
284
+ not require refetching the app's records.
285
+
286
+ At a hosted workspace origin, `auth.create()` accepts no options. Trusted
287
+ same-origin OS setup selects the
256
288
  provider internally through exact-origin `GET /api/auth`, owned by OS. The SDK
257
289
  uses same-origin credentials, refuses redirects and bypasses caching. Missing or
258
290
  invalid setup makes authentication unavailable without a legacy endpoint or
@@ -652,9 +684,10 @@ external edits. Current local Data acquires no hosted binding or index. A dead
652
684
  local parent fails without hosted fallback.
653
685
 
654
686
  Each graph selection sends one request and returns only selected canonical
655
- values, expanded forward links and exact opaque public versions. Cursors
656
- continue the exact retained artifact and collection selection, even after
657
- replacement; repeat its options and links while omitting unrelated collections.
687
+ values, expanded forward links and exact opaque public versions. For selected current repositories, cursors
688
+ continue a live keyset position with the same selection; each page reads current.
689
+ Concurrent movement can repeat or skip a Record. Unselected repositories temporarily
690
+ retain their existing exact-version cursors. Repeat options and links while omitting completed collections.
658
691
  There is no automatic retry or separate version fetch.
659
692
  Collection schemas omit Markdown storage hints.
660
693
 
@@ -698,19 +731,22 @@ const matches = await general.records.all(query, { maxRecords: 10_000 });
698
731
 
699
732
  Both helpers preserve literal collection arrays and selected-link inference.
700
733
  `pages` captures the query at invocation, fetches only when requested, and never
701
- prefetches after a consumer stops. Independent collection cursors pin the first
702
- snapshot. Completed collections yield empty arrays on later pages and retain
734
+ prefetches after a consumer stops. Independent collection cursors continue live positions; versions and requested
735
+ counts may change between pages. Completed collections yield empty arrays on later pages and retain
703
736
  requested counts. Empty matches yield one versioned page. Helpers own continuation;
704
737
  use `query` for a manual `after` cursor or explicit historical query.
705
738
 
706
- `all` consumes the same iterator and returns only after successful exhaustion.
739
+ `all` sends one bounded collection operation. Selected hosted execution uses one
740
+ ordinary database transaction, and local execution reads one captured in-memory
741
+ view. Records, requested counts and version describe that single observation;
742
+ the operation retains no snapshot afterward.
707
743
  Its required positive safe-integer `maxRecords` caps top-level Record occurrences
708
744
  across all collections; expanded links do not count toward that ceiling. Overflow
709
745
  names the queried collections and ceiling, tells the author to narrow the query
710
746
  or increase `maxRecords`, and rejects with nonretryable `capacity` without a
711
747
  partial or truncated result.
712
748
  Invalid helper arguments use `invalid_query`; malformed pages, cursor cycles,
713
- snapshot changes or inconsistent requested counts use nonretryable `unavailable`.
749
+ or missing/invalid requested counts use nonretryable `unavailable`.
714
750
  Query and authority failures propagate unchanged, with no retry or restart.
715
751
 
716
752
  Read known stable refs directly, including refs from different collections:
@@ -789,7 +825,7 @@ sees completed Changes writes and external edits. Search Records supply captured
789
825
  versions for subsequent Changes. Explicit History keeps its hosted contract.
790
826
 
791
827
  `where` combines field conditions with AND. Alongside exact scalar/null equality
792
- and `$in`, declared one-valued `integer`, `number` and `date` fields accept
828
+ and `$in`, declared one-valued `integer`, `number`, `date` and `datetime` fields accept
793
829
  `$gt`, `$gte`, `$lt` and `$lte`:
794
830
 
795
831
  ```ts
@@ -808,6 +844,28 @@ A valid interval without matches returns an empty array and requested zero
808
844
  count. V0 types check expression structure; runtime schema validation owns field
809
845
  eligibility. Repeat the same bounds when continuing a cursor.
810
846
 
847
+ A `kind: datetime` field is an instant represented as a canonical UTC string
848
+ with milliseconds, for example `2026-09-15T14:30:00.000Z`. OS validates the real
849
+ date/time and exact `YYYY-MM-DDTHH:mm:ss.sssZ` spelling. Invalid dates, offsets,
850
+ missing or excess fractional digits and Date objects reject; values are never
851
+ silently converted or rounded. To convert an instant explicitly, use
852
+ `Date.toISOString()`. Returned values remain strings and equality/ranges/order
853
+ all use the same canonical representation. Existing string fields need an
854
+ explicit schema change to acquire datetime semantics.
855
+
856
+ The app owns business timezone boundaries. For September 2026 in Europe/London,
857
+ pass the UTC start (inclusive) and next-month start (exclusive):
858
+
859
+ ```ts
860
+ await general.records.query({ emails: { $: { where: {
861
+ received_at: { $gte: '2026-08-31T23:00:00.000Z', $lt: '2026-09-30T23:00:00.000Z' },
862
+ }, order: { received_at: 'desc' } } } });
863
+ ```
864
+
865
+ This filters metadata in the query engine before returning selected Records;
866
+ the app can then batch-read bodies only for those matches. A calendar `date`
867
+ continues to mean a day/month without a timezone.
868
+
811
869
  `DataOperationError.code` distinguishes `context_unavailable`, `unauthenticated`,
812
870
  `access_denied`, `invalid_query`, `request_too_large`, `capacity` and `unavailable`.
813
871
  For reads, only `capacity` and `unavailable` are retryable; an error never includes private
@@ -835,7 +893,6 @@ schema fields, in the same guarded change:
835
893
  changes.create({ $ref: 'emails/email-123', type: 'email', subject: 'Invoice',
836
894
  $body: 'Hello,\n\nPlease find our invoice attached.\n' });
837
895
  changes.update(existingEmail, { $body: revisedMarkdown }); // body-only change
838
- const emails = await general.records.read(emailRefs, { body: true });
839
896
  ```
840
897
 
841
898
  Omitted `$body` on create means empty; omitted on update preserves the existing
@@ -843,16 +900,12 @@ body exactly. A string deliberately replaces it, `''` clears it, and `null` is
843
900
  invalid. Body-only edits preserve front matter, field-only edits preserve the
844
901
  body, and both share the Record's exact-byte version guard. Bodies round-trip
845
902
  without Markdown interpretation, formatting, trimming, or fence semantics.
846
- Ordinary reads, selections and search stay body-free; explicitly requested
847
- bodies use the same ordered batch/null-slot shape. Local change batches support
903
+ Reads, selections and search return structured fields only. The removed
904
+ `body: true` option rejects with `invalid_query`. Local change batches support
848
905
  32 MiB of serialized JSON; hosted requests retain their separate 256 KiB limit.
849
- Body-enabled read responses are bounded to 8 MiB, with an explicit error
850
- instead of truncation. Hosted body reads use a protected immutable v3 index;
851
- older indexes without body coverage are unavailable rather than falsely empty.
852
906
 
853
907
  The customer routine owns content and authoring policy. For imported emails,
854
- create with the prepared body, omit `$body` on ordinary reruns, and backfill
855
- only bodies explicitly read as exactly empty. Existing notes and fenced blocks
908
+ create with the prepared body, omit `$body` on ordinary reruns, and preserve existing source bodies on ordinary reruns. Existing notes and fenced blocks
856
909
  remain ordinary authored content. A fresh version alone does not authorize
857
910
  overwriting an existing nonempty body.
858
911