@stratasync/next 0.2.0
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 +57 -0
- package/dist/bootstrap/fetch.d.ts +3 -0
- package/dist/bootstrap/fetch.d.ts.map +1 -0
- package/dist/bootstrap/fetch.js +84 -0
- package/dist/bootstrap/fetch.js.map +1 -0
- package/dist/bootstrap/index.d.ts +4 -0
- package/dist/bootstrap/index.d.ts.map +1 -0
- package/dist/bootstrap/index.js +5 -0
- package/dist/bootstrap/index.js.map +1 -0
- package/dist/bootstrap/parse.d.ts +14 -0
- package/dist/bootstrap/parse.d.ts.map +1 -0
- package/dist/bootstrap/parse.js +126 -0
- package/dist/bootstrap/parse.js.map +1 -0
- package/dist/bootstrap/seed.d.ts +3 -0
- package/dist/bootstrap/seed.d.ts.map +1 -0
- package/dist/bootstrap/seed.js +61 -0
- package/dist/bootstrap/seed.js.map +1 -0
- package/dist/bootstrap/serialize.d.ts +7 -0
- package/dist/bootstrap/serialize.d.ts.map +1 -0
- package/dist/bootstrap/serialize.js +77 -0
- package/dist/bootstrap/serialize.js.map +1 -0
- package/dist/bootstrap/types.d.ts +45 -0
- package/dist/bootstrap/types.d.ts.map +1 -0
- package/dist/bootstrap/types.js +2 -0
- package/dist/bootstrap/types.js.map +1 -0
- package/dist/client.d.ts +2 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +4 -0
- package/dist/client.js.map +1 -0
- package/dist/provider.d.ts +39 -0
- package/dist/provider.d.ts.map +1 -0
- package/dist/provider.js +108 -0
- package/dist/provider.js.map +1 -0
- package/dist/server.d.ts +2 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +3 -0
- package/dist/server.js.map +1 -0
- package/package.json +68 -0
package/README.md
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# @stratasync/next
|
|
2
|
+
|
|
3
|
+
Next.js integration helpers for the Done with App Router support.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
sync-next bridges the sync engine with Next.js App Router patterns:
|
|
8
|
+
|
|
9
|
+
- **Server utilities** — server-side setup and initialization
|
|
10
|
+
- **Client utilities** — hooks and providers for client components
|
|
11
|
+
- **Metadata helpers** — integration with Next.js metadata API
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm install @stratasync/next
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Peer dependencies: `next` ^14.0.0 || ^15.0.0, `react` ^18.0.0 || ^19.0.0
|
|
20
|
+
|
|
21
|
+
## Exports
|
|
22
|
+
|
|
23
|
+
The package has separate entry points for server and client code:
|
|
24
|
+
|
|
25
|
+
```typescript
|
|
26
|
+
// Client components
|
|
27
|
+
import {} from /* client utilities */ "@stratasync/next/client";
|
|
28
|
+
|
|
29
|
+
// Server components / route handlers
|
|
30
|
+
import {} from /* server utilities */ "@stratasync/next/server";
|
|
31
|
+
|
|
32
|
+
// Default export (client-side)
|
|
33
|
+
import {} from /* default exports */ "@stratasync/next";
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Usage
|
|
37
|
+
|
|
38
|
+
Use Server Components for initial data loading and Client Components for interactive sync features:
|
|
39
|
+
|
|
40
|
+
```tsx
|
|
41
|
+
// Server Component — fetch initial data
|
|
42
|
+
import { initSync } from "@stratasync/next/server";
|
|
43
|
+
|
|
44
|
+
export default async function Page() {
|
|
45
|
+
const initialData = await initSync();
|
|
46
|
+
return <ClientApp initialData={initialData} />;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Client Component — use sync hooks
|
|
50
|
+
("use client");
|
|
51
|
+
import { useSyncClient } from "@stratasync/next/client";
|
|
52
|
+
|
|
53
|
+
function ClientApp({ initialData }) {
|
|
54
|
+
const client = useSyncClient({ initialData });
|
|
55
|
+
// ...
|
|
56
|
+
}
|
|
57
|
+
```
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fetch.d.ts","sourceRoot":"","sources":["../../src/bootstrap/fetch.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,iBAAiB,EAAE,wBAAwB,EAAE,MAAM,YAAY,CAAC;AAO9E,eAAO,MAAM,iBAAiB,GAC5B,SAAS,wBAAwB,KAChC,OAAO,CAAC,iBAAiB,CAiF3B,CAAC"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
// oxlint-disable no-use-before-define -- helper functions are grouped after exported functions for readability
|
|
2
|
+
import { ensureBootstrapMetadata, readBootstrapStream, resolveFirstSyncId, } from "./parse.js";
|
|
3
|
+
const DEFAULT_PREFETCH_TIMEOUT_MS = 10_000;
|
|
4
|
+
const NDJSON_ACCEPT_HEADER = "application/x-ndjson";
|
|
5
|
+
const TRAILING_SLASH_RE = /\/+$/;
|
|
6
|
+
const KNOWN_SYNC_SUFFIXES = ["/bootstrap", "/batch", "/deltas"];
|
|
7
|
+
export const prefetchBootstrap = async (options) => {
|
|
8
|
+
const { endpoint, authorization, headers, models, groups, schemaHash, timeout = DEFAULT_PREFETCH_TIMEOUT_MS, } = options;
|
|
9
|
+
// Build request headers
|
|
10
|
+
const requestHeaders = {
|
|
11
|
+
Accept: NDJSON_ACCEPT_HEADER,
|
|
12
|
+
...headers,
|
|
13
|
+
};
|
|
14
|
+
if (authorization) {
|
|
15
|
+
requestHeaders.Authorization = authorization;
|
|
16
|
+
}
|
|
17
|
+
// Build query params
|
|
18
|
+
const params = new URLSearchParams();
|
|
19
|
+
params.set("type", "full");
|
|
20
|
+
if (models?.length) {
|
|
21
|
+
params.set("onlyModels", models.join(","));
|
|
22
|
+
}
|
|
23
|
+
if (schemaHash) {
|
|
24
|
+
params.set("schemaHash", schemaHash);
|
|
25
|
+
}
|
|
26
|
+
if (groups?.length) {
|
|
27
|
+
params.set("syncGroups", groups.join(","));
|
|
28
|
+
}
|
|
29
|
+
// Build URL
|
|
30
|
+
const baseEndpoint = normalizeSyncEndpoint(endpoint);
|
|
31
|
+
const url = joinSyncUrl(baseEndpoint, "/bootstrap");
|
|
32
|
+
const fullUrl = `${url}?${params.toString()}`;
|
|
33
|
+
// Fetch with timeout
|
|
34
|
+
const controller = new AbortController();
|
|
35
|
+
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
36
|
+
let response;
|
|
37
|
+
try {
|
|
38
|
+
response = await fetch(fullUrl, {
|
|
39
|
+
headers: requestHeaders,
|
|
40
|
+
method: "GET",
|
|
41
|
+
signal: controller.signal,
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
finally {
|
|
45
|
+
clearTimeout(timeoutId);
|
|
46
|
+
}
|
|
47
|
+
// Ensure response OK
|
|
48
|
+
if (!response.ok) {
|
|
49
|
+
const text = await response.text();
|
|
50
|
+
throw new Error(`Bootstrap prefetch failed: ${response.status} ${text}`);
|
|
51
|
+
}
|
|
52
|
+
// Get body
|
|
53
|
+
if (!response.body) {
|
|
54
|
+
throw new Error("Bootstrap prefetch response has no body");
|
|
55
|
+
}
|
|
56
|
+
const { rows, metadata, rowCount } = await readBootstrapStream(response.body);
|
|
57
|
+
const resolvedMetadata = ensureBootstrapMetadata(metadata);
|
|
58
|
+
const snapshotSchemaHash = resolvedMetadata.schemaHash ?? schemaHash ?? "";
|
|
59
|
+
return {
|
|
60
|
+
fetchedAt: Date.now(),
|
|
61
|
+
firstSyncId: resolveFirstSyncId(resolvedMetadata),
|
|
62
|
+
groups: resolvedMetadata.subscribedSyncGroups,
|
|
63
|
+
lastSyncId: resolvedMetadata.lastSyncId,
|
|
64
|
+
rowCount,
|
|
65
|
+
rows,
|
|
66
|
+
schemaHash: snapshotSchemaHash,
|
|
67
|
+
version: 1,
|
|
68
|
+
};
|
|
69
|
+
};
|
|
70
|
+
const normalizeSyncEndpoint = (endpoint) => {
|
|
71
|
+
const trimmed = endpoint.replace(TRAILING_SLASH_RE, "");
|
|
72
|
+
for (const suffix of KNOWN_SYNC_SUFFIXES) {
|
|
73
|
+
if (trimmed.endsWith(suffix)) {
|
|
74
|
+
return trimmed.slice(0, -suffix.length);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return trimmed;
|
|
78
|
+
};
|
|
79
|
+
const joinSyncUrl = (base, path) => {
|
|
80
|
+
const normalizedBase = base.replace(TRAILING_SLASH_RE, "");
|
|
81
|
+
const normalizedPath = path.startsWith("/") ? path : `/${path}`;
|
|
82
|
+
return `${normalizedBase}${normalizedPath}`;
|
|
83
|
+
};
|
|
84
|
+
//# sourceMappingURL=fetch.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fetch.js","sourceRoot":"","sources":["../../src/bootstrap/fetch.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { prefetchBootstrap } from "./fetch.js";
|
|
2
|
+
export { seedStorageFromBootstrap } from "./seed.js";
|
|
3
|
+
export { decodeBootstrapSnapshot, deserializeBootstrapSnapshot, encodeBootstrapSnapshot, isBootstrapSnapshotStale, serializeBootstrapSnapshot, } from "./serialize.js";
|
|
4
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/bootstrap/index.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAC/C,OAAO,EAAE,wBAAwB,EAAE,MAAM,WAAW,CAAC;AACrD,OAAO,EACL,uBAAuB,EACvB,4BAA4B,EAC5B,uBAAuB,EACvB,wBAAwB,EACxB,0BAA0B,GAC3B,MAAM,gBAAgB,CAAC"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
// biome-ignore-all lint/performance/noBarrelFile: This is the bootstrap module's public API
|
|
2
|
+
export { prefetchBootstrap } from "./fetch.js";
|
|
3
|
+
export { seedStorageFromBootstrap } from "./seed.js";
|
|
4
|
+
export { decodeBootstrapSnapshot, deserializeBootstrapSnapshot, encodeBootstrapSnapshot, isBootstrapSnapshotStale, serializeBootstrapSnapshot, } from "./serialize.js";
|
|
5
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/bootstrap/index.ts"],"names":[],"mappings":"AAAA,4FAA4F;AAE5F,OAAO,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAC/C,OAAO,EAAE,wBAAwB,EAAE,MAAM,WAAW,CAAC;AACrD,OAAO,EACL,uBAAuB,EACvB,4BAA4B,EAC5B,uBAAuB,EACvB,wBAAwB,EACxB,0BAA0B,GAC3B,MAAM,gBAAgB,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { BootstrapMetadata, ModelRow, SyncId } from "@stratasync/core";
|
|
2
|
+
export interface BootstrapParseResult {
|
|
3
|
+
rows: ModelRow[];
|
|
4
|
+
metadata: BootstrapMetadata | null;
|
|
5
|
+
rowCount?: number;
|
|
6
|
+
}
|
|
7
|
+
interface ValidatedBootstrapMetadata extends BootstrapMetadata {
|
|
8
|
+
lastSyncId: SyncId;
|
|
9
|
+
}
|
|
10
|
+
export declare const readBootstrapStream: (stream: ReadableStream<Uint8Array>) => Promise<BootstrapParseResult>;
|
|
11
|
+
export declare const ensureBootstrapMetadata: (metadata: BootstrapMetadata | null) => ValidatedBootstrapMetadata;
|
|
12
|
+
export declare const resolveFirstSyncId: (metadata: ValidatedBootstrapMetadata) => SyncId;
|
|
13
|
+
export {};
|
|
14
|
+
//# sourceMappingURL=parse.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parse.d.ts","sourceRoot":"","sources":["../../src/bootstrap/parse.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,iBAAiB,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAE5E,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,QAAQ,EAAE,CAAC;IACjB,QAAQ,EAAE,iBAAiB,GAAG,IAAI,CAAC;IACnC,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,UAAU,0BAA2B,SAAQ,iBAAiB;IAC5D,UAAU,EAAE,MAAM,CAAC;CACpB;AAwCD,eAAO,MAAM,mBAAmB,GAC9B,QAAQ,cAAc,CAAC,UAAU,CAAC,KACjC,OAAO,CAAC,oBAAoB,CAqB9B,CAAC;AA0FF,eAAO,MAAM,uBAAuB,GAClC,UAAU,iBAAiB,GAAG,IAAI,KACjC,0BAQF,CAAC;AAEF,eAAO,MAAM,kBAAkB,GAC7B,UAAU,0BAA0B,KACnC,MASF,CAAC"}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
// oxlint-disable-next-line func-style -- generators require function declaration
|
|
2
|
+
async function* readNdjsonLines(stream) {
|
|
3
|
+
const reader = stream.getReader();
|
|
4
|
+
const decoder = new TextDecoder();
|
|
5
|
+
let buffer = "";
|
|
6
|
+
try {
|
|
7
|
+
while (true) {
|
|
8
|
+
const { done, value } = await reader.read();
|
|
9
|
+
if (done) {
|
|
10
|
+
break;
|
|
11
|
+
}
|
|
12
|
+
buffer += decoder.decode(value, { stream: true });
|
|
13
|
+
const lines = buffer.split("\n");
|
|
14
|
+
buffer = lines.pop() ?? "";
|
|
15
|
+
for (const line of lines) {
|
|
16
|
+
yield line;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
const trimmed = buffer.trim();
|
|
20
|
+
if (trimmed) {
|
|
21
|
+
yield trimmed;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
finally {
|
|
25
|
+
reader.releaseLock();
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
export const readBootstrapStream = async (stream) => {
|
|
29
|
+
const rows = [];
|
|
30
|
+
let metadata = null;
|
|
31
|
+
let rowCount;
|
|
32
|
+
for await (const line of readNdjsonLines(stream)) {
|
|
33
|
+
const parsed = parseBootstrapLine(line);
|
|
34
|
+
if (!parsed) {
|
|
35
|
+
continue;
|
|
36
|
+
}
|
|
37
|
+
if (parsed.type === "meta") {
|
|
38
|
+
({ metadata } = parsed);
|
|
39
|
+
}
|
|
40
|
+
else if (parsed.type === "row") {
|
|
41
|
+
rows.push(parsed.row);
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
({ rowCount } = parsed);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return { metadata, rowCount, rows };
|
|
48
|
+
};
|
|
49
|
+
const parseBootstrapLine = (line) => {
|
|
50
|
+
const trimmed = line.trim();
|
|
51
|
+
if (!trimmed) {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
if (trimmed.startsWith("_metadata_=")) {
|
|
55
|
+
const raw = JSON.parse(trimmed.slice("_metadata_=".length));
|
|
56
|
+
return { metadata: normalizeBootstrapMetadata(raw), type: "meta" };
|
|
57
|
+
}
|
|
58
|
+
const parsed = JSON.parse(trimmed);
|
|
59
|
+
if (typeof parsed._metadata_ === "object" && parsed._metadata_ !== null) {
|
|
60
|
+
return {
|
|
61
|
+
metadata: normalizeBootstrapMetadata(parsed._metadata_),
|
|
62
|
+
type: "meta",
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
if (isBootstrapMetadata(parsed)) {
|
|
66
|
+
return { metadata: normalizeBootstrapMetadata(parsed), type: "meta" };
|
|
67
|
+
}
|
|
68
|
+
if (parsed.type === "end") {
|
|
69
|
+
return {
|
|
70
|
+
rowCount: typeof parsed.rowCount === "number" ? parsed.rowCount : undefined,
|
|
71
|
+
type: "end",
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
if (typeof parsed.__class !== "string") {
|
|
75
|
+
throw new TypeError("Bootstrap row is missing __class");
|
|
76
|
+
}
|
|
77
|
+
const { __class: modelName, ...data } = parsed;
|
|
78
|
+
return {
|
|
79
|
+
row: { data, modelName },
|
|
80
|
+
type: "row",
|
|
81
|
+
};
|
|
82
|
+
};
|
|
83
|
+
const isBootstrapMetadata = (parsed) => "lastSyncId" in parsed ||
|
|
84
|
+
"subscribedSyncGroups" in parsed ||
|
|
85
|
+
"returnedModelsCount" in parsed;
|
|
86
|
+
const normalizeBootstrapMetadata = (parsed) => {
|
|
87
|
+
const lastSyncIdRaw = parsed.lastSyncId;
|
|
88
|
+
const subscribedSyncGroupsRaw = parsed.subscribedSyncGroups;
|
|
89
|
+
const subscribedSyncGroups = Array.isArray(subscribedSyncGroupsRaw)
|
|
90
|
+
? subscribedSyncGroupsRaw.filter((group) => typeof group === "string")
|
|
91
|
+
: [];
|
|
92
|
+
const result = {
|
|
93
|
+
databaseVersion: typeof parsed.databaseVersion === "number"
|
|
94
|
+
? parsed.databaseVersion
|
|
95
|
+
: undefined,
|
|
96
|
+
raw: parsed,
|
|
97
|
+
returnedModelsCount: parsed.returnedModelsCount &&
|
|
98
|
+
typeof parsed.returnedModelsCount === "object"
|
|
99
|
+
? parsed.returnedModelsCount
|
|
100
|
+
: undefined,
|
|
101
|
+
schemaHash: typeof parsed.schemaHash === "string" ? parsed.schemaHash : undefined,
|
|
102
|
+
subscribedSyncGroups,
|
|
103
|
+
};
|
|
104
|
+
if (typeof lastSyncIdRaw === "string" || typeof lastSyncIdRaw === "number") {
|
|
105
|
+
result.lastSyncId = String(lastSyncIdRaw);
|
|
106
|
+
}
|
|
107
|
+
return result;
|
|
108
|
+
};
|
|
109
|
+
export const ensureBootstrapMetadata = (metadata) => {
|
|
110
|
+
if (!metadata) {
|
|
111
|
+
throw new Error("Bootstrap prefetch did not receive metadata");
|
|
112
|
+
}
|
|
113
|
+
if (metadata.lastSyncId === undefined) {
|
|
114
|
+
throw new Error("Bootstrap metadata is missing lastSyncId");
|
|
115
|
+
}
|
|
116
|
+
return { ...metadata, lastSyncId: metadata.lastSyncId };
|
|
117
|
+
};
|
|
118
|
+
export const resolveFirstSyncId = (metadata) => {
|
|
119
|
+
const rawFirstSyncId = metadata.raw?.firstSyncId;
|
|
120
|
+
if (typeof rawFirstSyncId === "string" ||
|
|
121
|
+
typeof rawFirstSyncId === "number") {
|
|
122
|
+
return String(rawFirstSyncId);
|
|
123
|
+
}
|
|
124
|
+
return metadata.lastSyncId;
|
|
125
|
+
};
|
|
126
|
+
//# sourceMappingURL=parse.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parse.js","sourceRoot":"","sources":["../../src/bootstrap/parse.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"seed.d.ts","sourceRoot":"","sources":["../../src/bootstrap/seed.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAGV,kBAAkB,EAClB,iBAAiB,EAClB,MAAM,YAAY,CAAC;AAEpB,eAAO,MAAM,wBAAwB,GACnC,SAAS,kBAAkB,KAC1B,OAAO,CAAC,iBAAiB,CAwE3B,CAAC"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
// oxlint-disable no-use-before-define -- helper functions are grouped after exported functions for readability
|
|
2
|
+
import { computeSchemaHash, getOrCreateClientId, ModelRegistry, } from "@stratasync/core";
|
|
3
|
+
import { deserializeBootstrapSnapshot } from "./serialize.js";
|
|
4
|
+
export const seedStorageFromBootstrap = async (options) => {
|
|
5
|
+
const { storage, snapshot, dbName = "sync-db", clearExisting = true, validateSchemaHash = true, batchSize = 500, closeAfter = true, schema, } = options;
|
|
6
|
+
const resolvedSnapshot = await resolveSnapshot(snapshot);
|
|
7
|
+
const localSchemaHash = computeSchemaHash(schema ?? ModelRegistry.snapshot());
|
|
8
|
+
if (validateSchemaHash &&
|
|
9
|
+
resolvedSnapshot.schemaHash &&
|
|
10
|
+
resolvedSnapshot.schemaHash !== localSchemaHash) {
|
|
11
|
+
return { applied: false, reason: "schema_mismatch", rowCount: 0 };
|
|
12
|
+
}
|
|
13
|
+
let opened = false;
|
|
14
|
+
try {
|
|
15
|
+
await storage.open({ name: dbName, schema });
|
|
16
|
+
opened = true;
|
|
17
|
+
const existingMeta = await storage.getMeta();
|
|
18
|
+
const clientId = existingMeta.clientId || getOrCreateClientId(`${dbName}_client_id`);
|
|
19
|
+
if (clearExisting) {
|
|
20
|
+
await storage.clear();
|
|
21
|
+
}
|
|
22
|
+
let ops = [];
|
|
23
|
+
for (const row of resolvedSnapshot.rows) {
|
|
24
|
+
ops.push({ data: row.data, modelName: row.modelName, type: "put" });
|
|
25
|
+
if (ops.length >= batchSize) {
|
|
26
|
+
await storage.writeBatch(ops);
|
|
27
|
+
ops = [];
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
if (ops.length > 0) {
|
|
31
|
+
await storage.writeBatch(ops);
|
|
32
|
+
}
|
|
33
|
+
await storage.setMeta({
|
|
34
|
+
bootstrapComplete: true,
|
|
35
|
+
clientId,
|
|
36
|
+
firstSyncId: resolvedSnapshot.firstSyncId,
|
|
37
|
+
lastSyncAt: resolvedSnapshot.fetchedAt,
|
|
38
|
+
lastSyncId: resolvedSnapshot.lastSyncId,
|
|
39
|
+
schemaHash: localSchemaHash,
|
|
40
|
+
subscribedSyncGroups: resolvedSnapshot.groups,
|
|
41
|
+
});
|
|
42
|
+
return { applied: true, rowCount: resolvedSnapshot.rows.length };
|
|
43
|
+
}
|
|
44
|
+
finally {
|
|
45
|
+
if (closeAfter && opened) {
|
|
46
|
+
await storage.close();
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
const isPayload = (value) => typeof value === "object" && value !== null && "encoding" in value;
|
|
51
|
+
const resolveSnapshot = (snapshot) => {
|
|
52
|
+
if (typeof snapshot === "string") {
|
|
53
|
+
const parsed = JSON.parse(snapshot);
|
|
54
|
+
return deserializeBootstrapSnapshot(parsed);
|
|
55
|
+
}
|
|
56
|
+
if (isPayload(snapshot)) {
|
|
57
|
+
return deserializeBootstrapSnapshot(snapshot);
|
|
58
|
+
}
|
|
59
|
+
return Promise.resolve(snapshot);
|
|
60
|
+
};
|
|
61
|
+
//# sourceMappingURL=seed.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"seed.js","sourceRoot":"","sources":["../../src/bootstrap/seed.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { BootstrapSnapshot, BootstrapSnapshotPayload, SerializeBootstrapOptions } from "./types.js";
|
|
2
|
+
export declare const serializeBootstrapSnapshot: (snapshot: BootstrapSnapshot, options?: SerializeBootstrapOptions) => Promise<BootstrapSnapshotPayload>;
|
|
3
|
+
export declare const deserializeBootstrapSnapshot: (payload: BootstrapSnapshotPayload) => Promise<BootstrapSnapshot>;
|
|
4
|
+
export declare const encodeBootstrapSnapshot: (snapshot: BootstrapSnapshot, options?: SerializeBootstrapOptions) => Promise<string>;
|
|
5
|
+
export declare const decodeBootstrapSnapshot: (encoded: string) => Promise<BootstrapSnapshot>;
|
|
6
|
+
export declare const isBootstrapSnapshotStale: (snapshot: BootstrapSnapshot, maxAge?: number) => boolean;
|
|
7
|
+
//# sourceMappingURL=serialize.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"serialize.d.ts","sourceRoot":"","sources":["../../src/bootstrap/serialize.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,iBAAiB,EACjB,wBAAwB,EACxB,yBAAyB,EAC1B,MAAM,YAAY,CAAC;AA+CpB,eAAO,MAAM,0BAA0B,GACrC,UAAU,iBAAiB,EAC3B,UAAS,yBAA8B,KACtC,OAAO,CAAC,wBAAwB,CAmBlC,CAAC;AAEF,eAAO,MAAM,4BAA4B,GACvC,SAAS,wBAAwB,KAChC,OAAO,CAAC,iBAAiB,CAiB3B,CAAC;AAEF,eAAO,MAAM,uBAAuB,GAClC,UAAU,iBAAiB,EAC3B,UAAS,yBAA8B,KACtC,OAAO,CAAC,MAAM,CAGhB,CAAC;AAEF,eAAO,MAAM,uBAAuB,GAClC,SAAS,MAAM,KACd,OAAO,CAAC,iBAAiB,CAG3B,CAAC;AAEF,eAAO,MAAM,wBAAwB,GACnC,UAAU,iBAAiB,EAC3B,eAAe,KACd,OAAmD,CAAC"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
const canUseCompressionStreams = () => typeof CompressionStream !== "undefined" &&
|
|
2
|
+
typeof DecompressionStream !== "undefined";
|
|
3
|
+
const toBase64 = (bytes) => {
|
|
4
|
+
if (typeof Buffer !== "undefined") {
|
|
5
|
+
return Buffer.from(bytes).toString("base64");
|
|
6
|
+
}
|
|
7
|
+
let binary = "";
|
|
8
|
+
for (const byte of bytes) {
|
|
9
|
+
binary += String.fromCodePoint(byte);
|
|
10
|
+
}
|
|
11
|
+
return btoa(binary);
|
|
12
|
+
};
|
|
13
|
+
const fromBase64 = (encoded) => {
|
|
14
|
+
if (typeof Buffer !== "undefined") {
|
|
15
|
+
return new Uint8Array(Buffer.from(encoded, "base64"));
|
|
16
|
+
}
|
|
17
|
+
const binary = atob(encoded);
|
|
18
|
+
const bytes = new Uint8Array(binary.length);
|
|
19
|
+
for (let i = 0; i < binary.length; i += 1) {
|
|
20
|
+
bytes[i] = binary.codePointAt(i) ?? 0;
|
|
21
|
+
}
|
|
22
|
+
return bytes;
|
|
23
|
+
};
|
|
24
|
+
const compressToBase64 = async (input) => {
|
|
25
|
+
const compressedStream = new Blob([input])
|
|
26
|
+
.stream()
|
|
27
|
+
.pipeThrough(new CompressionStream("gzip"));
|
|
28
|
+
const buffer = await new Response(compressedStream).arrayBuffer();
|
|
29
|
+
return toBase64(new Uint8Array(buffer));
|
|
30
|
+
};
|
|
31
|
+
const decompressFromBase64 = (encoded) => {
|
|
32
|
+
const bytes = fromBase64(encoded);
|
|
33
|
+
const decompressedStream = new Blob([bytes])
|
|
34
|
+
.stream()
|
|
35
|
+
.pipeThrough(new DecompressionStream("gzip"));
|
|
36
|
+
return new Response(decompressedStream).text();
|
|
37
|
+
};
|
|
38
|
+
export const serializeBootstrapSnapshot = async (snapshot, options = {}) => {
|
|
39
|
+
const json = JSON.stringify(snapshot);
|
|
40
|
+
const shouldCompress = options.compress !== false && canUseCompressionStreams();
|
|
41
|
+
if (!shouldCompress) {
|
|
42
|
+
return {
|
|
43
|
+
data: json,
|
|
44
|
+
encoding: "json",
|
|
45
|
+
version: snapshot.version,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
const compressed = await compressToBase64(json);
|
|
49
|
+
return {
|
|
50
|
+
data: compressed,
|
|
51
|
+
encoding: "gzip-base64",
|
|
52
|
+
version: snapshot.version,
|
|
53
|
+
};
|
|
54
|
+
};
|
|
55
|
+
export const deserializeBootstrapSnapshot = async (payload) => {
|
|
56
|
+
if (payload.version !== 1) {
|
|
57
|
+
throw new Error(`Unsupported bootstrap payload version: ${payload.version}`);
|
|
58
|
+
}
|
|
59
|
+
if (payload.encoding === "json") {
|
|
60
|
+
return JSON.parse(payload.data);
|
|
61
|
+
}
|
|
62
|
+
if (!canUseCompressionStreams()) {
|
|
63
|
+
throw new Error("DecompressionStream is not available in this runtime");
|
|
64
|
+
}
|
|
65
|
+
const json = await decompressFromBase64(payload.data);
|
|
66
|
+
return JSON.parse(json);
|
|
67
|
+
};
|
|
68
|
+
export const encodeBootstrapSnapshot = async (snapshot, options = {}) => {
|
|
69
|
+
const payload = await serializeBootstrapSnapshot(snapshot, options);
|
|
70
|
+
return JSON.stringify(payload);
|
|
71
|
+
};
|
|
72
|
+
export const decodeBootstrapSnapshot = (encoded) => {
|
|
73
|
+
const payload = JSON.parse(encoded);
|
|
74
|
+
return deserializeBootstrapSnapshot(payload);
|
|
75
|
+
};
|
|
76
|
+
export const isBootstrapSnapshotStale = (snapshot, maxAge = 30_000) => Date.now() - snapshot.fetchedAt > maxAge;
|
|
77
|
+
//# sourceMappingURL=serialize.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"serialize.js","sourceRoot":"","sources":["../../src/bootstrap/serialize.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import type { StorageAdapter } from "@stratasync/client";
|
|
2
|
+
import type { ModelRegistrySnapshot, ModelRow, SchemaDefinition, SyncId } from "@stratasync/core";
|
|
3
|
+
export interface BootstrapSnapshot {
|
|
4
|
+
version: 1;
|
|
5
|
+
schemaHash: string;
|
|
6
|
+
lastSyncId: SyncId;
|
|
7
|
+
firstSyncId?: SyncId;
|
|
8
|
+
groups: string[];
|
|
9
|
+
rows: ModelRow[];
|
|
10
|
+
fetchedAt: number;
|
|
11
|
+
rowCount?: number;
|
|
12
|
+
}
|
|
13
|
+
export interface BootstrapSnapshotPayload {
|
|
14
|
+
version: 1;
|
|
15
|
+
encoding: "json" | "gzip-base64";
|
|
16
|
+
data: string;
|
|
17
|
+
}
|
|
18
|
+
export interface PrefetchBootstrapOptions {
|
|
19
|
+
endpoint: string;
|
|
20
|
+
authorization?: string;
|
|
21
|
+
headers?: Record<string, string>;
|
|
22
|
+
models?: string[];
|
|
23
|
+
groups?: string[];
|
|
24
|
+
schemaHash?: string;
|
|
25
|
+
timeout?: number;
|
|
26
|
+
}
|
|
27
|
+
export interface SerializeBootstrapOptions {
|
|
28
|
+
compress?: boolean;
|
|
29
|
+
}
|
|
30
|
+
export interface SeedStorageOptions {
|
|
31
|
+
storage: StorageAdapter;
|
|
32
|
+
snapshot: BootstrapSnapshot | BootstrapSnapshotPayload | string;
|
|
33
|
+
dbName?: string;
|
|
34
|
+
clearExisting?: boolean;
|
|
35
|
+
validateSchemaHash?: boolean;
|
|
36
|
+
batchSize?: number;
|
|
37
|
+
closeAfter?: boolean;
|
|
38
|
+
schema?: SchemaDefinition | ModelRegistrySnapshot;
|
|
39
|
+
}
|
|
40
|
+
export interface SeedStorageResult {
|
|
41
|
+
applied: boolean;
|
|
42
|
+
rowCount: number;
|
|
43
|
+
reason?: "schema_mismatch";
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/bootstrap/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACzD,OAAO,KAAK,EACV,qBAAqB,EACrB,QAAQ,EACR,gBAAgB,EAChB,MAAM,EACP,MAAM,kBAAkB,CAAC;AAE1B,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,CAAC,CAAC;IACX,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,IAAI,EAAE,QAAQ,EAAE,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,wBAAwB;IACvC,OAAO,EAAE,CAAC,CAAC;IACX,QAAQ,EAAE,MAAM,GAAG,aAAa,CAAC;IACjC,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,wBAAwB;IACvC,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,yBAAyB;IACxC,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,cAAc,CAAC;IACxB,QAAQ,EAAE,iBAAiB,GAAG,wBAAwB,GAAG,MAAM,CAAC;IAChE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,MAAM,CAAC,EAAE,gBAAgB,GAAG,qBAAqB,CAAC;CACnD;AAED,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,iBAAiB,CAAC;CAC5B"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/bootstrap/types.ts"],"names":[],"mappings":""}
|
package/dist/client.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC"}
|
package/dist/client.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,uGAAuG;AACvG,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { SyncClient } from "@stratasync/client";
|
|
2
|
+
import type { ReactNode } from "react";
|
|
3
|
+
/**
|
|
4
|
+
* Props for the Next.js sync provider
|
|
5
|
+
*/
|
|
6
|
+
export interface NextSyncProviderProps {
|
|
7
|
+
/** Sync client instance or factory function */
|
|
8
|
+
client: SyncClient | (() => SyncClient);
|
|
9
|
+
/** Children to render */
|
|
10
|
+
children: ReactNode;
|
|
11
|
+
/** Loading component to show while client resolves (typically one render frame) */
|
|
12
|
+
loading?: ReactNode;
|
|
13
|
+
/** Error component to show if initialization fails */
|
|
14
|
+
error?: (error: Error) => ReactNode;
|
|
15
|
+
/** Callback when client is ready */
|
|
16
|
+
onReady?: () => void;
|
|
17
|
+
/** Callback when an error occurs */
|
|
18
|
+
onError?: (error: Error) => void;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Next.js App Router compatible sync provider
|
|
22
|
+
*
|
|
23
|
+
* Renders children immediately while the sync client starts in the background.
|
|
24
|
+
* Children receive isReady=false (via context) until bootstrap completes,
|
|
25
|
+
* at which point useQuery hooks begin returning data.
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ```tsx
|
|
29
|
+
* <NextSyncProvider
|
|
30
|
+
* client={syncClient}
|
|
31
|
+
* loading={<Spinner />}
|
|
32
|
+
* error={(err) => <ErrorPage message={err.message} />}
|
|
33
|
+
* >
|
|
34
|
+
* {children}
|
|
35
|
+
* </NextSyncProvider>
|
|
36
|
+
* ```
|
|
37
|
+
*/
|
|
38
|
+
export declare const NextSyncProvider: ({ client, children, loading, error: errorComponent, onReady, onError, }: NextSyncProviderProps) => ReactNode;
|
|
39
|
+
//# sourceMappingURL=provider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../src/provider.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AAGrD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,+CAA+C;IAC/C,MAAM,EAAE,UAAU,GAAG,CAAC,MAAM,UAAU,CAAC,CAAC;IACxC,yBAAyB;IACzB,QAAQ,EAAE,SAAS,CAAC;IACpB,mFAAmF;IACnF,OAAO,CAAC,EAAE,SAAS,CAAC;IACpB,sDAAsD;IACtD,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,SAAS,CAAC;IACpC,oCAAoC;IACpC,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,oCAAoC;IACpC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;CAClC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,gBAAgB,GAAI,yEAO9B,qBAAqB,KAAG,SAkG1B,CAAC"}
|
package/dist/provider.js
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
// oxlint-disable no-use-before-define -- catch clause variable shadowing pattern
|
|
2
|
+
"use client";
|
|
3
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
4
|
+
import { SyncProvider as BaseSyncProvider } from "@stratasync/react";
|
|
5
|
+
import { useEffect, useRef, useState } from "react";
|
|
6
|
+
/**
|
|
7
|
+
* Next.js App Router compatible sync provider
|
|
8
|
+
*
|
|
9
|
+
* Renders children immediately while the sync client starts in the background.
|
|
10
|
+
* Children receive isReady=false (via context) until bootstrap completes,
|
|
11
|
+
* at which point useQuery hooks begin returning data.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```tsx
|
|
15
|
+
* <NextSyncProvider
|
|
16
|
+
* client={syncClient}
|
|
17
|
+
* loading={<Spinner />}
|
|
18
|
+
* error={(err) => <ErrorPage message={err.message} />}
|
|
19
|
+
* >
|
|
20
|
+
* {children}
|
|
21
|
+
* </NextSyncProvider>
|
|
22
|
+
* ```
|
|
23
|
+
*/
|
|
24
|
+
export const NextSyncProvider = ({ client, children, loading = null, error: errorComponent, onReady, onError, }) => {
|
|
25
|
+
const [resolvedClient, setResolvedClient] = useState(null);
|
|
26
|
+
const [syncError, setSyncError] = useState(null);
|
|
27
|
+
const onReadyRef = useRef(onReady);
|
|
28
|
+
const onErrorRef = useRef(onError);
|
|
29
|
+
onReadyRef.current = onReady;
|
|
30
|
+
onErrorRef.current = onError;
|
|
31
|
+
// Resolve client synchronously — don't await start().
|
|
32
|
+
// BaseSyncProvider handles start() in the background via autoStart.
|
|
33
|
+
useEffect(() => {
|
|
34
|
+
let mounted = true;
|
|
35
|
+
let ownsClient = false;
|
|
36
|
+
let clientInstance = null;
|
|
37
|
+
setSyncError(null);
|
|
38
|
+
try {
|
|
39
|
+
if (typeof client === "function") {
|
|
40
|
+
clientInstance = client();
|
|
41
|
+
ownsClient = true;
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
clientInstance = client;
|
|
45
|
+
}
|
|
46
|
+
if (mounted) {
|
|
47
|
+
setResolvedClient(clientInstance);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
if (mounted) {
|
|
52
|
+
const e = error instanceof Error ? error : new Error(String(error));
|
|
53
|
+
setSyncError(e);
|
|
54
|
+
onErrorRef.current?.(e);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return () => {
|
|
58
|
+
mounted = false;
|
|
59
|
+
// Stop client on cleanup if we own it (factory was used)
|
|
60
|
+
if (ownsClient && clientInstance) {
|
|
61
|
+
// oxlint-disable-next-line prefer-await-to-then -- fire-and-forget pattern
|
|
62
|
+
clientInstance.stop().catch(() => {
|
|
63
|
+
/* noop */
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
}, [client]);
|
|
68
|
+
// Subscribe to client events for onReady/onError callbacks
|
|
69
|
+
useEffect(() => {
|
|
70
|
+
if (!resolvedClient) {
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
let mounted = true;
|
|
74
|
+
const unsubState = resolvedClient.onStateChange((state) => {
|
|
75
|
+
if (mounted && state === "syncing") {
|
|
76
|
+
onReadyRef.current?.();
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
const unsubEvents = resolvedClient.onEvent((event) => {
|
|
80
|
+
if (!mounted) {
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
if (event.type === "syncError") {
|
|
84
|
+
setSyncError(event.error);
|
|
85
|
+
onErrorRef.current?.(event.error);
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
return () => {
|
|
89
|
+
mounted = false;
|
|
90
|
+
unsubState();
|
|
91
|
+
unsubEvents();
|
|
92
|
+
};
|
|
93
|
+
}, [resolvedClient]);
|
|
94
|
+
if (syncError) {
|
|
95
|
+
if (errorComponent) {
|
|
96
|
+
return errorComponent(syncError);
|
|
97
|
+
}
|
|
98
|
+
throw syncError;
|
|
99
|
+
}
|
|
100
|
+
if (!resolvedClient) {
|
|
101
|
+
return loading;
|
|
102
|
+
}
|
|
103
|
+
// Render children immediately. BaseSyncProvider starts the client
|
|
104
|
+
// in the background (autoStart) and propagates isReady through context.
|
|
105
|
+
// useQuery hooks return { data: [], isLoading: true } until ready.
|
|
106
|
+
return (_jsx(BaseSyncProvider, { autoStart: true, autoStop: false, client: resolvedClient, children: children }));
|
|
107
|
+
};
|
|
108
|
+
//# sourceMappingURL=provider.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provider.js","sourceRoot":"","sources":["../src/provider.tsx"],"names":[],"mappings":"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"}
|
package/dist/server.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AACA,OAAO,EACL,uBAAuB,EACvB,iBAAiB,EACjB,wBAAwB,EACxB,0BAA0B,GAC3B,MAAM,sBAAsB,CAAC"}
|
package/dist/server.js
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
// biome-ignore-all lint/performance/noBarrelFile: This is the package's public server-side API entry point
|
|
2
|
+
export { encodeBootstrapSnapshot, prefetchBootstrap, seedStorageFromBootstrap, serializeBootstrapSnapshot, } from "./bootstrap/index.js";
|
|
3
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,2GAA2G;AAC3G,OAAO,EACL,uBAAuB,EACvB,iBAAiB,EACjB,wBAAwB,EACxB,0BAA0B,GAC3B,MAAM,sBAAsB,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@stratasync/next",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"files": [
|
|
5
|
+
"dist"
|
|
6
|
+
],
|
|
7
|
+
"type": "module",
|
|
8
|
+
"main": "./dist/client.js",
|
|
9
|
+
"types": "./dist/client.d.ts",
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"node": {
|
|
13
|
+
"types": "./dist/server.d.ts",
|
|
14
|
+
"import": "./dist/server.js"
|
|
15
|
+
},
|
|
16
|
+
"default": {
|
|
17
|
+
"types": "./dist/client.d.ts",
|
|
18
|
+
"default": "./dist/client.js"
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"./client": {
|
|
22
|
+
"types": "./dist/client.d.ts",
|
|
23
|
+
"import": "./dist/client.js"
|
|
24
|
+
},
|
|
25
|
+
"./server": {
|
|
26
|
+
"types": "./dist/server.d.ts",
|
|
27
|
+
"import": "./dist/server.js"
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
"publishConfig": {
|
|
31
|
+
"access": "public"
|
|
32
|
+
},
|
|
33
|
+
"scripts": {
|
|
34
|
+
"build": "rm -rf dist && tsc -p tsconfig.build.json",
|
|
35
|
+
"clean": "rm -rf dist",
|
|
36
|
+
"dev": "tsc --watch -p tsconfig.build.json",
|
|
37
|
+
"lint": "oxlint .",
|
|
38
|
+
"lint:fix": "oxlint --fix .",
|
|
39
|
+
"format": "oxfmt --write .",
|
|
40
|
+
"format:check": "oxfmt --check .",
|
|
41
|
+
"check-types": "tsc --noEmit",
|
|
42
|
+
"test": "vitest run"
|
|
43
|
+
},
|
|
44
|
+
"dependencies": {
|
|
45
|
+
"@stratasync/client": "*",
|
|
46
|
+
"@stratasync/core": "*",
|
|
47
|
+
"@stratasync/react": "*"
|
|
48
|
+
},
|
|
49
|
+
"devDependencies": {
|
|
50
|
+
"@testing-library/jest-dom": "^6.9.1",
|
|
51
|
+
"@testing-library/react": "^16.3.2",
|
|
52
|
+
"@types/react": "^19.2.14",
|
|
53
|
+
"@types/react-dom": "^19.2.3",
|
|
54
|
+
"jsdom": "^29.0.0",
|
|
55
|
+
"lefthook": "^2.1.4",
|
|
56
|
+
"oxfmt": "^0.41.0",
|
|
57
|
+
"oxlint": "^1.56.0",
|
|
58
|
+
"react": "^19.2.4",
|
|
59
|
+
"react-dom": "^19.2.4",
|
|
60
|
+
"typescript": "^5.9.3",
|
|
61
|
+
"ultracite": "^7.3.2",
|
|
62
|
+
"vitest": "^4.1.0"
|
|
63
|
+
},
|
|
64
|
+
"peerDependencies": {
|
|
65
|
+
"next": "^14.0.0 || ^15.0.0 || ^16.0.0",
|
|
66
|
+
"react": "^18.0.0 || ^19.0.0"
|
|
67
|
+
}
|
|
68
|
+
}
|