@spooky-sync/core 0.0.1-canary.360 → 0.0.1-canary.362

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/AGENTS.md CHANGED
@@ -45,7 +45,7 @@ re-materialized from state, never patched in place.
45
45
  - `services/blobs/` — durable cache for bucket bytes: OPFS holds the files, `_00_blob` holds a manifest that reconcile rebuilds from disk (so a wiped local store costs metadata, not the offline cache). Nothing expires on a timer; eviction is LRU under a byte budget only, skipping pinned and on-screen entries. Config: `blobCache` in `types.ts`.
46
46
  - `AuthService` — token management, sign-in/sign-out events. `subscribe(cb, { immediate? })` calls `cb` at once with the current user unless `immediate: false`, then on every change.
47
47
  - `CrdtManager`, `CrdtField`, `cursorColorFromName`, `CURSOR_COLORS`: collaborative `@crdt` fields on Loro. Client methods: `openCrdtField(table, recordId, field, draft?)` -> `Promise<CrdtField>` (every open of one field in a tab shares one `LoroDoc`; pair each with a close), `closeCrdtField(table, recordId, field)` -> `Promise<void>` (resolves once unsent edits are in the outbox; offline, once they are saved on the device, never waiting on the network), `createCrdtDraft(table, field)` -> a detached `CrdtField` for a record not created yet (`db.create` first, then pass it to `openCrdtField` as `draft`). `CrdtField`: `getDoc()`, `hasContent()`, `pushCursorState(bytes)`, `onCursorUpdate = cb` (a setter). Config: `crdt: true` preloads Loro, `crdtDebounceMs` (the first edit after a quiet spell waits this long, default 50), `crdtSendIntervalMs` (shortest gap between two sends of one field while typing, default 200). Design: `docs/crdt-v2.md` at the repo root.
48
- - **Peer to peer channels** (`-- @channel` tables, experimental): `client.channel(query, peers)` (a query builder, a built query, or a list, on `@channel` tables; typed from the query; own id skipped; both sides must list each other) opens WebRTC data channels to the peers' tabs and returns `Sp00kyChannel` (`state()`, `subscribe(cb)`, `setPeers`, `setQueries`, `close`, `tables()`, plus scoped `update` / `create` / `delete`). Only the query's `where` counts (`matchesWhere`, pure JS over the plan; `compileScope` throws for `.related()`, link-following paths, `$params`, unknown ops); it is never registered. `orderBy` / `limit` / `offset` / `select` / `.one()` warn once per shape (`unusedArgs`) unless the builder has `.ignoreUnusedArgs()`. Writes go to the peers of every open channel whose filter the row matches: a create checks the new row, an update or delete the stored row (`readRow` saga) before and after the patch. A peer's write is taken only if the sender is listed by a channel whose filter the row matches (an update/delete of a row not held is ignored). Peers fold updates into every subscription showing the row (output only, never the store): normal fields retire once the synced row holds them (or after `channels.confirmTimeoutMs`, default 5000); `-- @ephemeral` fields (optional, never filtered on) never reach the database, last writer wins, vanish when their author disconnects. A peer's create goes into the store and circuit like an own optimistic create minus the outbox (`peerCreate` saga, `state.peerWrites` folded into `overlay()`) and is undone at `confirmTimeoutMs` unless the server body landed (`versions`); a peer's delete hides the row through the overlay until it expires (`peerDelete`, `PeerWriteExpired`). Signaling is raw CREATE/LIVE on the generated server-only `_00_channel_signal` (sender stamped by the DB). Everything beyond identity, filter and field types is `channels.accept: { [table]: ({ kind, from, table, id, row, patch }) => boolean }`. Config also `iceServers`, `transport`. TS only (no Dart). Code: `modules/channel/`. Docs: `/docs/client/channels`.
48
+ - **Peer to peer channels** (`-- @channel` tables, experimental): `client.channel(query, peers)` (a query builder, a built query, or a list, on `@channel` tables; typed from the query; own id skipped; both sides must list each other) opens WebRTC data channels to the peers' tabs and returns `Sp00kyChannel` (`state()`, `subscribe(cb)`, `setPeers`, `setQueries`, `close`, `tables()`, plus scoped `update` / `create` / `delete`). Only the query's `where` counts (`matchesWhere`, pure JS over the plan; `compileScope` throws for `.related()`, link-following paths, `$params`, unknown ops); it is never registered. `orderBy` / `limit` / `offset` / `select` / `.one()` warn once per shape (`unusedArgs`) unless the builder has `.ignoreUnusedArgs()`. Writes go to the peers of every open channel whose filter the row matches: a create checks the new row, an update or delete the stored row (`readRow` saga) before and after the patch. A peer's write is taken only if the sender is listed by a channel whose filter the row matches (an update/delete of a row not held is ignored). Peers fold updates into every subscription showing the row (output only, never the store): normal fields retire once the synced row holds them (or after `channels.confirmTimeoutMs`, default 5000); `-- @ephemeral` fields (optional, never filtered on) never reach the database, last writer wins, vanish when their author disconnects. A peer's create goes into the store and circuit like an own optimistic create minus the outbox (`peerCreate` saga, `state.peerWrites` folded into `overlay()`) and is undone at `confirmTimeoutMs` unless the server body landed (`versions`); a peer's delete hides the row through the overlay until it expires (`peerDelete`, `PeerWriteExpired`). Signaling is raw CREATE/LIVE on the generated server-only `_00_channel_signal` (sender stamped by the DB). Everything beyond identity, filter and field types is `channels.accept: { [table]: ({ kind, from, table, id, row, patch }) => boolean }`. Config also `iceServers`, `transport`. Dart has the same feature on the same wire protocol (`spooky_core` `modules/channel/`, `client.channel(...)`, WebRTC via `spooky_flutter`'s `flutterWebRtc`), so web and Flutter peers interoperate: keep the message shape, signaling rows and offer/answer ordering in step across both. Code: `modules/channel/`. Docs: `/docs/client/channels`.
49
49
  - Types: `Sp00kyConfig` (client-solid wraps it as `SyncedDbConfig`), `QueryTimeToLive`, `PersistenceClient`, `StoreType`, `UpdateOptions`, `RunOptions`, `PreloadOptions`.
