@spooky-sync/core 0.0.1-canary.32 → 0.0.1-canary.320

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.
Files changed (259) hide show
  1. package/AGENTS.md +81 -0
  2. package/README.md +56 -16
  3. package/dist/index.d.ts +2034 -137
  4. package/dist/index.js +15137 -2434
  5. package/dist/live.d.ts +141 -0
  6. package/dist/live.js +709 -0
  7. package/dist/otel/index.d.ts +1 -1
  8. package/dist/otel/index.js +3 -3
  9. package/dist/pure.d.ts +337 -0
  10. package/dist/pure.js +24 -0
  11. package/dist/sqlite-open.js +379 -0
  12. package/dist/sqlite-worker.d.ts +1 -0
  13. package/dist/sqlite-worker.js +439 -0
  14. package/dist/sw.d.ts +207 -0
  15. package/dist/sw.js +393 -0
  16. package/dist/tabs-broker-worker.d.ts +8 -0
  17. package/dist/tabs-broker-worker.js +472 -0
  18. package/dist/types.d.ts +1811 -38
  19. package/dist/worker-edges.js +439 -0
  20. package/dist/worker-impersonation.js +60 -0
  21. package/dist/worker-ref-tables.d.ts +46 -0
  22. package/dist/worker-types.d.ts +112 -0
  23. package/dist/worker-types.js +73 -0
  24. package/package.json +27 -7
  25. package/scripts/check-broker-bundle.mjs +33 -0
  26. package/scripts/check-worker-entries.mjs +172 -0
  27. package/skills/sp00ky-core/SKILL.md +31 -6
  28. package/skills/sp00ky-core/references/config.md +6 -0
  29. package/src/boot/auth-flip.saga.test.ts +135 -0
  30. package/src/boot/auth-flip.saga.ts +77 -0
  31. package/src/boot/boot.saga.test.ts +180 -0
  32. package/src/boot/boot.saga.ts +142 -0
  33. package/src/boot/bucket-switch.saga.test.ts +187 -0
  34. package/src/boot/bucket-switch.saga.ts +133 -0
  35. package/src/boot/preload.saga.test.ts +51 -0
  36. package/src/boot/preload.saga.ts +31 -0
  37. package/src/bucket-blurhash.test.ts +148 -0
  38. package/src/build-globals.d.ts +12 -0
  39. package/src/client/bucket-handle.test.ts +117 -0
  40. package/src/client/bucket-handle.ts +227 -0
  41. package/src/client/router.test.ts +58 -0
  42. package/src/client/router.ts +100 -0
  43. package/src/client/runtime.test.ts +195 -0
  44. package/src/client/runtime.ts +296 -0
  45. package/src/client/services.test.ts +51 -0
  46. package/src/client/services.ts +404 -0
  47. package/src/client/sp00ky-client.test.ts +303 -0
  48. package/src/client/sp00ky-client.ts +586 -0
  49. package/src/events/events.test.ts +2 -1
  50. package/src/events/index.ts +3 -0
  51. package/src/index.ts +69 -1
  52. package/src/kernel/constants.test.ts +19 -0
  53. package/src/kernel/constants.ts +80 -0
  54. package/src/kernel/effects.test.ts +40 -0
  55. package/src/kernel/effects.ts +163 -0
  56. package/src/kernel/events.ts +57 -0
  57. package/src/kernel/interpreter.test.ts +116 -0
  58. package/src/kernel/interpreter.ts +148 -0
  59. package/src/kernel/saga.test.ts +89 -0
  60. package/src/kernel/saga.ts +73 -0
  61. package/src/live/index.test.ts +478 -0
  62. package/src/live/index.ts +1013 -0
  63. package/src/modules/app-release/index.test.ts +125 -0
  64. package/src/modules/app-release/index.ts +209 -0
  65. package/src/modules/auth/auth.local-first.test.ts +169 -0
  66. package/src/modules/auth/events/index.ts +9 -2
  67. package/src/modules/auth/impersonation-banner.ts +443 -0
  68. package/src/modules/auth/impersonation.integration.test.ts +205 -0
  69. package/src/modules/auth/impersonation.test.ts +377 -0
  70. package/src/modules/auth/impersonation.ts +179 -0
  71. package/src/modules/auth/index.ts +466 -19
  72. package/src/modules/crdt/crdt-field.ts +294 -0
  73. package/src/modules/crdt/crdt-hydration.test.ts +210 -0
  74. package/src/modules/crdt/crdt-reconnect.test.ts +195 -0
  75. package/src/modules/crdt/index.ts +463 -0
  76. package/src/modules/crdt/loro-loader.ts +25 -0
  77. package/src/modules/devtools/flags.ts +349 -0
  78. package/src/modules/devtools/index.ts +746 -95
  79. package/src/modules/devtools/mutations.test.ts +92 -0
  80. package/src/modules/devtools/mutations.ts +160 -0
  81. package/src/modules/devtools/notify-throttle.test.ts +166 -0
  82. package/src/modules/devtools/state-shape.test.ts +263 -0
  83. package/src/modules/devtools/storage-info.test.ts +79 -0
  84. package/src/modules/devtools/storage-info.ts +168 -0
  85. package/src/modules/devtools/versions.test.ts +74 -0
  86. package/src/modules/devtools/versions.ts +110 -0
  87. package/src/modules/feature-flag/index.test.ts +298 -0
  88. package/src/modules/feature-flag/index.ts +316 -0
  89. package/src/modules/ref-tables.test.ts +91 -0
  90. package/src/modules/ref-tables.ts +88 -0
  91. package/src/modules/web-push/index.test.ts +824 -0
  92. package/src/modules/web-push/index.ts +1027 -0
  93. package/src/mutation/jobs.test.ts +25 -0
  94. package/src/mutation/jobs.ts +41 -0
  95. package/src/mutation/mutation-id.test.ts +37 -0
  96. package/src/mutation/mutation-id.ts +35 -0
  97. package/src/mutation/pending-writes.integration.test.ts +175 -0
  98. package/src/mutation/push.saga.test.ts +285 -0
  99. package/src/mutation/push.saga.ts +217 -0
  100. package/src/mutation/rows.test.ts +231 -0
  101. package/src/mutation/rows.ts +427 -0
  102. package/src/mutation/tray.saga.test.ts +63 -0
  103. package/src/mutation/tray.saga.ts +48 -0
  104. package/src/mutation/write.saga.test.ts +316 -0
  105. package/src/mutation/write.saga.ts +268 -0
  106. package/src/otel/index.ts +10 -7
  107. package/src/pure.ts +65 -0
  108. package/src/push/types.ts +174 -0
  109. package/src/query/env.test.ts +23 -0
  110. package/src/query/env.ts +56 -0
  111. package/src/query/fetch.saga.test.ts +199 -0
  112. package/src/query/fetch.saga.ts +120 -0
  113. package/src/query/hash.test.ts +14 -0
  114. package/src/query/hash.ts +18 -0
  115. package/src/query/id-set-plan.test.ts +132 -0
  116. package/src/query/lifecycle.saga.test.ts +274 -0
  117. package/src/query/lifecycle.saga.ts +159 -0
  118. package/src/query/materialize.saga.test.ts +115 -0
  119. package/src/query/materialize.saga.ts +84 -0
  120. package/src/query/materialize.test.ts +58 -0
  121. package/src/query/materialize.ts +57 -0
  122. package/src/query/membership.saga.test.ts +350 -0
  123. package/src/query/membership.saga.ts +278 -0
  124. package/src/query/membership.test.ts +118 -0
  125. package/src/query/membership.ts +201 -0
  126. package/src/query/register.saga.test.ts +311 -0
  127. package/src/query/register.saga.ts +229 -0
  128. package/src/query/render-set.test.ts +47 -0
  129. package/src/query/render-set.ts +71 -0
  130. package/src/query/sql.test.ts +81 -0
  131. package/src/query/sql.ts +112 -0
  132. package/src/query/window-query.test.ts +70 -0
  133. package/src/query/window-query.ts +195 -0
  134. package/src/services/blobs/blob-cache.test.ts +359 -0
  135. package/src/services/blobs/blob-cache.ts +603 -0
  136. package/src/services/blobs/blob-manifest.ts +227 -0
  137. package/src/services/blobs/blob-store.test.ts +77 -0
  138. package/src/services/blobs/blob-store.ts +359 -0
  139. package/src/services/blobs/blob.fixture.ts +90 -0
  140. package/src/services/blobs/index.ts +70 -0
  141. package/src/services/database/cache-engine.ts +199 -0
  142. package/src/services/database/connection-supervisor.test.ts +318 -0
  143. package/src/services/database/connection-supervisor.ts +422 -0
  144. package/src/services/database/database.query-timeout.test.ts +140 -0
  145. package/src/services/database/database.responses.test.ts +43 -0
  146. package/src/services/database/database.ts +146 -61
  147. package/src/services/database/engine-factory.ts +33 -0
  148. package/src/services/database/errors.ts +34 -0
  149. package/src/services/database/events/index.ts +2 -1
  150. package/src/services/database/index.ts +7 -0
  151. package/src/services/database/landed-body-clear.integration.test.ts +236 -0
  152. package/src/services/database/local-migrator.integration.test.ts +110 -0
  153. package/src/services/database/local-migrator.ts +59 -16
  154. package/src/services/database/local.test.ts +64 -0
  155. package/src/services/database/local.ts +487 -63
  156. package/src/services/database/plan-render.test.ts +159 -0
  157. package/src/services/database/plan-render.ts +108 -0
  158. package/src/services/database/relation-resolver.test.ts +484 -0
  159. package/src/services/database/relation-resolver.ts +0 -0
  160. package/src/services/database/remote.ts +255 -6
  161. package/src/services/database/sqlite-cache-engine.test.ts +617 -0
  162. package/src/services/database/sqlite-cache-engine.timeout.test.ts +61 -0
  163. package/src/services/database/sqlite-cache-engine.ts +1372 -0
  164. package/src/services/database/sqlite-devtools-queries.integration.test.ts +143 -0
  165. package/src/services/database/sqlite-devtools-queries.test.ts +154 -0
  166. package/src/services/database/sqlite-lock-verify.test.ts +33 -0
  167. package/src/services/database/sqlite-lock-verify.ts +45 -0
  168. package/src/services/database/sqlite-open.test.ts +150 -0
  169. package/src/services/database/sqlite-open.ts +164 -0
  170. package/src/services/database/sqlite-plan-sql.test.ts +145 -0
  171. package/src/services/database/sqlite-plan-sql.ts +182 -0
  172. package/src/services/database/sqlite-projection.test.ts +121 -0
  173. package/src/services/database/sqlite-select.integration.test.ts +185 -0
  174. package/src/services/database/sqlite-select.test.ts +261 -0
  175. package/src/services/database/sqlite-select.ts +131 -0
  176. package/src/services/database/sqlite-transport.fixture.ts +30 -0
  177. package/src/services/database/sqlite-transport.ts +224 -0
  178. package/src/services/database/sqlite-worker.ts +437 -0
  179. package/src/services/database/surql-translate.ts +416 -0
  180. package/src/services/database/surreal-cache-engine.ts +161 -0
  181. package/src/services/logger/index.test.ts +132 -0
  182. package/src/services/logger/index.ts +189 -7
  183. package/src/services/persistence/localstorage.ts +2 -2
  184. package/src/services/persistence/resilient.ts +10 -3
  185. package/src/services/persistence/surrealdb.ts +4 -4
  186. package/src/services/stream-processor/index.ts +783 -68
  187. package/src/services/stream-processor/permissions.test.ts +47 -0
  188. package/src/services/stream-processor/permissions.ts +53 -0
  189. package/src/services/stream-processor/stream-processor.batch.test.ts +186 -0
  190. package/src/services/stream-processor/stream-processor.prime.test.ts +198 -0
  191. package/src/services/stream-processor/stream-processor.reset.test.ts +226 -0
  192. package/src/services/stream-processor/stream-processor.test.ts +1 -1
  193. package/src/services/stream-processor/wasm-types.ts +58 -2
  194. package/src/services/tabs/broker-client.ts +283 -0
  195. package/src/services/tabs/broker.test.ts +327 -0
  196. package/src/services/tabs/coordinator.test.ts +386 -0
  197. package/src/services/tabs/coordinator.ts +638 -0
  198. package/src/services/tabs/fake-ports.fixture.ts +112 -0
  199. package/src/services/tabs/leader-locks.ts +75 -0
  200. package/src/services/tabs/protocol.ts +262 -0
  201. package/src/services/tabs/support.ts +36 -0
  202. package/src/services/tabs/tabs-broker-worker.ts +640 -0
  203. package/src/sp00ky.ts +5 -395
  204. package/src/state/client-state.ts +200 -0
  205. package/src/state/lifecycle.test.ts +141 -0
  206. package/src/state/lifecycle.ts +99 -0
  207. package/src/state/reducers.test.ts +304 -0
  208. package/src/state/reducers.ts +526 -0
  209. package/src/state/selectors.test.ts +175 -0
  210. package/src/state/selectors.ts +222 -0
  211. package/src/sw/index.test.ts +582 -0
  212. package/src/sw/index.ts +607 -0
  213. package/src/sw/store.ts +96 -0
  214. package/src/sync/connection.saga.test.ts +86 -0
  215. package/src/sync/connection.saga.ts +85 -0
  216. package/src/sync/live.saga.test.ts +178 -0
  217. package/src/sync/live.saga.ts +107 -0
  218. package/src/sync/policy.health.test.ts +56 -0
  219. package/src/sync/policy.test.ts +659 -0
  220. package/src/sync/policy.ts +502 -0
  221. package/src/sync/poll.saga.test.ts +51 -0
  222. package/src/sync/poll.saga.ts +36 -0
  223. package/src/sync/tabs.saga.test.ts +78 -0
  224. package/src/sync/tabs.saga.ts +96 -0
  225. package/src/testing/adapters.ts +133 -0
  226. package/src/testing/build.ts +63 -0
  227. package/src/testing/fake-services.ts +48 -0
  228. package/src/testing/run-pure.test.ts +92 -0
  229. package/src/testing/run-pure.ts +128 -0
  230. package/src/testing/services.ts +17 -0
  231. package/src/types.ts +624 -10
  232. package/src/utils/blurhash.ts +90 -0
  233. package/src/utils/edges.ts +39 -0
  234. package/src/utils/error-classification.test.ts +54 -0
  235. package/src/utils/error-classification.ts +25 -0
  236. package/src/utils/index.ts +78 -12
  237. package/src/utils/parser.test.ts +67 -102
  238. package/src/utils/parser.ts +64 -2
  239. package/src/utils/semver.test.ts +32 -0
  240. package/src/utils/semver.ts +30 -0
  241. package/src/utils/sha256.ts +15 -0
  242. package/src/utils/surql.ts +30 -18
  243. package/src/utils/utils.test.ts +3 -2
  244. package/tsdown.config.ts +117 -3
  245. package/vitest.config.ts +48 -0
  246. package/src/modules/cache/index.ts +0 -241
  247. package/src/modules/cache/types.ts +0 -19
  248. package/src/modules/data/data.test.ts +0 -58
  249. package/src/modules/data/index.ts +0 -879
  250. package/src/modules/sync/engine.ts +0 -173
  251. package/src/modules/sync/events/index.ts +0 -77
  252. package/src/modules/sync/index.ts +0 -3
  253. package/src/modules/sync/queue/index.ts +0 -2
  254. package/src/modules/sync/queue/queue-down.ts +0 -89
  255. package/src/modules/sync/queue/queue-up.ts +0 -223
  256. package/src/modules/sync/scheduler.ts +0 -84
  257. package/src/modules/sync/sync.ts +0 -421
  258. package/src/modules/sync/utils.test.ts +0 -311
  259. package/src/modules/sync/utils.ts +0 -167
