@sveltebase/sync 1.2.0 → 1.4.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 +129 -451
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/live-query.svelte.d.ts.map +1 -1
- package/dist/client/status.svelte.d.ts.map +1 -1
- package/dist/cloudflare/index.d.ts +17 -0
- package/dist/cloudflare/index.d.ts.map +1 -0
- package/dist/cloudflare/index.js +116 -0
- package/dist/index.d.ts +1 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +0 -1
- package/dist/protocol.d.ts.map +1 -1
- package/dist/server/broker.d.ts +5 -4
- package/dist/server/broker.d.ts.map +1 -1
- package/dist/server/broker.js +3 -5
- package/dist/server/engine.d.ts +5 -2
- package/dist/server/engine.d.ts.map +1 -1
- package/dist/server/engine.js +26 -5
- package/dist/server/handler.d.ts +15 -16
- package/dist/server/handler.d.ts.map +1 -1
- package/dist/server/handler.js +67 -98
- package/dist/server/index.d.ts +29 -15
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +2 -0
- package/dist/sveltekit/index.d.ts +10 -0
- package/dist/sveltekit/index.d.ts.map +1 -0
- package/dist/sveltekit/index.js +29 -0
- package/package.json +9 -9
- package/dist/global.d.ts +0 -25
- package/dist/server/dev-engine.d.ts +0 -15
- package/dist/server/dev-engine.d.ts.map +0 -1
- package/dist/server/dev-engine.js +0 -121
- package/dist/vite.d.ts +0 -6
- package/dist/vite.d.ts.map +0 -1
- package/dist/vite.js +0 -55
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"status.svelte.d.ts","sourceRoot":"","sources":["
|
|
1
|
+
{"version":3,"file":"status.svelte.d.ts","sourceRoot":"","sources":["status.svelte.ts"],"names":[],"mappings":"AAAA,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,MAAM,CAAqE;IAEnF,IAAI,KAAK,IAIW,YAAY,GAAG,WAAW,GAAG,cAAc,CAF9D;IAED,IAAI,KAAK,CAAC,QAAQ,EAAE,YAAY,GAAG,WAAW,GAAG,cAAc,EAE9D;CACF"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { SyncEngineBase } from "../server/engine.js";
|
|
2
|
+
import { type SyncAuthResult } from "../server/handler.js";
|
|
3
|
+
import type { SyncHandler, SyncPlatform } from "../server/index.js";
|
|
4
|
+
export type SyncWorkerOptions<TAuth = unknown> = {
|
|
5
|
+
handlers: SyncHandler[];
|
|
6
|
+
durableObjectBinding?: string;
|
|
7
|
+
websocketPath?: string;
|
|
8
|
+
auth?: (request: Request, platform: SyncPlatform) => Promise<SyncAuthResult<TAuth>> | SyncAuthResult<TAuth>;
|
|
9
|
+
identity?: (auth: TAuth) => string | number | bigint | null | undefined;
|
|
10
|
+
allowUnauthenticated?: boolean;
|
|
11
|
+
};
|
|
12
|
+
export declare function createSyncWorker<TAuth = unknown>(options: SyncWorkerOptions<TAuth>): ExportedHandler;
|
|
13
|
+
export declare function defineSyncWorker<TAuth = unknown>(options: SyncWorkerOptions<TAuth>): ExportedHandler;
|
|
14
|
+
export declare class SyncEngine extends SyncEngineBase {
|
|
15
|
+
constructor(ctx: DurableObjectState, env: Record<string, unknown>);
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cloudflare/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,EAEL,KAAK,cAAc,EACpB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AA2CpE,MAAM,MAAM,iBAAiB,CAAC,KAAK,GAAG,OAAO,IAAI;IAC/C,QAAQ,EAAE,WAAW,EAAE,CAAC;IACxB,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,IAAI,CAAC,EAAE,CACL,OAAO,EAAE,OAAO,EAChB,QAAQ,EAAE,YAAY,KACnB,OAAO,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC,GAAG,cAAc,CAAC,KAAK,CAAC,CAAC;IAC5D,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,KAAK,KAAK,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IACxE,oBAAoB,CAAC,EAAE,OAAO,CAAC;CAChC,CAAC;AA6FF,wBAAgB,gBAAgB,CAAC,KAAK,GAAG,OAAO,EAC9C,OAAO,EAAE,iBAAiB,CAAC,KAAK,CAAC,GAChC,eAAe,CAgDjB;AAED,wBAAgB,gBAAgB,CAAC,KAAK,GAAG,OAAO,EAC9C,OAAO,EAAE,iBAAiB,CAAC,KAAK,CAAC,GAChC,eAAe,CAEjB;AAED,qBAAa,UAAW,SAAQ,cAAc;gBAChC,GAAG,EAAE,kBAAkB,EAAE,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;CAGlE"}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { SyncEngineBase } from "../server/engine.js";
|
|
2
|
+
import { INTERNAL_AUTH_HEADER, } from "../server/handler.js";
|
|
3
|
+
let activeHandlers = [];
|
|
4
|
+
function defaultIdentity(auth) {
|
|
5
|
+
const value = auth?.identity ?? auth?.user?.id ?? auth?.userId;
|
|
6
|
+
return value == null ? null : String(value);
|
|
7
|
+
}
|
|
8
|
+
function serializeConnectionAuth(auth, identity) {
|
|
9
|
+
const payload = { auth, identity };
|
|
10
|
+
return btoa(unescape(encodeURIComponent(JSON.stringify(payload))));
|
|
11
|
+
}
|
|
12
|
+
function createPlatform(request, env, ctx) {
|
|
13
|
+
return {
|
|
14
|
+
env,
|
|
15
|
+
ctx,
|
|
16
|
+
context: ctx,
|
|
17
|
+
caches,
|
|
18
|
+
cf: request.cf,
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
function withPath(request, pathname) {
|
|
22
|
+
const url = new URL(request.url);
|
|
23
|
+
url.pathname = pathname;
|
|
24
|
+
return url.toString();
|
|
25
|
+
}
|
|
26
|
+
function isWebSocketRequest(request) {
|
|
27
|
+
return request.headers.get("Upgrade")?.toLowerCase() === "websocket";
|
|
28
|
+
}
|
|
29
|
+
async function forwardToEngine(request, env, durableObjectBinding) {
|
|
30
|
+
const namespace = env[durableObjectBinding];
|
|
31
|
+
if (!namespace) {
|
|
32
|
+
return new Response(`Missing ${durableObjectBinding} Durable Object binding`, { status: 500 });
|
|
33
|
+
}
|
|
34
|
+
const id = namespace.idFromName("global");
|
|
35
|
+
return namespace.get(id).fetch(request);
|
|
36
|
+
}
|
|
37
|
+
async function handleWebSocket(request, env, ctx, options) {
|
|
38
|
+
if (!isWebSocketRequest(request)) {
|
|
39
|
+
return new Response("Expected Upgrade: websocket", { status: 426 });
|
|
40
|
+
}
|
|
41
|
+
const publicHeaders = new Headers(request.headers);
|
|
42
|
+
publicHeaders.delete(INTERNAL_AUTH_HEADER);
|
|
43
|
+
const publicRequest = new Request(request, {
|
|
44
|
+
headers: publicHeaders,
|
|
45
|
+
});
|
|
46
|
+
const platform = createPlatform(publicRequest, env, ctx);
|
|
47
|
+
let resolvedAuth = null;
|
|
48
|
+
let identity = null;
|
|
49
|
+
if (options.auth) {
|
|
50
|
+
resolvedAuth = (await options.auth(publicRequest, platform)) ?? null;
|
|
51
|
+
if (!resolvedAuth && options.allowUnauthenticated === false) {
|
|
52
|
+
return new Response("Unauthorized", { status: 401 });
|
|
53
|
+
}
|
|
54
|
+
if (resolvedAuth) {
|
|
55
|
+
const identityValue = options.identity
|
|
56
|
+
? options.identity(resolvedAuth)
|
|
57
|
+
: defaultIdentity(resolvedAuth);
|
|
58
|
+
identity = identityValue == null ? null : String(identityValue);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
else if (options.allowUnauthenticated === false) {
|
|
62
|
+
return new Response("Unauthorized", { status: 401 });
|
|
63
|
+
}
|
|
64
|
+
const forwardedHeaders = new Headers(publicRequest.headers);
|
|
65
|
+
forwardedHeaders.delete(INTERNAL_AUTH_HEADER);
|
|
66
|
+
if (resolvedAuth) {
|
|
67
|
+
forwardedHeaders.set(INTERNAL_AUTH_HEADER, serializeConnectionAuth(resolvedAuth, identity));
|
|
68
|
+
}
|
|
69
|
+
const forwardedRequest = new Request(withPath(publicRequest, "/websocket"), publicRequest);
|
|
70
|
+
for (const [key, value] of forwardedHeaders) {
|
|
71
|
+
forwardedRequest.headers.set(key, value);
|
|
72
|
+
}
|
|
73
|
+
return forwardToEngine(forwardedRequest, env, options.durableObjectBinding);
|
|
74
|
+
}
|
|
75
|
+
export function createSyncWorker(options) {
|
|
76
|
+
activeHandlers = options.handlers;
|
|
77
|
+
const durableObjectBinding = options.durableObjectBinding ?? "SYNC_ENGINE";
|
|
78
|
+
const websocketPath = options.websocketPath ?? "/api/sync";
|
|
79
|
+
const authMetadata = options.auth;
|
|
80
|
+
const allowUnauthenticated = options.allowUnauthenticated ??
|
|
81
|
+
authMetadata?.allowUnauthenticated ??
|
|
82
|
+
true;
|
|
83
|
+
return {
|
|
84
|
+
async fetch(request, env, ctx) {
|
|
85
|
+
const url = new URL(request.url);
|
|
86
|
+
const workerEnv = env;
|
|
87
|
+
if (url.pathname === websocketPath && request.method === "GET") {
|
|
88
|
+
return handleWebSocket(request, workerEnv, ctx, {
|
|
89
|
+
...options,
|
|
90
|
+
durableObjectBinding,
|
|
91
|
+
websocketPath,
|
|
92
|
+
allowUnauthenticated,
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
if (url.pathname === "/websocket") {
|
|
96
|
+
return new Response("Not found", { status: 404 });
|
|
97
|
+
}
|
|
98
|
+
if ((url.pathname === "/broadcast" ||
|
|
99
|
+
url.pathname === "/broadcast-batch") &&
|
|
100
|
+
request.method === "POST") {
|
|
101
|
+
const headers = new Headers(request.headers);
|
|
102
|
+
headers.delete(INTERNAL_AUTH_HEADER);
|
|
103
|
+
return forwardToEngine(new Request(request, { headers }), workerEnv, durableObjectBinding);
|
|
104
|
+
}
|
|
105
|
+
return new Response("Not found", { status: 404 });
|
|
106
|
+
},
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
export function defineSyncWorker(options) {
|
|
110
|
+
return createSyncWorker(options);
|
|
111
|
+
}
|
|
112
|
+
export class SyncEngine extends SyncEngineBase {
|
|
113
|
+
constructor(ctx, env) {
|
|
114
|
+
super(ctx, env, activeHandlers);
|
|
115
|
+
}
|
|
116
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
export { SyncClient, createLiveQuery } from "./client/index.js";
|
|
2
2
|
export type { LiveQueryState } from "./client/index.js";
|
|
3
3
|
export { defineSync } from "./server/index.js";
|
|
4
|
-
export {
|
|
5
|
-
export type { PublishEventData, InferSchemaFromHandlers } from "./server/handler.js";
|
|
6
|
-
export type { SyncContext, SyncHandler } from "./server/index.js";
|
|
4
|
+
export type { BulkPublishFn, InferSchemaFromHandlers, PublishEventData, PublishFn, SyncAuthResult, SyncConnectionAuth, SyncContext, SyncHandler, SyncPlatform, SyncPublisherOptions, } from "./server/index.js";
|
|
7
5
|
export type { SyncMessage } from "./protocol.js";
|
|
8
6
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAChE,YAAY,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAC/C,YAAY,EACV,aAAa,EACb,uBAAuB,EACvB,gBAAgB,EAChB,SAAS,EACT,cAAc,EACd,kBAAkB,EAClB,WAAW,EACX,WAAW,EACX,YAAY,EACZ,oBAAoB,GACrB,MAAM,mBAAmB,CAAC;AAC3B,YAAY,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC"}
|
package/dist/index.js
CHANGED
package/dist/protocol.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"protocol.d.ts","sourceRoot":"","sources":["
|
|
1
|
+
{"version":3,"file":"protocol.d.ts","sourceRoot":"","sources":["protocol.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,WAAW,GACnB;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,GACtD;IAAE,IAAI,EAAE,aAAa,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GACxC;IACE,IAAI,EAAE,QAAQ,CAAC;IACf,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,QAAQ,GAAG,QAAQ,GAAG,QAAQ,CAAC;IACvC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,GAAG,CAAC;CACZ,GACD;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAChB;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAChB;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,GAAG,EAAE,CAAC;IAAC,OAAO,CAAC,EAAE,OAAO,CAAA;CAAE,GACrE;IAAE,IAAI,EAAE,KAAK,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,GAAG,CAAA;CAAE,GACvC;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAC7C;IACE,IAAI,EAAE,QAAQ,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,QAAQ,GAAG,QAAQ,GAAG,QAAQ,CAAC;IACvC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,GAAG,CAAC;IACX,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,GACD;IACE,IAAI,EAAE,OAAO,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,KAAK,CAAC;QACb,MAAM,EAAE,QAAQ,GAAG,QAAQ,GAAG,QAAQ,CAAC;QACvC,GAAG,CAAC,EAAE,MAAM,CAAC;QACb,IAAI,CAAC,EAAE,GAAG,CAAC;KACZ,CAAC,CAAC;CACJ,CAAC;AAEN,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI,CAcjE"}
|
package/dist/server/broker.d.ts
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
|
-
import type { SyncHandler } from "./index.js";
|
|
1
|
+
import type { SyncHandler, SyncPlatform } from "./index.js";
|
|
2
2
|
export interface ISyncConnection {
|
|
3
3
|
send(data: string): void;
|
|
4
4
|
close(code?: number, reason?: string): void;
|
|
5
5
|
getAuth(): any;
|
|
6
6
|
setAuth(auth: any): void;
|
|
7
|
+
getIdentity(): string | null;
|
|
8
|
+
setIdentity(identity: string | null): void;
|
|
7
9
|
getSubscribedChannels(): Set<string>;
|
|
8
10
|
readonly headers: Headers;
|
|
9
11
|
readonly url: string;
|
|
@@ -11,8 +13,7 @@ export interface ISyncConnection {
|
|
|
11
13
|
export declare class SyncBroker {
|
|
12
14
|
private handlers;
|
|
13
15
|
private connections;
|
|
14
|
-
|
|
15
|
-
constructor(handlers: SyncHandler[], authorizeConnection?: (request: Request, platform: App.Platform | undefined) => Promise<any>);
|
|
16
|
+
constructor(handlers: SyncHandler[]);
|
|
16
17
|
setHandlers(handlers: SyncHandler[]): void;
|
|
17
18
|
registerConnection(conn: ISyncConnection): void;
|
|
18
19
|
removeConnection(conn: ISyncConnection): void;
|
|
@@ -20,7 +21,7 @@ export declare class SyncBroker {
|
|
|
20
21
|
* Resolves the appropriate handler for a channel name.
|
|
21
22
|
*/
|
|
22
23
|
private findHandler;
|
|
23
|
-
handleMessage(conn: ISyncConnection, rawMessage: string, platform:
|
|
24
|
+
handleMessage(conn: ISyncConnection, rawMessage: string, platform: SyncPlatform, request: Request): Promise<void>;
|
|
24
25
|
private broadcastChange;
|
|
25
26
|
handleExternalChange(channel: string, action: "create" | "update" | "delete", key: string | undefined, data: any): Promise<void>;
|
|
26
27
|
handleExternalBatchChange(channel: string, changes: Array<{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"broker.d.ts","sourceRoot":"","sources":["
|
|
1
|
+
{"version":3,"file":"broker.d.ts","sourceRoot":"","sources":["broker.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAe,YAAY,EAAE,MAAM,YAAY,CAAC;AAGzE,MAAM,WAAW,eAAe;IAC9B,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5C,OAAO,IAAI,GAAG,CAAC;IACf,OAAO,CAAC,IAAI,EAAE,GAAG,GAAG,IAAI,CAAC;IACzB,WAAW,IAAI,MAAM,GAAG,IAAI,CAAC;IAC7B,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAAC;IAC3C,qBAAqB,IAAI,GAAG,CAAC,MAAM,CAAC,CAAC;IACrC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;CACtB;AAED,qBAAa,UAAU;IACrB,OAAO,CAAC,QAAQ,CAA2B;IAC3C,OAAO,CAAC,WAAW,CAAmC;gBAE1C,QAAQ,EAAE,WAAW,EAAE;IAK5B,WAAW,CAAC,QAAQ,EAAE,WAAW,EAAE;IAanC,kBAAkB,CAAC,IAAI,EAAE,eAAe;IAIxC,gBAAgB,CAAC,IAAI,EAAE,eAAe;IAI7C;;OAEG;IACH,OAAO,CAAC,WAAW;IAqCN,aAAa,CACxB,IAAI,EAAE,eAAe,EACrB,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,YAAY,EACtB,OAAO,EAAE,OAAO;YA2IJ,eAAe;IAmDhB,oBAAoB,CAC/B,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,QAAQ,GAAG,QAAQ,GAAG,QAAQ,EACtC,GAAG,EAAE,MAAM,GAAG,SAAS,EACvB,IAAI,EAAE,GAAG;IAqBE,yBAAyB,CACpC,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,KAAK,CAAC;QAAE,MAAM,EAAE,QAAQ,GAAG,QAAQ,GAAG,QAAQ,CAAC;QAAC,GAAG,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,GAAG,CAAA;KAAE,CAAC;CAkBvF"}
|
package/dist/server/broker.js
CHANGED
|
@@ -2,11 +2,9 @@ import { parseSyncMessage } from "../protocol.js";
|
|
|
2
2
|
export class SyncBroker {
|
|
3
3
|
handlers;
|
|
4
4
|
connections = new Set();
|
|
5
|
-
|
|
6
|
-
constructor(handlers, authorizeConnection) {
|
|
5
|
+
constructor(handlers) {
|
|
7
6
|
this.handlers = new Map();
|
|
8
7
|
this.setHandlers(handlers);
|
|
9
|
-
this.authorizeConnection = authorizeConnection;
|
|
10
8
|
}
|
|
11
9
|
setHandlers(handlers) {
|
|
12
10
|
this.handlers.clear();
|
|
@@ -71,6 +69,7 @@ export class SyncBroker {
|
|
|
71
69
|
platform,
|
|
72
70
|
request,
|
|
73
71
|
auth,
|
|
72
|
+
identity: conn.getIdentity(),
|
|
74
73
|
};
|
|
75
74
|
try {
|
|
76
75
|
switch (msg.type) {
|
|
@@ -194,8 +193,7 @@ export class SyncBroker {
|
|
|
194
193
|
}
|
|
195
194
|
// Filter based on scope
|
|
196
195
|
if (allowedUserIds !== "all") {
|
|
197
|
-
const
|
|
198
|
-
const userId = connAuth?.userId;
|
|
196
|
+
const userId = conn.getIdentity();
|
|
199
197
|
if (!userId || !allowedUserIds.includes(userId)) {
|
|
200
198
|
continue;
|
|
201
199
|
}
|
package/dist/server/engine.d.ts
CHANGED
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
import { DurableObject } from "cloudflare:workers";
|
|
2
2
|
import { SyncBroker } from "./broker.js";
|
|
3
|
-
|
|
3
|
+
import type { SyncHandler } from "./index.js";
|
|
4
|
+
type SyncEngineEnv = Record<string, unknown>;
|
|
5
|
+
export declare class SyncEngineBase extends DurableObject<SyncEngineEnv> {
|
|
4
6
|
protected broker: SyncBroker;
|
|
5
7
|
private connMap;
|
|
6
|
-
constructor(ctx: DurableObjectState, env:
|
|
8
|
+
constructor(ctx: DurableObjectState, env: SyncEngineEnv, handlers: SyncHandler[]);
|
|
7
9
|
fetch(request: Request): Promise<Response>;
|
|
8
10
|
private connectWebSocket;
|
|
9
11
|
webSocketMessage(ws: WebSocket, message: string | ArrayBuffer): Promise<void>;
|
|
10
12
|
webSocketClose(ws: WebSocket, code: number, reason: string): void;
|
|
11
13
|
webSocketError(ws: WebSocket): void;
|
|
12
14
|
}
|
|
15
|
+
export {};
|
|
13
16
|
//# sourceMappingURL=engine.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["
|
|
1
|
+
{"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["engine.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,EAAE,UAAU,EAAwB,MAAM,aAAa,CAAC;AAE/D,OAAO,KAAK,EAAE,WAAW,EAAgB,MAAM,YAAY,CAAC;AAE5D,KAAK,aAAa,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAoE7C,qBAAa,cAAe,SAAQ,aAAa,CAAC,aAAa,CAAC;IAC9D,SAAS,CAAC,MAAM,EAAE,UAAU,CAAC;IAC7B,OAAO,CAAC,OAAO,CAAkD;gBAG/D,GAAG,EAAE,kBAAkB,EACvB,GAAG,EAAE,aAAa,EAClB,QAAQ,EAAE,WAAW,EAAE;IAMnB,KAAK,CAAC,OAAO,EAAE,OAAO;IAoC5B,OAAO,CAAC,gBAAgB;IA4BlB,gBAAgB,CAAC,EAAE,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,GAAG,WAAW;IAoBnE,cAAc,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;IAa1D,cAAc,CAAC,EAAE,EAAE,SAAS;CAO7B"}
|
package/dist/server/engine.js
CHANGED
|
@@ -1,8 +1,20 @@
|
|
|
1
1
|
import { DurableObject } from "cloudflare:workers";
|
|
2
2
|
import { SyncBroker } from "./broker.js";
|
|
3
|
+
import { INTERNAL_AUTH_HEADER } from "./handler.js";
|
|
4
|
+
function deserializeConnectionAuth(value) {
|
|
5
|
+
if (!value)
|
|
6
|
+
return null;
|
|
7
|
+
try {
|
|
8
|
+
return JSON.parse(decodeURIComponent(escape(atob(value))));
|
|
9
|
+
}
|
|
10
|
+
catch {
|
|
11
|
+
return null;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
3
14
|
class CloudflareSyncConnection {
|
|
4
15
|
ws;
|
|
5
16
|
auth = null;
|
|
17
|
+
identity = null;
|
|
6
18
|
subscribedChannels = new Set();
|
|
7
19
|
headers;
|
|
8
20
|
url;
|
|
@@ -33,6 +45,12 @@ class CloudflareSyncConnection {
|
|
|
33
45
|
setAuth(newAuth) {
|
|
34
46
|
this.auth = newAuth;
|
|
35
47
|
}
|
|
48
|
+
getIdentity() {
|
|
49
|
+
return this.identity;
|
|
50
|
+
}
|
|
51
|
+
setIdentity(identity) {
|
|
52
|
+
this.identity = identity;
|
|
53
|
+
}
|
|
36
54
|
getSubscribedChannels() {
|
|
37
55
|
return this.subscribedChannels;
|
|
38
56
|
}
|
|
@@ -84,10 +102,10 @@ export class SyncEngineBase extends DurableObject {
|
|
|
84
102
|
const [client, server] = Object.values(new WebSocketPair());
|
|
85
103
|
this.ctx.acceptWebSocket(server);
|
|
86
104
|
const conn = new CloudflareSyncConnection(server, request);
|
|
87
|
-
const
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
conn.
|
|
105
|
+
const forwardedAuth = deserializeConnectionAuth(request.headers.get(INTERNAL_AUTH_HEADER));
|
|
106
|
+
if (forwardedAuth) {
|
|
107
|
+
conn.setAuth(forwardedAuth.auth);
|
|
108
|
+
conn.setIdentity(forwardedAuth.identity);
|
|
91
109
|
}
|
|
92
110
|
this.connMap.set(server, conn);
|
|
93
111
|
this.broker.registerConnection(conn);
|
|
@@ -105,7 +123,10 @@ export class SyncEngineBase extends DurableObject {
|
|
|
105
123
|
const request = new Request(conn.url, {
|
|
106
124
|
headers: conn.headers,
|
|
107
125
|
});
|
|
108
|
-
|
|
126
|
+
const platform = {
|
|
127
|
+
env: this.env,
|
|
128
|
+
};
|
|
129
|
+
await this.broker.handleMessage(conn, message, platform, request);
|
|
109
130
|
}
|
|
110
131
|
webSocketClose(ws, code, reason) {
|
|
111
132
|
const conn = this.connMap.get(ws);
|
package/dist/server/handler.d.ts
CHANGED
|
@@ -1,27 +1,26 @@
|
|
|
1
|
-
import type { SyncHandler } from "./index.js";
|
|
1
|
+
import type { SyncHandler, SyncPlatform } from "./index.js";
|
|
2
|
+
export type SyncAuthResult<TAuth> = TAuth | null | undefined;
|
|
3
|
+
export declare const INTERNAL_AUTH_HEADER = "x-sveltebase-sync-auth";
|
|
2
4
|
export type PublishEventData<TRecord, TAction extends "create" | "update" | "delete"> = TAction extends "create" ? TRecord : TAction extends "update" ? Partial<TRecord> : {
|
|
3
5
|
updatedAt?: string;
|
|
4
6
|
} | undefined;
|
|
5
7
|
export type InferSchemaFromHandlers<T extends SyncHandler[]> = {
|
|
6
8
|
[K in T[number] as K["config"]["channel"] extends string ? K["config"]["channel"] : K["config"]["channel"] extends (...args: any[]) => infer R ? R extends string ? R : string : string]: K extends SyncHandler<infer TRow> ? TRow : never;
|
|
7
9
|
};
|
|
8
|
-
export
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
}>) => Promise<void>;
|
|
15
|
-
export
|
|
10
|
+
export type SyncPublisherOptions = {
|
|
11
|
+
binding?: string;
|
|
12
|
+
fallbackUrl?: string;
|
|
13
|
+
durableObjectBinding?: string;
|
|
14
|
+
platform?: SyncPlatform;
|
|
15
|
+
};
|
|
16
|
+
export type PublishFn<TSchema extends Record<string, unknown>> = <TChannel extends keyof TSchema & string, TAction extends "create" | "update" | "delete">(channel: TChannel | `${TChannel}:${string}`, action: TAction, key: string | undefined, data: PublishEventData<TSchema[TChannel], TAction>) => Promise<void>;
|
|
17
|
+
export type BulkPublishFn<TSchema extends Record<string, unknown>> = <TChannel extends keyof TSchema & string>(channel: TChannel | `${TChannel}:${string}`, changes: Array<{
|
|
16
18
|
action: "create" | "update" | "delete";
|
|
17
19
|
key?: string;
|
|
18
20
|
data?: any;
|
|
19
21
|
}>) => Promise<void>;
|
|
20
|
-
export declare function
|
|
21
|
-
export declare function
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
data?: any;
|
|
25
|
-
}>): Promise<void>;
|
|
26
|
-
export declare function handleUpgrade(request: Request, platform: App.Platform | undefined): Promise<Response>;
|
|
22
|
+
export declare function createPublisher<TSchema extends Record<string, unknown>>(options: SyncPublisherOptions): PublishFn<TSchema>;
|
|
23
|
+
export declare function createPublisher<THandlers extends SyncHandler[]>(options: SyncPublisherOptions, handlers: THandlers): PublishFn<InferSchemaFromHandlers<THandlers>>;
|
|
24
|
+
export declare function createBulkPublisher<TSchema extends Record<string, unknown>>(options: SyncPublisherOptions): BulkPublishFn<TSchema>;
|
|
25
|
+
export declare function createBulkPublisher<THandlers extends SyncHandler[]>(options: SyncPublisherOptions, handlers: THandlers): BulkPublishFn<InferSchemaFromHandlers<THandlers>>;
|
|
27
26
|
//# sourceMappingURL=handler.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"handler.d.ts","sourceRoot":"","sources":["
|
|
1
|
+
{"version":3,"file":"handler.d.ts","sourceRoot":"","sources":["handler.ts"],"names":[],"mappings":"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"}
|
package/dist/server/handler.js
CHANGED
|
@@ -1,113 +1,82 @@
|
|
|
1
|
-
export
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
return publishEvent(resolvedChannel, action, key, data);
|
|
5
|
-
};
|
|
1
|
+
export const INTERNAL_AUTH_HEADER = "x-sveltebase-sync-auth";
|
|
2
|
+
function getEnv(options) {
|
|
3
|
+
return options.platform?.env;
|
|
6
4
|
}
|
|
7
|
-
|
|
8
|
-
return
|
|
9
|
-
const resolvedChannel = String(channel);
|
|
10
|
-
return publishBulkEvent(resolvedChannel, changes);
|
|
11
|
-
};
|
|
5
|
+
function normalizeEndpoint(baseUrl, pathname) {
|
|
6
|
+
return new URL(pathname, baseUrl).toString();
|
|
12
7
|
}
|
|
13
|
-
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
const env = await import(/* @vite-ignore */ envId);
|
|
18
|
-
isDev = env.dev;
|
|
19
|
-
}
|
|
20
|
-
catch { }
|
|
21
|
-
if (!isDev) {
|
|
22
|
-
const globalObj = typeof globalThis !== "undefined" ? globalThis : {};
|
|
23
|
-
const processEnv = globalObj.process?.env;
|
|
24
|
-
if (processEnv?.NODE_ENV === "development" ||
|
|
25
|
-
processEnv?.NODE_ENV === "test" ||
|
|
26
|
-
globalObj.__sync_dev_broker__) {
|
|
27
|
-
isDev = true;
|
|
28
|
-
}
|
|
8
|
+
async function publishToDurableObject(platform, durableObjectBinding, pathname, body) {
|
|
9
|
+
const namespace = platform.env[durableObjectBinding];
|
|
10
|
+
if (!namespace) {
|
|
11
|
+
throw new Error(`Missing ${durableObjectBinding} Durable Object binding`);
|
|
29
12
|
}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
13
|
+
const id = namespace.idFromName("global");
|
|
14
|
+
const stub = namespace.get(id);
|
|
15
|
+
const response = await stub.fetch(`https://sync.internal${pathname}`, {
|
|
16
|
+
method: "POST",
|
|
17
|
+
headers: { "Content-Type": "application/json" },
|
|
18
|
+
body: JSON.stringify(body),
|
|
19
|
+
});
|
|
20
|
+
if (!response.ok) {
|
|
21
|
+
throw new Error(await response.text());
|
|
34
22
|
}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
await stub.fetch("https://realtime.internal/broadcast", {
|
|
45
|
-
method: "POST",
|
|
46
|
-
headers: { "Content-Type": "application/json" },
|
|
47
|
-
body: JSON.stringify({ channel, action, key, data }),
|
|
48
|
-
});
|
|
23
|
+
}
|
|
24
|
+
async function publishToServiceBinding(binding, pathname, body) {
|
|
25
|
+
const response = await binding.fetch(`https://sync.internal${pathname}`, {
|
|
26
|
+
method: "POST",
|
|
27
|
+
headers: { "Content-Type": "application/json" },
|
|
28
|
+
body: JSON.stringify(body),
|
|
29
|
+
});
|
|
30
|
+
if (!response.ok) {
|
|
31
|
+
throw new Error(await response.text());
|
|
49
32
|
}
|
|
50
|
-
|
|
51
|
-
|
|
33
|
+
}
|
|
34
|
+
async function publishToFallbackUrl(fallbackUrl, pathname, body) {
|
|
35
|
+
const response = await fetch(normalizeEndpoint(fallbackUrl, pathname), {
|
|
36
|
+
method: "POST",
|
|
37
|
+
headers: { "Content-Type": "application/json" },
|
|
38
|
+
body: JSON.stringify(body),
|
|
39
|
+
});
|
|
40
|
+
if (!response.ok) {
|
|
41
|
+
throw new Error(await response.text());
|
|
52
42
|
}
|
|
53
43
|
}
|
|
54
|
-
|
|
55
|
-
const
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
44
|
+
async function publish(options, pathname, body) {
|
|
45
|
+
const env = getEnv(options);
|
|
46
|
+
const durableObjectBinding = options.durableObjectBinding ?? "SYNC_ENGINE";
|
|
47
|
+
const bindingName = options.binding ?? "SYNC_WORKER";
|
|
48
|
+
if (options.platform && env?.[durableObjectBinding]) {
|
|
49
|
+
await publishToDurableObject(options.platform, durableObjectBinding, pathname, body);
|
|
50
|
+
return;
|
|
60
51
|
}
|
|
61
|
-
|
|
62
|
-
if (
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
if (processEnv?.NODE_ENV === "development" ||
|
|
66
|
-
processEnv?.NODE_ENV === "test" ||
|
|
67
|
-
globalObj.__sync_dev_broker__) {
|
|
68
|
-
isDev = true;
|
|
69
|
-
}
|
|
52
|
+
const serviceBinding = env?.[bindingName];
|
|
53
|
+
if (serviceBinding?.fetch) {
|
|
54
|
+
await publishToServiceBinding(serviceBinding, pathname, body);
|
|
55
|
+
return;
|
|
70
56
|
}
|
|
71
|
-
if (
|
|
72
|
-
|
|
73
|
-
await broadcastExternalBatchChange(channel, changes);
|
|
57
|
+
if (options.fallbackUrl) {
|
|
58
|
+
await publishToFallbackUrl(options.fallbackUrl, pathname, body);
|
|
74
59
|
return;
|
|
75
60
|
}
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
method: "POST",
|
|
87
|
-
headers: { "Content-Type": "application/json" },
|
|
88
|
-
body: JSON.stringify({ channel, changes }),
|
|
61
|
+
throw new Error(`Missing sync publisher target: provide platform.env.${durableObjectBinding}, platform.env.${bindingName}, or fallbackUrl`);
|
|
62
|
+
}
|
|
63
|
+
export function createPublisher(options, handlers) {
|
|
64
|
+
void handlers;
|
|
65
|
+
return async (channel, action, key, data) => {
|
|
66
|
+
await publish(options, "/broadcast", {
|
|
67
|
+
channel: String(channel),
|
|
68
|
+
action,
|
|
69
|
+
key,
|
|
70
|
+
data,
|
|
89
71
|
});
|
|
90
|
-
}
|
|
91
|
-
catch (err) {
|
|
92
|
-
console.error("Failed to publish bulk sync event to Durable Object:", err);
|
|
93
|
-
}
|
|
72
|
+
};
|
|
94
73
|
}
|
|
95
|
-
export
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
return new Response("SyncEngine binding is not available", { status: 500 });
|
|
102
|
-
}
|
|
103
|
-
try {
|
|
104
|
-
const id = namespace.idFromName("global");
|
|
105
|
-
const stub = namespace.get(id);
|
|
106
|
-
return await stub.fetch(new Request("https://realtime.internal/websocket", request));
|
|
107
|
-
}
|
|
108
|
-
catch (err) {
|
|
109
|
-
return new Response(err.message || "SyncEngine binding is not available", {
|
|
110
|
-
status: 503,
|
|
74
|
+
export function createBulkPublisher(options, handlers) {
|
|
75
|
+
void handlers;
|
|
76
|
+
return async (channel, changes) => {
|
|
77
|
+
await publish(options, "/broadcast-batch", {
|
|
78
|
+
channel: String(channel),
|
|
79
|
+
changes,
|
|
111
80
|
});
|
|
112
|
-
}
|
|
81
|
+
};
|
|
113
82
|
}
|