50
50
  - Subpath: `@spooky-sync/core/otel` — `createOtelTransmit(endpoint)` for piping pino logs to OpenTelemetry.
51
51
  - `client.webPush` (`WebPushModule`, `db.webPush` in the Solid bindings) - Web Push on the page: `support()`, `info()`, `subscribe(opts)` (call from a click), `unsubscribe(opts)`, `sync()`, `isSubscribed()`, `update(opts)`, `devices()`, `notify(msg)`, `cancel(id)`, `test()`, `bridge()`, `onMessage(cb)`. Server calls are `fn::push::*`; typed `WebPushError`; config `webPush` in `types.ts`. Sign-out runs through `AuthService.onBeforeSignOut`.
package/dist/index.js CHANGED
@@ -3650,8 +3650,8 @@ function trimHistory(history, cap) {
3650
3650
 
3651
3651
  //#endregion
3652
3652
  //#region src/modules/devtools/index.ts
3653
- const CORE_VERSION = "0.0.1-canary.360";
3654
- const WASM_VERSION = "0.0.1-canary.360";
3653
+ const CORE_VERSION = "0.0.1-canary.362";
3654
+ const WASM_VERSION = "0.0.1-canary.362";
3655
3655
  const SURREAL_VERSION = "3.1.0";
3656
3656
  var DevToolsService = class DevToolsService {
3657
3657
  version = CORE_VERSION;
@@ -17609,7 +17609,7 @@ function createTabsCoordinator(config, s, host) {
17609
17609
  return new TabsCoordinator({
17610
17610
  tabId: s.tabId,
17611
17611
  fingerprint: computeTabsFingerprint({
17612
- coreVersion: "0.0.1-canary.360",
17612
+ coreVersion: "0.0.1-canary.362",
17613
17613
  schemaHash: hash53(config.schemaSurql),
17614
17614
  endpoint: config.database.endpoint ?? "",
17615
17615
  namespace: config.database.namespace,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@spooky-sync/core",
3
- "version": "0.0.1-canary.360",
3
+ "version": "0.0.1-canary.362",
4
4
  "type": "module",
5
5
  "sideEffects": false,
6
6
  "main": "./dist/index.js",
@@ -75,8 +75,8 @@
75
75
  }
76
76
  },
77
77
  "dependencies": {
78
- "@spooky-sync/query-builder": "0.0.1-canary.360",
79
- "@spooky-sync/ssp-wasm": "0.0.1-canary.360",
78
+ "@spooky-sync/query-builder": "0.0.1-canary.362",
79
+ "@spooky-sync/ssp-wasm": "0.0.1-canary.362",
80
80
  "@sqlite.org/sqlite-wasm": "3.53.0-build1",
81
81
  "@surrealdb/wasm": "^3.1.0",
82
82
  "@surrealdb/wasm-native": "^3.3.4",