@@ -50,9 +50,9 @@ function createOtelTransmit(endpoint, level = "info") {
50
50
  const messages = [...logEvent.messages];
51
51
  const severityNumber = mapLevelToSeverityNumber(levelLabel);
52
52
  let body = "";
53
- const msg = messages.pop();
54
- if (typeof msg === "string") body = msg;
55
- else if (msg) body = JSON.stringify(msg);
53
+ const lastMsg = messages.pop();
54
+ if (typeof lastMsg === "string") body = lastMsg;
55
+ else if (lastMsg) body = JSON.stringify(lastMsg);
56
56
  let category = "sp00ky-client::unknown";
57
57
  const attributes = {};
58
58
  for (const msg of messages) if (typeof msg === "object") {
package/dist/pure.d.ts ADDED
@@ -0,0 +1,337 @@
1
+ import { a as listRefTableFor, i as bucketIdForUser, n as DEFAULT_REF_MODE, o as sanitizeUserId, r as RefMode, t as ANON_USER_ID } from "./worker-ref-tables.js";
2
+ import { a as PushNotificationAction, c as PushPayloadKind, d as base64UrlToBytes, f as bytesToBase64Url, i as PushNotification, l as PushUrgency, m as sameBytes, n as BridgeTokenMessage, o as PushOp, p as isPushPayload, r as PUSH_PAYLOAD_VERSION, s as PushPayload, t as BridgeSignOutMessage, u as WebPushClientMessage } from "./worker-types.js";
3
+ import { Duration, RecordId } from "surrealdb";
4
+ import { ColumnSchema, RecordId as RecordId$1, SchemaStructure } from "@spooky-sync/query-builder";
5
+ import "pino";
6
+
7
+ //#region src/query/hash.d.ts
8
+ /**
9
+ * Hash inputs for the two query keys. Kept byte-identical to the previous
10
+ * implementation (`DataModule.calculateHash` / `calculateMembershipKey`):
11
+ * the salted key names the remote `_00_query` row, the unsalted one names the
12
+ * durable `_00_view` row, and both are `sha256(JSON.stringify(input))`.
13
+ */
14
+ interface QueryKeyInput {
15
+ surql: string;
16
+ params: Record<string, unknown>;
17
+ }
18
+ /** Session-salted: two tabs of one user must not share a remote view. */
19
+ declare const queryHashInput: (input: QueryKeyInput, sessionId: string | null) => string;
20
+ /** Session-independent: survives reload and bucket switch. */
21
+ declare const viewKeyInput: (input: QueryKeyInput) => string;
22
+ //#endregion
23
+ //#region src/modules/auth/impersonation.d.ts
24
+ /**
25
+ * Admin impersonation, client side.
26
+ *
27
+ * The server does the enforcing (see `impersonation_remote.surql` in the CLI):
28
+ * an admin calls `fn::_00_impersonate::start`, receives a token for the
29
+ * `_00_impersonate` access method, and authenticates with it, so `$auth.id` is
30
+ * the target user. Everything here is state and timers around that:
31
+ *
32
+ * - the admin's own token is kept under {@link ORIGIN_TOKEN_KEY} so Stop (and a
33
+ * reload in between) can return to it;
34
+ * - the impersonation state is derived from the token's claims, never stored
35
+ * separately, so it cannot disagree with the session actually in use;
36
+ * - the token is re-signed before it expires, and the session is polled so a
37
+ * stop or revocation made elsewhere reaches an open WebSocket quickly.
38
+ */
39
+ /** Access method impersonation tokens are issued for. */
40
+ declare const IMPERSONATION_ACCESS = "_00_impersonate";
41
+ /** Persistence key holding the admin's own token while impersonating. */
42
+
43
+ interface TokenClaims {
44
+ access: string | null;
45
+ userId: string | null;
46
+ exp: number | null;
47
+ impersonation: {
48
+ session: string;
49
+ admin: string;
50
+ } | null;
51
+ }
52
+ /**
53
+ * Read the claims of a SurrealDB record-access JWT WITHOUT verifying it. The
54
+ * server still enforces the token on every request; this is only so the client
55
+ * can act on what it already holds before a round trip completes.
56
+ *
57
+ * Returns nulls on any malformed input.
58
+ */
59
+ declare function decodeTokenClaims(token: string): TokenClaims;
60
+ /** The impersonation a token represents, or null for a regular session. */
61
+ //#endregion
62
+ //#region src/utils/surql.d.ts
63
+ interface TxQuery {
64
+ readonly __brand: 'TxQuery';
65
+ readonly sql: string;
66
+ readonly statementCount: number;
67
+ }
68
+ interface SealOptions {
69
+ /** 0-based index of the inner statement to extract. Default: last statement. */
70
+ resultIndex?: number;
71
+ }
72
+ interface SealedQuery<T = void> {
73
+ readonly sql: string;
74
+ readonly extract: (results: unknown[]) => T;
75
+ }
76
+ interface SurqlHelper {
77
+ seal(query: string): string;
78
+ seal<T = void>(query: TxQuery, options?: SealOptions): SealedQuery<T>;
79
+ tx(queries: string[]): TxQuery;
80
+ selectById(idVar: string, returnValues: string[]): string;
81
+ selectByFieldsAnd(table: string, whereVar: ({
82
+ field: string;
83
+ variable: string;
84
+ } | string)[], returnValues: ({
85
+ field: string;
86
+ alias: string;
87
+ } | string)[]): string;
88
+ create(idVar: string, dataVar: string): string;
89
+ createSet(idVar: string, keyDataVars: ({
90
+ key: string;
91
+ variable: string;
92
+ } | {
93
+ statement: string;
94
+ } | string)[]): string;
95
+ upsert(idVar: string, dataVar: string): string;
96
+ upsertMerge(idVar: string, dataVar: string): string;
97
+ updateMerge(idVar: string, dataVar: string): string;
98
+ updateSet(idVar: string, keyDataVar: ({
99
+ key: string;
100
+ variable: string;
101
+ } | {
102
+ statement: string;
103
+ } | string)[]): string;
104
+ delete(idVar: string): string;
105
+ let(name: string, query: string): string;
106
+ createMutation(t: MutationEventType, mutationIdVar: string, recordIdVar: string, dataVar?: string, beforeRecordVar?: string): string;
107
+ returnObject(entries: {
108
+ key: string;
109
+ variable: string;
110
+ }[]): string;
111
+ }
112
+ declare const surql: SurqlHelper;
113
+ //#endregion
114
+ //#region src/types.d.ts
115
+
116
+ /**
117
+ * Supported Time-To-Live (TTL) values for cached queries.
118
+ * Format: number + unit (m=minutes, h=hours, d=days).
119
+ */
120
+ type QueryTimeToLive = '1m' | '5m' | '10m' | '15m' | '20m' | '25m' | '30m' | '1h' | '2h' | '3h' | '4h' | '5h' | '6h' | '7h' | '8h' | '9h' | '10h' | '11h' | '12h' | '1d';
121
+ /**
122
+ * Refresh behavior for `preload` when the data is already cached locally (warm).
123
+ * The FIRST load (cold) always fetches + blocks regardless.
124
+ * - `onUse` (default): do nothing when warm — the data freshens on use, when the
125
+ * real `useQuery` mounts and registers its live view. No network on load.
126
+ * - `background`: return instantly, but kick a one-time silent refetch.
127
+ * - `stale`: like `background`, but only if the cached copy is older than
128
+ * `staleTime`.
129
+ */
130
+
131
+ /**
132
+ * One changed row delivered inline on a LIVE notification, when the
133
+ * subscription was opened with the body joined on (see
134
+ * {@link Sp00kyConfig.liveInlineBodies}). `version` is the edge's `version`,
135
+ * i.e. the same number the membership read would have reported for this row.
136
+ */
137
+ interface InlineRow {
138
+ id: string;
139
+ version: number;
140
+ record: Record<string, unknown>;
141
+ }
142
+ type RecordVersionArray = Array<[string, number]>;
143
+ /**
144
+ * Represents the difference between two record version sets.
145
+ * Used for synchronizing local and remote states.
146
+ */
147
+
148
+ /**
149
+ * What the server's `_00_query` row says about a view, read alongside its
150
+ * `_00_list_ref` edges (registration, the poll, a targeted re-read).
151
+ *
152
+ * - `present: false`: the row is not there (or not readable). The SSP no
153
+ * longer has this view: a TTL sweep, an SSP reset, a scheduler wipe. An
154
+ * empty edge set read at the same time is NOT "no rows", it is "no view".
155
+ * - `state: 'materializing'`: the SSP has (re)computed the set and its edges
156
+ * are in flight; an empty read is "not landed yet".
157
+ * - `state: 'ready'`: the edges are committed (flipped in the same
158
+ * transaction), so `rowCount === 0` with no edges is a real empty result.
159
+ * - `state: null`: a server that predates the marker; only `rowCount` can be
160
+ * used, with its `> 0` = flush window / `0` = empty ambiguity.
161
+ */
162
+ interface ServerViewMeta {
163
+ present: boolean;
164
+ /** `_00_query.rowCount`; `null` when unreadable. */
165
+ rowCount: number | null;
166
+ state: 'materializing' | 'ready' | null;
167
+ }
168
+ /** Outcome of handing a server id-set to `DataModule.updateQueryRemoteArray`. */
169
+
170
+ type MutationEventType = 'create' | 'update' | 'delete';
171
+ /**
172
+ * Represents a mutation event (create, update, delete) to be synchronized.
173
+ */
174
+ //#endregion
175
+ //#region src/kernel/effects.d.ts
176
+ type Vars = Record<string, unknown>;
177
+ /** What the in-browser SSP needs to build a local view. */
178
+ //#endregion
179
+ //#region src/query/sql.d.ts
180
+ /** Every SurrealQL statement the query side sends, as pure builders. */
181
+ declare const VIEW_TABLE = "_00_view";
182
+ declare const LEGACY_VIEW_TABLE = "_00_window";
183
+ declare const viewRecordId: (viewKey: string) => RecordId<string>;
184
+ /**
185
+ * The durable `_00_view` row: the confirmed membership plus the subquery
186
+ * children, which the GC must keep and a cold scan may paint as much as the
187
+ * members.
188
+ */
189
+ declare const viewRow: (ids: RecordVersionArray, children: RecordVersionArray, confirmed: boolean, now: number) => Record<string, unknown>;
190
+ /** Every `_00_view` row: the durable view index and the GC read it. */
191
+ declare const readViewRows: () => string;
192
+ /** Copy every legacy `_00_window` row into `_00_view` once. */
193
+ declare const readLegacyViewRows: () => string;
194
+ declare const countViewRows: () => string;
195
+ declare const listRefSelect: (table: string) => string;
196
+ declare const subqueryListRefSelect: (table: string) => string;
197
+ declare const queryRowCountSelect: () => string;
198
+ declare const listRefBatchSelect: (table: string) => string;
199
+ declare const queryRowCountBatchSelect: () => string;
200
+ /** The single-query read: edges, meta, subquery children in one request. */
201
+ declare const singleSnapshotSelect: (table: string) => string;
202
+ /** The many-query read: all edges + all metas in one request. */
203
+ declare const batchSnapshotSelect: (table: string) => string;
204
+ interface RegisterPayload {
205
+ id: RecordId<string>;
206
+ surql: string;
207
+ params: Record<string, unknown>;
208
+ ttl: string;
209
+ }
210
+ /** Register + read back edges/meta/children in ONE request. */
211
+ declare const registerSelect: (table: string) => string;
212
+ declare const registerVars: (payload: RegisterPayload) => Vars;
213
+ /** One heartbeat statement per view id; result index i answers id i. */
214
+ declare function heartbeatBatch(ids: ReadonlyArray<RecordId<string>>): {
215
+ sql: string;
216
+ vars: Vars;
217
+ };
218
+ /** A heartbeat answer whose statement matched no row: the view was reclaimed. */
219
+ declare const heartbeatRowGone: (result: unknown) => boolean;
220
+ /**
221
+ * The pagehide release: one bare statement for `beaconSql`, which has no params
222
+ * channel, so ids are inlined. Only shapes the client minted itself get in, a
223
+ * `_00_query:<sha256>` id and a UUID session id, so nothing else is ever
224
+ * interpolated. `null` when there is nothing the server could act on: with no
225
+ * session ids it could not tell this page's entries from anyone else's.
226
+ */
227
+ declare function leaveViewsBeacon(ids: ReadonlyArray<unknown>, sessionIds: ReadonlyArray<string>): string | null;
228
+ declare const bodySelect: () => string;
229
+ /** Local transaction that MERGEs many fetched bodies. */
230
+ declare function upsertBodiesTx(records: ReadonlyArray<{
231
+ id: unknown;
232
+ content: Record<string, unknown>;
233
+ }>): {
234
+ query: SealedQuery<unknown>;
235
+ vars: Vars;
236
+ };
237
+ //#endregion
238
+ //#region src/query/membership.d.ts
239
+ /** The `{ rowCount, state }` object the row-count selects return per row. */
240
+ interface QueryMetaRow {
241
+ id?: RecordId<string>;
242
+ rowCount?: number | null;
243
+ state?: string | null;
244
+ }
245
+ declare function metaFromRow(row: QueryMetaRow | null | undefined): ServerViewMeta;
246
+ /** Collapse duplicate `(id, version)` pairs to one per id, keeping the highest version. */
247
+ declare function dedupeRecordVersions(pairs: RecordVersionArray): RecordVersionArray;
248
+ /**
249
+ * A statement that did not answer (an ERR, or no result at all). Kept apart
250
+ * from a `null` result, which is the server saying "no such row": reading a
251
+ * failed statement as a missing row turned every timeout under load into a
252
+ * lost view and re-registered it, over and over (whitepawn, 2026-09-16).
253
+ */
254
+ declare const FAILED: unique symbol;
255
+ type Answer<T> = T | null | typeof FAILED;
256
+ interface ListRefSnapshot {
257
+ primary: RecordVersionArray;
258
+ subquery: RecordVersionArray;
259
+ meta: ServerViewMeta;
260
+ }
261
+ /**
262
+ * Fold the single-query statement batch (edges, meta, children) into a
263
+ * snapshot. `null` when any of the three did not answer: half an answer is
264
+ * not one, and a missing meta would read as a lost view.
265
+ */
266
+ declare function snapshotFromSingle(items: Answer<Array<{
267
+ out: RecordId<string>;
268
+ version: number;
269
+ }>>, metaRow: Answer<QueryMetaRow>, children: Answer<Array<{
270
+ out: RecordId<string>;
271
+ version: number;
272
+ }>>): ListRefSnapshot | null;
273
+ /**
274
+ * Fold the many-query statement batch into one snapshot per hash. Every hash
275
+ * in `hashById` gets a snapshot; a query whose row did not come back reads
276
+ * `present: false`. Empty when either statement did not answer.
277
+ */
278
+ //#endregion
279
+ //#region src/utils/edges.d.ts
280
+ /**
281
+ * Reading `_00_list_ref` edges as a LIVE notification delivers them. Pure, so
282
+ * both the full client and the wasm-free live feed (`@spooky-sync/core/live`)
283
+ * decode the same wire shape the same way.
284
+ */
285
+ /** The hash a `_00_list_ref` edge belongs to: the id part of its `in` (`_00_query:<hash>`). */
286
+ declare function hashOfEdge(value: unknown): string | null;
287
+ /**
288
+ * The row an edge notification carries, when the subscription joined it on.
289
+ *
290
+ * Without `FETCH out` the edge's `out` is a record id and there is nothing to
291
+ * land, so this returns null and the caller falls back to fetching the body.
292
+ * It also returns null for a row the session may not read (the join yields
293
+ * `out: null`) and for an edge whose target has been deleted.
294
+ */
295
+ declare function rowOfEdge(value: unknown): InlineRow | null;
296
+ //#endregion
297
+ //#region src/utils/parser.d.ts
298
+ /**
299
+ * Parse a QUERY's params. Unlike a record's fields, a param name is not always a
300
+ * column: an `_or` branch binds under a synthetic `white__or0` (see
301
+ * `orParamName` in @spooky-sync/query-builder), and the surql that references it
302
+ * is already written. So resolve the column through the synthetic name, and keep
303
+ * a param we cannot type rather than dropping it.
304
+ *
305
+ * Dropping was the bug: `parseParams` kept only column-named params, so every
306
+ * `_or` query registered with `$or0`/`$or1` unbound and matched no rows at all,
307
+ * silently (rowCount 0, errorCount 0).
308
+ */
309
+ declare function parseQueryParams(tableSchema: Record<string, ColumnSchema>, params: Record<string, any>): Record<string, any>;
310
+ //#endregion
311
+ //#region src/utils/index.d.ts
312
+ declare const encodeRecordId: (recordId: RecordId<string>) => string;
313
+ declare const parseRecordIdString: (id: string) => RecordId<string>;
314
+ /**
315
+ * Parse duration string or Duration object to milliseconds
316
+ */
317
+ declare function parseDuration(duration: QueryTimeToLive | Duration): number;
318
+ //#endregion
319
+ //#region src/utils/sha256.d.ts
320
+ /**
321
+ * Lowercase hex SHA-256 of a UTF-8 string, through WebCrypto.
322
+ *
323
+ * Pure and dependency free so it can live in `@spooky-sync/core/pure` and run
324
+ * in a service worker, a dedicated worker or Node (`globalThis.crypto` is
325
+ * there on Node 19+). Query keys are `sha256Hex(JSON.stringify(..))`, see
326
+ * `query/hash.ts`.
327
+ */
328
+ declare function sha256Hex(input: string): Promise<string>;
329
+ //#endregion
330
+ //#region src/pure.d.ts
331
+ /**
332
+ * The remote `_00_query` key of a query for one session salt: what the full
333
+ * client names its view (`_00_query:<key>`).
334
+ */
335
+ declare function queryKey(input: QueryKeyInput, sessionId: string | null): Promise<string>;
336
+ //#endregion
337
+ export { ANON_USER_ID, type Answer, BridgeSignOutMessage, BridgeTokenMessage, DEFAULT_REF_MODE, FAILED, IMPERSONATION_ACCESS, LEGACY_VIEW_TABLE, type ListRefSnapshot, PUSH_PAYLOAD_VERSION, PushNotification, PushNotificationAction, PushOp, PushPayload, PushPayloadKind, PushUrgency, type QueryKeyInput, type QueryMetaRow, type RefMode, RegisterPayload, type SealOptions, type SealedQuery, type SurqlHelper, type TokenClaims, type TxQuery, VIEW_TABLE, WebPushClientMessage, base64UrlToBytes, batchSnapshotSelect, bodySelect, bucketIdForUser, bytesToBase64Url, countViewRows, decodeTokenClaims, dedupeRecordVersions, encodeRecordId, hashOfEdge, heartbeatBatch, heartbeatRowGone, isPushPayload, leaveViewsBeacon, listRefBatchSelect, listRefSelect, listRefTableFor, metaFromRow, parseDuration, parseQueryParams, parseRecordIdString, queryHashInput, queryKey, queryRowCountBatchSelect, queryRowCountSelect, readLegacyViewRows, readViewRows, registerSelect, registerVars, rowOfEdge, sameBytes, sanitizeUserId, sha256Hex, singleSnapshotSelect, snapshotFromSingle, subqueryListRefSelect, surql, upsertBodiesTx, viewKeyInput, viewRecordId, viewRow };
package/dist/pure.js ADDED
@@ -0,0 +1,24 @@
1
+ import { A as readViewRows, B as queryHashInput, C as heartbeatRowGone, D as queryRowCountBatchSelect, E as listRefSelect, F as upsertBodiesTx, I as viewRecordId, L as viewRow, M as registerVars, N as singleSnapshotSelect, O as queryRowCountSelect, P as subqueryListRefSelect, R as surql, S as heartbeatBatch, T as listRefBatchSelect, V as viewKeyInput, _ as LEGACY_VIEW_TABLE, a as metaFromRow, b as bodySelect, c as parseDuration, d as parseQueryParams, f as ANON_USER_ID, g as sanitizeUserId, h as listRefTableFor, i as dedupeRecordVersions, j as registerSelect, k as readLegacyViewRows, l as parseRecordIdString, m as bucketIdForUser, n as rowOfEdge, o as snapshotFromSingle, p as DEFAULT_REF_MODE, r as FAILED, s as encodeRecordId, t as hashOfEdge, v as VIEW_TABLE, w as leaveViewsBeacon, x as countViewRows, y as batchSnapshotSelect, z as sha256Hex } from "./worker-edges.js";
2
+ import { n as decodeTokenClaims, t as IMPERSONATION_ACCESS } from "./worker-impersonation.js";
3
+ import { a as sameBytes, i as isPushPayload, n as base64UrlToBytes, r as bytesToBase64Url, t as PUSH_PAYLOAD_VERSION } from "./worker-types.js";
4
+
5
+ //#region src/pure.ts
6
+ /**
7
+ * `@spooky-sync/core/pure`: the side-effect-free builders of the sync protocol.
8
+ *
9
+ * Everything here is plain functions and constants over `surrealdb` value
10
+ * types: no wasm, no logger, no `window`/`document`/`localStorage`. Safe to
11
+ * import from a service worker, a dedicated worker or Node. The full client
12
+ * imports the same modules, so what this entry builds is byte for byte what
13
+ * `Sp00kyClient` sends.
14
+ */
15
+ /**
16
+ * The remote `_00_query` key of a query for one session salt: what the full
17
+ * client names its view (`_00_query:<key>`).
18
+ */
19
+ function queryKey(input, sessionId) {
20
+ return sha256Hex(queryHashInput(input, sessionId));
21
+ }
22
+
23
+ //#endregion
24
+ export { ANON_USER_ID, DEFAULT_REF_MODE, FAILED, IMPERSONATION_ACCESS, LEGACY_VIEW_TABLE, PUSH_PAYLOAD_VERSION, VIEW_TABLE, base64UrlToBytes, batchSnapshotSelect, bodySelect, bucketIdForUser, bytesToBase64Url, countViewRows, decodeTokenClaims, dedupeRecordVersions, encodeRecordId, hashOfEdge, heartbeatBatch, heartbeatRowGone, isPushPayload, leaveViewsBeacon, listRefBatchSelect, listRefSelect, listRefTableFor, metaFromRow, parseDuration, parseQueryParams, parseRecordIdString, queryHashInput, queryKey, queryRowCountBatchSelect, queryRowCountSelect, readLegacyViewRows, readViewRows, registerSelect, registerVars, rowOfEdge, sameBytes, sanitizeUserId, sha256Hex, singleSnapshotSelect, snapshotFromSingle, subqueryListRefSelect, surql, upsertBodiesTx, viewKeyInput, viewRecordId, viewRow };