@spooky-sync/core 0.0.1-canary.280 → 0.0.1-canary.281
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 +2 -0
- package/dist/index.d.ts +7 -329
- package/dist/index.js +748 -39
- package/dist/live.d.ts +141 -0
- package/dist/live.js +709 -0
- package/dist/pure.d.ts +322 -0
- package/dist/pure.js +24 -0
- package/dist/sw.d.ts +207 -0
- package/dist/sw.js +393 -0
- package/dist/types.d.ts +742 -3
- package/dist/worker-edges.js +415 -0
- package/dist/worker-impersonation.js +60 -0
- package/dist/worker-ref-tables.d.ts +46 -0
- package/dist/worker-types.d.ts +112 -0
- package/dist/worker-types.js +73 -0
- package/package.json +16 -4
- package/scripts/check-worker-entries.mjs +172 -0
- package/src/client/services.ts +10 -38
- package/src/client/sp00ky-client.ts +13 -0
- package/src/index.ts +32 -0
- package/src/live/index.test.ts +478 -0
- package/src/live/index.ts +1013 -0
- package/src/modules/auth/index.ts +52 -0
- package/src/modules/web-push/index.test.ts +797 -0
- package/src/modules/web-push/index.ts +997 -0
- package/src/pure.ts +65 -0
- package/src/push/types.ts +174 -0
- package/src/sw/index.test.ts +582 -0
- package/src/sw/index.ts +607 -0
- package/src/sw/store.ts +96 -0
- package/src/testing/fake-services.ts +1 -1
- package/src/types.ts +11 -0
- package/src/utils/edges.ts +39 -0
- package/src/utils/sha256.ts +15 -0
- package/tsdown.config.ts +31 -2
|
@@ -24,7 +24,7 @@ export function fakeServiceBundle<S extends SchemaStructure>(over: Partial<Servi
|
|
|
24
24
|
streamProcessor: { addReceiver: (r: unknown) => void receivers.push(r), checkpoint: async () => undefined, dispose: noop } as any,
|
|
25
25
|
migrator: {} as any,
|
|
26
26
|
crdt: { closeAll: noop, dispose: noop, open: async () => 'field', close: noop } as any,
|
|
27
|
-
auth: { subscribe: (cb: (u: string | null) => void) => (authListeners.push(cb), noop), subscribeImpersonation: () => noop, currentUser: null, isAuthenticated: false, impersonation: null, eventSystem: { subscribe: noop } } as any,
|
|
27
|
+
auth: { subscribe: (cb: (u: string | null) => void) => (authListeners.push(cb), noop), subscribeImpersonation: () => noop, onBeforeSignOut: () => noop, currentUser: null, isAuthenticated: false, token: null, impersonation: null, eventSystem: { subscribe: noop } } as any,
|
|
28
28
|
tabs: null,
|
|
29
29
|
tabsUnsupportedReason: 'test',
|
|
30
30
|
tabId: 'tab-test',
|
package/src/types.ts
CHANGED
|
@@ -3,6 +3,7 @@ import type { RecordId, SchemaStructure, QueryPlan } from '@spooky-sync/query-bu
|
|
|
3
3
|
import type { Level, LoggerOptions } from 'pino';
|
|
4
4
|
import type { PushEventOptions } from './events/index';
|
|
5
5
|
import type { LocalEngineChoice } from './services/database/cache-engine';
|
|
6
|
+
import type { WebPushConfig } from './modules/web-push/index';
|
|
6
7
|
|
|
7
8
|
export type { Level };
|
|
8
9
|
|
|
@@ -384,6 +385,16 @@ export interface Sp00kyConfig<S extends SchemaStructure> {
|
|
|
384
385
|
* Defaults to 30000; `0` disables the timeout.
|
|
385
386
|
*/
|
|
386
387
|
downTimeoutMs?: number;
|
|
388
|
+
/**
|
|
389
|
+
* Web Push on this page (`client.webPush`, `db.webPush`). Everything is
|
|
390
|
+
* optional and the defaults need no service worker configuration here:
|
|
391
|
+
* `autoSync` (repair this browser's subscription after sign-in),
|
|
392
|
+
* `unsubscribeOnSignOut` (drop this device's subscription on `signOut()`),
|
|
393
|
+
* `bridge` (post the session to the service worker for live rendering) are
|
|
394
|
+
* on; `serviceWorkerUrl` registers a worker when the app does not.
|
|
395
|
+
* See the `push-notifications` docs page.
|
|
396
|
+
*/
|
|
397
|
+
webPush?: WebPushConfig;
|
|
387
398
|
}
|
|
388
399
|
|
|
389
400
|
/** Tunables for sync-health reporting. See {@link Sp00kyConfig.syncHealth}. */
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { RecordId } from 'surrealdb';
|
|
2
|
+
import type { InlineRow } from '../types';
|
|
3
|
+
import { encodeRecordId } from './index';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Reading `_00_list_ref` edges as a LIVE notification delivers them. Pure, so
|
|
7
|
+
* both the full client and the wasm-free live feed (`@spooky-sync/core/live`)
|
|
8
|
+
* decode the same wire shape the same way.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/** The hash a `_00_list_ref` edge belongs to: the id part of its `in` (`_00_query:<hash>`). */
|
|
12
|
+
export function hashOfEdge(value: unknown): string | null {
|
|
13
|
+
const inId = (value as { in?: unknown } | null)?.in;
|
|
14
|
+
if (!inId) return null;
|
|
15
|
+
const str = inId instanceof RecordId ? String(inId.id) : String(inId).replace(/^_00_query:/, '');
|
|
16
|
+
return str.length > 0 ? str : null;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* The row an edge notification carries, when the subscription joined it on.
|
|
21
|
+
*
|
|
22
|
+
* Without `FETCH out` the edge's `out` is a record id and there is nothing to
|
|
23
|
+
* land, so this returns null and the caller falls back to fetching the body.
|
|
24
|
+
* It also returns null for a row the session may not read (the join yields
|
|
25
|
+
* `out: null`) and for an edge whose target has been deleted.
|
|
26
|
+
*/
|
|
27
|
+
export function rowOfEdge(value: unknown): InlineRow | null {
|
|
28
|
+
const edge = value as { out?: unknown; version?: unknown } | null;
|
|
29
|
+
const out = edge?.out;
|
|
30
|
+
if (!out || typeof out !== 'object' || Array.isArray(out) || out instanceof RecordId) return null;
|
|
31
|
+
// Must be a real RecordId: the landing path keys off `row.id` being one, and
|
|
32
|
+
// would otherwise record the version for a body it never wrote.
|
|
33
|
+
const rid = (out as { id?: unknown }).id;
|
|
34
|
+
if (!(rid instanceof RecordId)) return null;
|
|
35
|
+
const id = encodeRecordId(rid);
|
|
36
|
+
const version = typeof edge?.version === 'number' ? edge.version : null;
|
|
37
|
+
if (version === null) return null;
|
|
38
|
+
return { id, version, record: out as Record<string, unknown> };
|
|
39
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lowercase hex SHA-256 of a UTF-8 string, through WebCrypto.
|
|
3
|
+
*
|
|
4
|
+
* Pure and dependency free so it can live in `@spooky-sync/core/pure` and run
|
|
5
|
+
* in a service worker, a dedicated worker or Node (`globalThis.crypto` is
|
|
6
|
+
* there on Node 19+). Query keys are `sha256Hex(JSON.stringify(..))`, see
|
|
7
|
+
* `query/hash.ts`.
|
|
8
|
+
*/
|
|
9
|
+
export async function sha256Hex(input: string): Promise<string> {
|
|
10
|
+
const bytes = new TextEncoder().encode(input);
|
|
11
|
+
const digest = await crypto.subtle.digest('SHA-256', bytes);
|
|
12
|
+
return Array.from(new Uint8Array(digest))
|
|
13
|
+
.map((b) => b.toString(16).padStart(2, '0'))
|
|
14
|
+
.join('');
|
|
15
|
+
}
|
package/tsdown.config.ts
CHANGED
|
@@ -70,7 +70,8 @@ const workerUrlPlugin = {
|
|
|
70
70
|
},
|
|
71
71
|
};
|
|
72
72
|
|
|
73
|
-
export default defineConfig(
|
|
73
|
+
export default defineConfig([
|
|
74
|
+
{
|
|
74
75
|
// `sqlite-worker` is emitted at `dist/sqlite-worker.js` (top level, NOT under
|
|
75
76
|
// services/database) so the `new URL('./sqlite-worker.js', import.meta.url)`
|
|
76
77
|
// in `SqliteCacheEngine` — which gets bundled into the flat `dist/index.js` —
|
|
@@ -91,4 +92,32 @@ export default defineConfig({
|
|
|
91
92
|
clean: true,
|
|
92
93
|
hash: false,
|
|
93
94
|
plugins: [versionDefinePlugin, workerUrlPlugin],
|
|
94
|
-
}
|
|
95
|
+
},
|
|
96
|
+
// The wasm-free entries a service worker, a dedicated worker or Node can
|
|
97
|
+
// import: `/pure` (protocol builders), `/live` (the live feed over the
|
|
98
|
+
// pure-JS `surrealdb` SDK) and `/sw` (push handlers). Built separately so
|
|
99
|
+
// they never share a chunk with `index` (whose graph holds the wasm engines)
|
|
100
|
+
// and so `dist/index.js` stays one flat file. They may share chunks among
|
|
101
|
+
// themselves; scripts/check-worker-entries.mjs walks those too and fails
|
|
102
|
+
// the build on any wasm/pino import or top-level window/document access.
|
|
103
|
+
{
|
|
104
|
+
entry: {
|
|
105
|
+
pure: 'src/pure.ts',
|
|
106
|
+
live: 'src/live/index.ts',
|
|
107
|
+
sw: 'src/sw/index.ts',
|
|
108
|
+
},
|
|
109
|
+
format: ['esm'],
|
|
110
|
+
dts: true,
|
|
111
|
+
clean: false,
|
|
112
|
+
hash: false,
|
|
113
|
+
// Own prefix so a chunk of this build never overwrites one of the first
|
|
114
|
+
// (both write `dist/`; `types.d.ts` collided).
|
|
115
|
+
// The entries' own declaration files arrive as chunks named `<entry>.d`
|
|
116
|
+
// and must keep their plain name.
|
|
117
|
+
outputOptions: {
|
|
118
|
+
chunkFileNames: (chunk: { name: string }) =>
|
|
119
|
+
['pure.d', 'live.d', 'sw.d'].includes(chunk.name) ? '[name].js' : 'worker-[name].js',
|
|
120
|
+
},
|
|
121
|
+
plugins: [versionDefinePlugin],
|
|
122
|
+
},
|
|
123
|
+
]);
|