bunderstack 0.20.0 → 0.21.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/CHANGELOG.md +17 -1
- package/dist/api/crud-router.d.ts +4 -4
- package/dist/api/realtime-router.d.ts +1 -1
- package/dist/client/client.d.ts +53 -0
- package/dist/client/client.d.ts.map +1 -0
- package/dist/client/client.js +111 -0
- package/dist/client/client.js.map +1 -0
- package/dist/client/index.d.ts +4 -0
- package/dist/client/index.d.ts.map +1 -0
- package/dist/client/index.js +4 -0
- package/dist/client/index.js.map +1 -0
- package/dist/client/live-view.d.ts +49 -0
- package/dist/client/live-view.d.ts.map +1 -0
- package/dist/client/live-view.js +184 -0
- package/dist/client/live-view.js.map +1 -0
- package/dist/client/react.d.ts +4 -0
- package/dist/client/react.d.ts.map +1 -0
- package/dist/client/react.js +6 -0
- package/dist/client/react.js.map +1 -0
- package/dist/client/realtime.d.ts +54 -0
- package/dist/client/realtime.d.ts.map +1 -0
- package/dist/client/realtime.js +105 -0
- package/dist/client/realtime.js.map +1 -0
- package/dist/client/rest.d.ts +2 -0
- package/dist/client/rest.d.ts.map +1 -0
- package/dist/client/rest.js +2 -0
- package/dist/client/rest.js.map +1 -0
- package/dist/client/rpc-client.d.ts +38 -0
- package/dist/client/rpc-client.d.ts.map +1 -0
- package/dist/client/rpc-client.js +59 -0
- package/dist/client/rpc-client.js.map +1 -0
- package/dist/client/solid.d.ts +7 -0
- package/dist/client/solid.d.ts.map +1 -0
- package/dist/client/solid.js +21 -0
- package/dist/client/solid.js.map +1 -0
- package/dist/client/svelte.d.ts +6 -0
- package/dist/client/svelte.d.ts.map +1 -0
- package/dist/client/svelte.js +9 -0
- package/dist/client/svelte.js.map +1 -0
- package/dist/client/vue.d.ts +5 -0
- package/dist/client/vue.d.ts.map +1 -0
- package/dist/client/vue.js +11 -0
- package/dist/client/vue.js.map +1 -0
- package/dist/query/api.d.ts +10 -0
- package/dist/query/api.d.ts.map +1 -0
- package/dist/query/api.js +7 -0
- package/dist/query/api.js.map +1 -0
- package/dist/query/client.d.ts +33 -0
- package/dist/query/client.d.ts.map +1 -0
- package/dist/query/client.js +98 -0
- package/dist/query/client.js.map +1 -0
- package/dist/query/fetch.d.ts +3 -0
- package/dist/query/fetch.d.ts.map +1 -0
- package/dist/query/fetch.js +4 -0
- package/dist/query/fetch.js.map +1 -0
- package/dist/query/index.d.ts +8 -0
- package/dist/query/index.d.ts.map +1 -0
- package/dist/query/index.js +4 -0
- package/dist/query/index.js.map +1 -0
- package/dist/query/infer.d.ts +25 -0
- package/dist/query/infer.d.ts.map +1 -0
- package/dist/query/infer.js +2 -0
- package/dist/query/infer.js.map +1 -0
- package/dist/query/react.d.ts +3 -0
- package/dist/query/react.d.ts.map +1 -0
- package/dist/query/react.js +2 -0
- package/dist/query/react.js.map +1 -0
- package/dist/query/realtime-flush.d.ts +26 -0
- package/dist/query/realtime-flush.d.ts.map +1 -0
- package/dist/query/realtime-flush.js +45 -0
- package/dist/query/realtime-flush.js.map +1 -0
- package/dist/query/realtime-stream.d.ts +3 -0
- package/dist/query/realtime-stream.d.ts.map +1 -0
- package/dist/query/realtime-stream.js +3 -0
- package/dist/query/realtime-stream.js.map +1 -0
- package/dist/query/realtime.d.ts +88 -0
- package/dist/query/realtime.d.ts.map +1 -0
- package/dist/query/realtime.js +255 -0
- package/dist/query/realtime.js.map +1 -0
- package/dist/start/auth-client.d.ts +11 -0
- package/dist/start/auth-client.d.ts.map +1 -0
- package/dist/start/auth-client.js +15 -0
- package/dist/start/auth-client.js.map +1 -0
- package/dist/start/index.d.ts +66 -0
- package/dist/start/index.d.ts.map +1 -0
- package/dist/start/index.js +49 -0
- package/dist/start/index.js.map +1 -0
- package/dist/start/isomorphic-fetch.d.ts +13 -0
- package/dist/start/isomorphic-fetch.d.ts.map +1 -0
- package/dist/start/isomorphic-fetch.js +33 -0
- package/dist/start/isomorphic-fetch.js.map +1 -0
- package/dist/sync/collection.d.ts +106 -0
- package/dist/sync/collection.d.ts.map +1 -0
- package/dist/sync/collection.js +258 -0
- package/dist/sync/collection.js.map +1 -0
- package/dist/sync/index.d.ts +8 -0
- package/dist/sync/index.d.ts.map +1 -0
- package/dist/sync/index.js +4 -0
- package/dist/sync/index.js.map +1 -0
- package/dist/sync/realtime-sync.d.ts +31 -0
- package/dist/sync/realtime-sync.d.ts.map +1 -0
- package/dist/sync/realtime-sync.js +46 -0
- package/dist/sync/realtime-sync.js.map +1 -0
- package/dist/sync/sync-client.d.ts +28 -0
- package/dist/sync/sync-client.d.ts.map +1 -0
- package/dist/sync/sync-client.js +52 -0
- package/dist/sync/sync-client.js.map +1 -0
- package/dist/sync/update-queue.d.ts +27 -0
- package/dist/sync/update-queue.d.ts.map +1 -0
- package/dist/sync/update-queue.js +66 -0
- package/dist/sync/update-queue.js.map +1 -0
- package/package.json +103 -3
- package/skills/creating-bunderstack-apps/references/runtime-integrations.md +1 -1
- package/skills/migrating-to-bunderstack/references/runtime-replacements.md +2 -2
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"realtime.d.ts","sourceRoot":"","sources":["../../src/client/realtime.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,cAAc,GAAG,QAAQ,GAAG,QAAQ,GAAG,QAAQ,CAAA;AAC3D,MAAM,MAAM,cAAc,GAAG;IAC3B,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,cAAc,CAAA;IACtB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC/B,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB,CAAA;AACD,MAAM,MAAM,iBAAiB,GAAG;IAAE,IAAI,EAAE,WAAW,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,CAAA;AAC1E,MAAM,MAAM,aAAa,GAAG,cAAc,GAAG,iBAAiB,CAAA;AAC9D,MAAM,MAAM,iBAAiB,GAAG;IAC9B,IAAI,CACF,KAAK,EAAE;QAAE,MAAM,EAAE,MAAM,EAAE,CAAA;KAAE,EAC3B,OAAO,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,WAAW,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,GACvD,OAAO,CAAC,aAAa,CAAC,aAAa,CAAC,CAAC,CAAA;CACzC,CAAA;AACD,MAAM,MAAM,kBAAkB,GAAG;IAAE,KAAK,IAAI,IAAI,CAAC;IAAC,IAAI,EAAE,OAAO,CAAC,IAAI,CAAC,CAAA;CAAE,CAAA;AACvE,MAAM,MAAM,aAAa,GAAG;IAC1B,UAAU,CAAC,EAAE,EAAE,MAAM,IAAI,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAA;IAC/C,YAAY,CAAC,MAAM,EAAE,OAAO,GAAG,IAAI,CAAA;CACpC,CAAA;AACD,eAAO,MAAM,oBAAoB,OAAQ,CAAA;AACzC,eAAO,MAAM,uBAAuB,MAAM,CAAA;AAQ1C,MAAM,MAAM,qBAAqB,GAAG;IAClC,SAAS,EAAE,CAAC,OAAO,EAAE;QACnB,MAAM,EAAE,WAAW,CAAA;QACnB,WAAW,CAAC,EAAE,MAAM,CAAA;KACrB,KAAK,OAAO,CAAC,aAAa,CAAC,aAAa,CAAC,CAAC,CAAA;IAC3C,QAAQ,EAAE,CAAC,MAAM,EAAE,cAAc,KAAK,IAAI,CAAA;IAC1C,WAAW,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACvC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAA;IAClC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,KAAK,IAAI,CAAA;IAC/D,MAAM,EAAE,WAAW,CAAA;IACnB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,kBAAkB,CAAC,EAAE,MAAM,CAAA;IAC3B,KAAK,CAAC,EAAE,aAAa,CAAA;IACrB,MAAM,CAAC,EAAE,MAAM,MAAM,CAAA;CACtB,CAAA;AAED,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,aAAa,GACnB,KAAK,IAAI,iBAAiB,CAE5B;AAID,8EAA8E;AAC9E,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,qBAAqB,GAC7B,kBAAkB,CAqFpB"}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { getEventMeta } from '@standardserver/core';
|
|
2
|
+
export const DEFAULT_KEEPALIVE_MS = 5_000;
|
|
3
|
+
export const DEFAULT_LIVENESS_FACTOR = 2.5;
|
|
4
|
+
const systemClock = {
|
|
5
|
+
setTimeout: (fn, ms) => setTimeout(fn, ms),
|
|
6
|
+
clearTimeout: (handle) => clearTimeout(handle),
|
|
7
|
+
};
|
|
8
|
+
export function isRealtimeHeartbeat(event) {
|
|
9
|
+
return 'type' in event && event.type === 'heartbeat';
|
|
10
|
+
}
|
|
11
|
+
const LIVENESS_ABORT = 'bunderstack: realtime stream went silent';
|
|
12
|
+
/** Shared reconnecting transport used by LiveView, Query, and DB adapters. */
|
|
13
|
+
export function openRealtimeStream(options) {
|
|
14
|
+
const controller = new AbortController();
|
|
15
|
+
const signal = AbortSignal.any([controller.signal, options.signal]);
|
|
16
|
+
const clock = options.clock ?? systemClock;
|
|
17
|
+
const random = options.random ?? Math.random;
|
|
18
|
+
const retryMs = Math.max(0, options.retryMs ?? 1_000);
|
|
19
|
+
const maxRetryMs = Math.max(retryMs, options.maxRetryMs ?? 30_000);
|
|
20
|
+
const livenessFactor = options.livenessFactor ?? DEFAULT_LIVENESS_FACTOR;
|
|
21
|
+
let keepaliveMs = options.defaultKeepaliveMs ?? DEFAULT_KEEPALIVE_MS;
|
|
22
|
+
const wait = (ms) => new Promise((resolve) => {
|
|
23
|
+
if (signal.aborted)
|
|
24
|
+
return resolve();
|
|
25
|
+
const timer = clock.setTimeout(done, ms);
|
|
26
|
+
function done() {
|
|
27
|
+
clock.clearTimeout(timer);
|
|
28
|
+
signal.removeEventListener('abort', done);
|
|
29
|
+
resolve();
|
|
30
|
+
}
|
|
31
|
+
signal.addEventListener('abort', done, { once: true });
|
|
32
|
+
});
|
|
33
|
+
const done = (async () => {
|
|
34
|
+
let connected = false;
|
|
35
|
+
let lastEventId;
|
|
36
|
+
let retryAttempt = 0;
|
|
37
|
+
while (!signal.aborted) {
|
|
38
|
+
const attempt = new AbortController();
|
|
39
|
+
const attemptSignal = AbortSignal.any([signal, attempt.signal]);
|
|
40
|
+
let timer;
|
|
41
|
+
let wentSilent = false;
|
|
42
|
+
const disarm = () => {
|
|
43
|
+
if (timer === undefined)
|
|
44
|
+
return;
|
|
45
|
+
clock.clearTimeout(timer);
|
|
46
|
+
timer = undefined;
|
|
47
|
+
};
|
|
48
|
+
const arm = () => {
|
|
49
|
+
disarm();
|
|
50
|
+
timer = clock.setTimeout(() => {
|
|
51
|
+
wentSilent = true;
|
|
52
|
+
attempt.abort(new Error(LIVENESS_ABORT));
|
|
53
|
+
}, keepaliveMs * livenessFactor);
|
|
54
|
+
};
|
|
55
|
+
try {
|
|
56
|
+
const events = await options.subscribe({
|
|
57
|
+
signal: attemptSignal,
|
|
58
|
+
lastEventId,
|
|
59
|
+
});
|
|
60
|
+
arm();
|
|
61
|
+
if (connected)
|
|
62
|
+
await options.onReconnect();
|
|
63
|
+
connected = true;
|
|
64
|
+
for await (const event of events) {
|
|
65
|
+
retryAttempt = 0;
|
|
66
|
+
if (isRealtimeHeartbeat(event)) {
|
|
67
|
+
if (typeof event.intervalMs === 'number' && event.intervalMs > 0)
|
|
68
|
+
keepaliveMs = event.intervalMs;
|
|
69
|
+
arm();
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
arm();
|
|
73
|
+
const id = getEventMeta(event)?.id;
|
|
74
|
+
if (id)
|
|
75
|
+
lastEventId = id;
|
|
76
|
+
try {
|
|
77
|
+
options.onChange(event);
|
|
78
|
+
}
|
|
79
|
+
catch (error) {
|
|
80
|
+
options.onError?.(error);
|
|
81
|
+
}
|
|
82
|
+
if (signal.aborted)
|
|
83
|
+
break;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
catch (error) {
|
|
87
|
+
if (!signal.aborted && !wentSilent)
|
|
88
|
+
options.onError?.(error);
|
|
89
|
+
}
|
|
90
|
+
finally {
|
|
91
|
+
disarm();
|
|
92
|
+
}
|
|
93
|
+
if (!signal.aborted) {
|
|
94
|
+
const attemptNumber = retryAttempt + 1;
|
|
95
|
+
const ceiling = Math.min(maxRetryMs, retryMs * 2 ** retryAttempt);
|
|
96
|
+
const delayMs = Math.floor(random() * ceiling);
|
|
97
|
+
retryAttempt = attemptNumber;
|
|
98
|
+
options.onRetry?.({ attempt: attemptNumber, delayMs });
|
|
99
|
+
await wait(delayMs);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
})();
|
|
103
|
+
return { close: () => controller.abort(), done };
|
|
104
|
+
}
|
|
105
|
+
//# sourceMappingURL=realtime.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"realtime.js","sourceRoot":"","sources":["../../src/client/realtime.ts"],"names":[],"mappings":"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","sourcesContent":["import { getEventMeta } from '@standardserver/core'\n\nexport type RealtimeAction = 'create' | 'update' | 'delete'\nexport type RealtimeChange = {\n table: string\n action: RealtimeAction\n record: Record<string, unknown>\n operationId?: string\n}\nexport type RealtimeHeartbeat = { type: 'heartbeat'; intervalMs?: number }\nexport type RealtimeEvent = RealtimeChange | RealtimeHeartbeat\nexport type RealtimeProcedure = {\n call(\n input: { tables: string[] },\n options?: { signal?: AbortSignal; lastEventId?: string },\n ): Promise<AsyncIterable<RealtimeEvent>>\n}\nexport type RealtimeSyncHandle = { close(): void; done: Promise<void> }\nexport type RealtimeClock = {\n setTimeout(fn: () => void, ms: number): unknown\n clearTimeout(handle: unknown): void\n}\nexport const DEFAULT_KEEPALIVE_MS = 5_000\nexport const DEFAULT_LIVENESS_FACTOR = 2.5\n\nconst systemClock: RealtimeClock = {\n setTimeout: (fn, ms) => setTimeout(fn, ms),\n clearTimeout: (handle) =>\n clearTimeout(handle as ReturnType<typeof setTimeout>),\n}\n\nexport type RealtimeStreamOptions = {\n subscribe: (options: {\n signal: AbortSignal\n lastEventId?: string\n }) => Promise<AsyncIterable<RealtimeEvent>>\n onChange: (change: RealtimeChange) => void\n onReconnect: () => void | Promise<void>\n onError?: (error: unknown) => void\n onRetry?: (retry: { attempt: number; delayMs: number }) => void\n signal: AbortSignal\n retryMs?: number\n maxRetryMs?: number\n livenessFactor?: number\n defaultKeepaliveMs?: number\n clock?: RealtimeClock\n random?: () => number\n}\n\nexport function isRealtimeHeartbeat(\n event: RealtimeEvent,\n): event is RealtimeHeartbeat {\n return 'type' in event && event.type === 'heartbeat'\n}\n\nconst LIVENESS_ABORT = 'bunderstack: realtime stream went silent'\n\n/** Shared reconnecting transport used by LiveView, Query, and DB adapters. */\nexport function openRealtimeStream(\n options: RealtimeStreamOptions,\n): RealtimeSyncHandle {\n const controller = new AbortController()\n const signal = AbortSignal.any([controller.signal, options.signal])\n const clock = options.clock ?? systemClock\n const random = options.random ?? Math.random\n const retryMs = Math.max(0, options.retryMs ?? 1_000)\n const maxRetryMs = Math.max(retryMs, options.maxRetryMs ?? 30_000)\n const livenessFactor = options.livenessFactor ?? DEFAULT_LIVENESS_FACTOR\n let keepaliveMs = options.defaultKeepaliveMs ?? DEFAULT_KEEPALIVE_MS\n\n const wait = (ms: number) =>\n new Promise<void>((resolve) => {\n if (signal.aborted) return resolve()\n const timer = clock.setTimeout(done, ms)\n function done() {\n clock.clearTimeout(timer)\n signal.removeEventListener('abort', done)\n resolve()\n }\n signal.addEventListener('abort', done, { once: true })\n })\n\n const done = (async () => {\n let connected = false\n let lastEventId: string | undefined\n let retryAttempt = 0\n while (!signal.aborted) {\n const attempt = new AbortController()\n const attemptSignal = AbortSignal.any([signal, attempt.signal])\n let timer: unknown\n let wentSilent = false\n const disarm = () => {\n if (timer === undefined) return\n clock.clearTimeout(timer)\n timer = undefined\n }\n const arm = () => {\n disarm()\n timer = clock.setTimeout(() => {\n wentSilent = true\n attempt.abort(new Error(LIVENESS_ABORT))\n }, keepaliveMs * livenessFactor)\n }\n try {\n const events = await options.subscribe({\n signal: attemptSignal,\n lastEventId,\n })\n arm()\n if (connected) await options.onReconnect()\n connected = true\n for await (const event of events) {\n retryAttempt = 0\n if (isRealtimeHeartbeat(event)) {\n if (typeof event.intervalMs === 'number' && event.intervalMs > 0)\n keepaliveMs = event.intervalMs\n arm()\n continue\n }\n arm()\n const id = getEventMeta(event)?.id\n if (id) lastEventId = id\n try {\n options.onChange(event)\n } catch (error) {\n options.onError?.(error)\n }\n if (signal.aborted) break\n }\n } catch (error) {\n if (!signal.aborted && !wentSilent) options.onError?.(error)\n } finally {\n disarm()\n }\n if (!signal.aborted) {\n const attemptNumber = retryAttempt + 1\n const ceiling = Math.min(maxRetryMs, retryMs * 2 ** retryAttempt)\n const delayMs = Math.floor(random() * ceiling)\n retryAttempt = attemptNumber\n options.onRetry?.({ attempt: attemptNumber, delayMs })\n await wait(delayMs)\n }\n }\n })()\n return { close: () => controller.abort(), done }\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rest.d.ts","sourceRoot":"","sources":["../../src/client/rest.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,gBAAgB,EAChB,KAAK,QAAQ,EACb,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,EACrB,KAAK,WAAW,EAChB,KAAK,cAAc,GACpB,MAAM,UAAU,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rest.js","sourceRoot":"","sources":["../../src/client/rest.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,gBAAgB,GAMjB,MAAM,UAAU,CAAA","sourcesContent":["export {\n createRestClient,\n type ClientOf,\n type RestClientOptions,\n type RouteCallOptions,\n type RouteMethod,\n type RouteOperation,\n} from './client'\n"]}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { AnyRouter, RouterClient } from '@orpc/server';
|
|
2
|
+
export declare const OPERATION_ID_HEADER = "x-bunderstack-operation-id";
|
|
3
|
+
export type Fetch = (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
|
|
4
|
+
export type ClientOptions = {
|
|
5
|
+
baseUrl?: string;
|
|
6
|
+
fetch?: Fetch;
|
|
7
|
+
headers?: HeadersInit | (() => HeadersInit | Promise<HeadersInit>);
|
|
8
|
+
};
|
|
9
|
+
export type CallOptions = {
|
|
10
|
+
signal?: AbortSignal;
|
|
11
|
+
lastEventId?: string;
|
|
12
|
+
operationId?: string;
|
|
13
|
+
headers?: HeadersInit;
|
|
14
|
+
};
|
|
15
|
+
export type ClientCarrier = {
|
|
16
|
+
api: AnyRouter;
|
|
17
|
+
buckets: string;
|
|
18
|
+
};
|
|
19
|
+
export type AnyBunderstackApp = {
|
|
20
|
+
$inferClient?: ClientCarrier | undefined;
|
|
21
|
+
};
|
|
22
|
+
export type InferApiRouter<TApp extends AnyBunderstackApp> = NonNullable<TApp['$inferClient']>['api'];
|
|
23
|
+
type RpcContext = {
|
|
24
|
+
operationId?: string;
|
|
25
|
+
headers?: HeadersInit;
|
|
26
|
+
};
|
|
27
|
+
type AdaptCallArgs<TArgs extends unknown[]> = {
|
|
28
|
+
[K in keyof TArgs]: K extends 1 | '1' ? CallOptions : TArgs[K];
|
|
29
|
+
};
|
|
30
|
+
export type DirectClient<T> = T extends (...args: infer TArgs) => infer TResult ? (...args: AdaptCallArgs<TArgs>) => TResult : {
|
|
31
|
+
[K in keyof T]: DirectClient<T[K]>;
|
|
32
|
+
};
|
|
33
|
+
export type RpcClient<TRouter extends AnyRouter> = DirectClient<RouterClient<TRouter, RpcContext>>;
|
|
34
|
+
export type BunderstackClient<TApp extends AnyBunderstackApp> = RpcClient<InferApiRouter<TApp>>;
|
|
35
|
+
export declare function createRpcClient<TRouter extends AnyRouter>(options?: ClientOptions): RpcClient<TRouter>;
|
|
36
|
+
export declare function createClient<TApp extends AnyBunderstackApp>(options?: ClientOptions): BunderstackClient<TApp>;
|
|
37
|
+
export {};
|
|
38
|
+
//# sourceMappingURL=rpc-client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rpc-client.d.ts","sourceRoot":"","sources":["../../src/client/rpc-client.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AAK3D,eAAO,MAAM,mBAAmB,+BAA+B,CAAA;AAE/D,MAAM,MAAM,KAAK,GAAG,CAClB,KAAK,EAAE,WAAW,GAAG,GAAG,EACxB,IAAI,CAAC,EAAE,WAAW,KACf,OAAO,CAAC,QAAQ,CAAC,CAAA;AAEtB,MAAM,MAAM,aAAa,GAAG;IAC1B,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,KAAK,CAAC,EAAE,KAAK,CAAA;IACb,OAAO,CAAC,EAAE,WAAW,GAAG,CAAC,MAAM,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC,CAAA;CACnE,CAAA;AAED,MAAM,MAAM,WAAW,GAAG;IACxB,MAAM,CAAC,EAAE,WAAW,CAAA;IACpB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,OAAO,CAAC,EAAE,WAAW,CAAA;CACtB,CAAA;AAED,MAAM,MAAM,aAAa,GAAG;IAC1B,GAAG,EAAE,SAAS,CAAA;IACd,OAAO,EAAE,MAAM,CAAA;CAChB,CAAA;AAED,MAAM,MAAM,iBAAiB,GAAG;IAC9B,YAAY,CAAC,EAAE,aAAa,GAAG,SAAS,CAAA;CACzC,CAAA;AAED,MAAM,MAAM,cAAc,CAAC,IAAI,SAAS,iBAAiB,IAAI,WAAW,CACtE,IAAI,CAAC,cAAc,CAAC,CACrB,CAAC,KAAK,CAAC,CAAA;AAER,KAAK,UAAU,GAAG;IAChB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,OAAO,CAAC,EAAE,WAAW,CAAA;CACtB,CAAA;AAED,KAAK,aAAa,CAAC,KAAK,SAAS,OAAO,EAAE,IAAI;KAC3C,CAAC,IAAI,MAAM,KAAK,GAAG,CAAC,SAAS,CAAC,GAAG,GAAG,GAAG,WAAW,GAAG,KAAK,CAAC,CAAC,CAAC;CAC/D,CAAA;AAED,MAAM,MAAM,YAAY,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,IAAI,EAAE,MAAM,KAAK,KAAK,MAAM,OAAO,GAC3E,CAAC,GAAG,IAAI,EAAE,aAAa,CAAC,KAAK,CAAC,KAAK,OAAO,GAC1C;KAAG,CAAC,IAAI,MAAM,CAAC,GAAG,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;CAAE,CAAA;AAE1C,MAAM,MAAM,SAAS,CAAC,OAAO,SAAS,SAAS,IAAI,YAAY,CAC7D,YAAY,CAAC,OAAO,EAAE,UAAU,CAAC,CAClC,CAAA;AAED,MAAM,MAAM,iBAAiB,CAAC,IAAI,SAAS,iBAAiB,IAAI,SAAS,CACvE,cAAc,CAAC,IAAI,CAAC,CACrB,CAAA;AA6CD,wBAAgB,eAAe,CAAC,OAAO,SAAS,SAAS,EACvD,OAAO,GAAE,aAAkB,GAC1B,SAAS,CAAC,OAAO,CAAC,CAuBpB;AAED,wBAAgB,YAAY,CAAC,IAAI,SAAS,iBAAiB,EACzD,OAAO,GAAE,aAAkB,GAC1B,iBAAiB,CAAC,IAAI,CAAC,CAEzB"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { createORPCClient } from '@orpc/client';
|
|
2
|
+
import { RPCLink } from '@orpc/client/fetch';
|
|
3
|
+
export const OPERATION_ID_HEADER = 'x-bunderstack-operation-id';
|
|
4
|
+
function rpcUrl(baseUrl = '/api') {
|
|
5
|
+
return (baseUrl.endsWith('/') ? `${baseUrl}rpc` : `${baseUrl}/rpc`);
|
|
6
|
+
}
|
|
7
|
+
function adaptClient(client) {
|
|
8
|
+
const cache = new Map();
|
|
9
|
+
return new Proxy(function () { }, {
|
|
10
|
+
get(_target, property) {
|
|
11
|
+
if (!cache.has(property)) {
|
|
12
|
+
const value = Reflect.get(client, property);
|
|
13
|
+
cache.set(property, value === null ||
|
|
14
|
+
(typeof value !== 'object' && typeof value !== 'function')
|
|
15
|
+
? value
|
|
16
|
+
: adaptClient(value));
|
|
17
|
+
}
|
|
18
|
+
return cache.get(property);
|
|
19
|
+
},
|
|
20
|
+
apply(_target, _thisArg, args) {
|
|
21
|
+
const [input, options = {}] = args;
|
|
22
|
+
return Reflect.apply(client, undefined, [
|
|
23
|
+
input,
|
|
24
|
+
{
|
|
25
|
+
signal: options.signal,
|
|
26
|
+
lastEventId: options.lastEventId,
|
|
27
|
+
context: {
|
|
28
|
+
operationId: options.operationId,
|
|
29
|
+
headers: options.headers,
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
]);
|
|
33
|
+
},
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
export function createRpcClient(options = {}) {
|
|
37
|
+
const fetcher = options.fetch ?? ((input, init) => globalThis.fetch(input, init));
|
|
38
|
+
const link = new RPCLink({
|
|
39
|
+
url: rpcUrl(options.baseUrl),
|
|
40
|
+
fetch: (url, init) => fetcher(url, init),
|
|
41
|
+
headers: async ({ context }) => {
|
|
42
|
+
const defaults = typeof options.headers === 'function'
|
|
43
|
+
? await options.headers()
|
|
44
|
+
: options.headers;
|
|
45
|
+
const headers = new Headers(defaults);
|
|
46
|
+
new Headers(context.headers).forEach((value, key) => headers.set(key, value));
|
|
47
|
+
if (context.operationId) {
|
|
48
|
+
headers.set(OPERATION_ID_HEADER, context.operationId);
|
|
49
|
+
}
|
|
50
|
+
return headers;
|
|
51
|
+
},
|
|
52
|
+
});
|
|
53
|
+
const client = createORPCClient(link);
|
|
54
|
+
return adaptClient(client);
|
|
55
|
+
}
|
|
56
|
+
export function createClient(options = {}) {
|
|
57
|
+
return createRpcClient(options);
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=rpc-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rpc-client.js","sourceRoot":"","sources":["../../src/client/rpc-client.ts"],"names":[],"mappings":"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","sourcesContent":["import type { StandardUrl } from '@orpc/client/standard'\nimport type { AnyRouter, RouterClient } from '@orpc/server'\n\nimport { createORPCClient } from '@orpc/client'\nimport { RPCLink } from '@orpc/client/fetch'\n\nexport const OPERATION_ID_HEADER = 'x-bunderstack-operation-id'\n\nexport type Fetch = (\n input: RequestInfo | URL,\n init?: RequestInit,\n) => Promise<Response>\n\nexport type ClientOptions = {\n baseUrl?: string\n fetch?: Fetch\n headers?: HeadersInit | (() => HeadersInit | Promise<HeadersInit>)\n}\n\nexport type CallOptions = {\n signal?: AbortSignal\n lastEventId?: string\n operationId?: string\n headers?: HeadersInit\n}\n\nexport type ClientCarrier = {\n api: AnyRouter\n buckets: string\n}\n\nexport type AnyBunderstackApp = {\n $inferClient?: ClientCarrier | undefined\n}\n\nexport type InferApiRouter<TApp extends AnyBunderstackApp> = NonNullable<\n TApp['$inferClient']\n>['api']\n\ntype RpcContext = {\n operationId?: string\n headers?: HeadersInit\n}\n\ntype AdaptCallArgs<TArgs extends unknown[]> = {\n [K in keyof TArgs]: K extends 1 | '1' ? CallOptions : TArgs[K]\n}\n\nexport type DirectClient<T> = T extends (...args: infer TArgs) => infer TResult\n ? (...args: AdaptCallArgs<TArgs>) => TResult\n : { [K in keyof T]: DirectClient<T[K]> }\n\nexport type RpcClient<TRouter extends AnyRouter> = DirectClient<\n RouterClient<TRouter, RpcContext>\n>\n\nexport type BunderstackClient<TApp extends AnyBunderstackApp> = RpcClient<\n InferApiRouter<TApp>\n>\n\nfunction rpcUrl(baseUrl = '/api'): StandardUrl {\n return (\n baseUrl.endsWith('/') ? `${baseUrl}rpc` : `${baseUrl}/rpc`\n ) as StandardUrl\n}\n\nfunction adaptClient<T>(client: T): DirectClient<T> {\n const cache = new Map<PropertyKey, unknown>()\n return new Proxy(function () {} as (...args: unknown[]) => unknown, {\n get(_target, property) {\n if (!cache.has(property)) {\n const value = Reflect.get(client as object, property)\n cache.set(\n property,\n value === null ||\n (typeof value !== 'object' && typeof value !== 'function')\n ? value\n : adaptClient(value),\n )\n }\n return cache.get(property)\n },\n apply(_target, _thisArg, args: [unknown, CallOptions?]) {\n const [input, options = {}] = args\n return Reflect.apply(\n client as (...args: unknown[]) => unknown,\n undefined,\n [\n input,\n {\n signal: options.signal,\n lastEventId: options.lastEventId,\n context: {\n operationId: options.operationId,\n headers: options.headers,\n },\n },\n ],\n )\n },\n }) as unknown as DirectClient<T>\n}\n\nexport function createRpcClient<TRouter extends AnyRouter>(\n options: ClientOptions = {},\n): RpcClient<TRouter> {\n const fetcher =\n options.fetch ?? ((input, init) => globalThis.fetch(input, init))\n const link = new RPCLink<RpcContext>({\n url: rpcUrl(options.baseUrl),\n fetch: (url, init) => fetcher(url, init),\n headers: async ({ context }) => {\n const defaults =\n typeof options.headers === 'function'\n ? await options.headers()\n : options.headers\n const headers = new Headers(defaults)\n new Headers(context.headers).forEach((value, key) =>\n headers.set(key, value),\n )\n if (context.operationId) {\n headers.set(OPERATION_ID_HEADER, context.operationId)\n }\n return headers\n },\n })\n const client = createORPCClient<RouterClient<TRouter, RpcContext>>(link)\n return adaptClient(client)\n}\n\nexport function createClient<TApp extends AnyBunderstackApp>(\n options: ClientOptions = {},\n): BunderstackClient<TApp> {\n return createRpcClient<InferApiRouter<TApp>>(options)\n}\n"]}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type Store } from 'solid-js';
|
|
2
|
+
import type { LiveView, LiveViewSnapshot } from './live-view.js';
|
|
3
|
+
/** Mirrors confirmed LiveView snapshots into a keyed Solid store. */
|
|
4
|
+
export declare function createLiveStore<T extends {
|
|
5
|
+
id: unknown;
|
|
6
|
+
}>(view: LiveView<T>): Store<LiveViewSnapshot<T>>;
|
|
7
|
+
//# sourceMappingURL=solid.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"solid.d.ts","sourceRoot":"","sources":["../../src/client/solid.ts"],"names":[],"mappings":"AAAA,OAAO,EAAqC,KAAK,KAAK,EAAE,MAAM,UAAU,CAAA;AAExE,OAAO,KAAK,EAAE,QAAQ,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA;AAE7D,qEAAqE;AACrE,wBAAgB,eAAe,CAAC,CAAC,SAAS;IAAE,EAAE,EAAE,OAAO,CAAA;CAAE,EACvD,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC,GAChB,KAAK,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAiB5B"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { createStore, onCleanup, reconcile } from 'solid-js';
|
|
2
|
+
/** Mirrors confirmed LiveView snapshots into a keyed Solid store. */
|
|
3
|
+
export function createLiveStore(view) {
|
|
4
|
+
const initial = view.getSnapshot();
|
|
5
|
+
const [state, setState] = createStore({
|
|
6
|
+
items: [...initial.items],
|
|
7
|
+
status: initial.status,
|
|
8
|
+
error: initial.error,
|
|
9
|
+
});
|
|
10
|
+
const unsubscribe = view.subscribe(() => {
|
|
11
|
+
const next = view.getSnapshot();
|
|
12
|
+
setState((draft) => {
|
|
13
|
+
reconcile([...next.items], (item) => item.id)(draft.items);
|
|
14
|
+
draft.status = next.status;
|
|
15
|
+
draft.error = next.error;
|
|
16
|
+
});
|
|
17
|
+
});
|
|
18
|
+
onCleanup(unsubscribe);
|
|
19
|
+
return state;
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=solid.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"solid.js","sourceRoot":"","sources":["../../src/client/solid.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,SAAS,EAAc,MAAM,UAAU,CAAA;AAIxE,qEAAqE;AACrE,MAAM,UAAU,eAAe,CAC7B,IAAiB;IAEjB,MAAM,OAAO,GAAG,IAAI,CAAC,WAAW,EAAE,CAAA;IAClC,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,WAAW,CAAC;QACpC,KAAK,EAAE,CAAC,GAAG,OAAO,CAAC,KAAK,CAAC;QACzB,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,KAAK,EAAE,OAAO,CAAC,KAAK;KACrB,CAAC,CAAA;IACF,MAAM,WAAW,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE;QACtC,MAAM,IAAI,GAAG,IAAI,CAAC,WAAW,EAAE,CAAA;QAC/B,QAAQ,CAAC,CAAC,KAAK,EAAE,EAAE;YACjB,SAAS,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,IAAO,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAA;YAC7D,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAA;YAC1B,KAAK,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAA;QAC1B,CAAC,CAAC,CAAA;IACJ,CAAC,CAAC,CAAA;IACF,SAAS,CAAC,WAAW,CAAC,CAAA;IACtB,OAAO,KAAmC,CAAA;AAC5C,CAAC","sourcesContent":["import { createStore, onCleanup, reconcile, type Store } from 'solid-js'\n\nimport type { LiveView, LiveViewSnapshot } from './live-view'\n\n/** Mirrors confirmed LiveView snapshots into a keyed Solid store. */\nexport function createLiveStore<T extends { id: unknown }>(\n view: LiveView<T>,\n): Store<LiveViewSnapshot<T>> {\n const initial = view.getSnapshot()\n const [state, setState] = createStore({\n items: [...initial.items],\n status: initial.status,\n error: initial.error,\n })\n const unsubscribe = view.subscribe(() => {\n const next = view.getSnapshot()\n setState((draft) => {\n reconcile([...next.items], (item: T) => item.id)(draft.items)\n draft.status = next.status\n draft.error = next.error\n })\n })\n onCleanup(unsubscribe)\n return state as Store<LiveViewSnapshot<T>>\n}\n"]}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { LiveView, LiveViewSnapshot } from './live-view.js';
|
|
2
|
+
export type Readable<T> = {
|
|
3
|
+
subscribe(run: (value: T) => void): () => void;
|
|
4
|
+
};
|
|
5
|
+
export declare function liveStore<T>(view: LiveView<T>): Readable<LiveViewSnapshot<T>>;
|
|
6
|
+
//# sourceMappingURL=svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"svelte.d.ts","sourceRoot":"","sources":["../../src/client/svelte.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA;AAE7D,MAAM,MAAM,QAAQ,CAAC,CAAC,IAAI;IACxB,SAAS,CAAC,GAAG,EAAE,CAAC,KAAK,EAAE,CAAC,KAAK,IAAI,GAAG,MAAM,IAAI,CAAA;CAC/C,CAAA;AAED,wBAAgB,SAAS,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC,GAAG,QAAQ,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,CAO7E"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"svelte.js","sourceRoot":"","sources":["../../src/client/svelte.ts"],"names":[],"mappings":"AAMA,MAAM,UAAU,SAAS,CAAI,IAAiB;IAC5C,OAAO;QACL,SAAS,CAAC,GAAG;YACX,GAAG,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,CAAA;YACvB,OAAO,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,CAAC,CAAA;QACtD,CAAC;KACF,CAAA;AACH,CAAC","sourcesContent":["import type { LiveView, LiveViewSnapshot } from './live-view'\n\nexport type Readable<T> = {\n subscribe(run: (value: T) => void): () => void\n}\n\nexport function liveStore<T>(view: LiveView<T>): Readable<LiveViewSnapshot<T>> {\n return {\n subscribe(run) {\n run(view.getSnapshot())\n return view.subscribe(() => run(view.getSnapshot()))\n },\n }\n}\n"]}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { type ShallowRef } from 'vue';
|
|
2
|
+
import type { LiveView, LiveViewSnapshot } from './live-view.js';
|
|
3
|
+
/** Vue owns reactivity; LiveView supplies immutable external snapshots. */
|
|
4
|
+
export declare function useLiveView<T>(view: LiveView<T>): ShallowRef<LiveViewSnapshot<T>>;
|
|
5
|
+
//# sourceMappingURL=vue.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vue.d.ts","sourceRoot":"","sources":["../../src/client/vue.ts"],"names":[],"mappings":"AAAA,OAAO,EAA8B,KAAK,UAAU,EAAE,MAAM,KAAK,CAAA;AAEjE,OAAO,KAAK,EAAE,QAAQ,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA;AAE7D,2EAA2E;AAC3E,wBAAgB,WAAW,CAAC,CAAC,EAC3B,IAAI,EAAE,QAAQ,CAAC,CAAC,CAAC,GAChB,UAAU,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,CASjC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { onScopeDispose, shallowRef } from 'vue';
|
|
2
|
+
/** Vue owns reactivity; LiveView supplies immutable external snapshots. */
|
|
3
|
+
export function useLiveView(view) {
|
|
4
|
+
const state = shallowRef(view.getSnapshot());
|
|
5
|
+
const unsubscribe = view.subscribe(() => {
|
|
6
|
+
state.value = view.getSnapshot();
|
|
7
|
+
});
|
|
8
|
+
onScopeDispose(unsubscribe);
|
|
9
|
+
return state;
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=vue.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vue.js","sourceRoot":"","sources":["../../src/client/vue.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,UAAU,EAAmB,MAAM,KAAK,CAAA;AAIjE,2EAA2E;AAC3E,MAAM,UAAU,WAAW,CACzB,IAAiB;IAEjB,MAAM,KAAK,GAAG,UAAU,CAAC,IAAI,CAAC,WAAW,EAAE,CAE1C,CAAA;IACD,MAAM,WAAW,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,EAAE;QACtC,KAAK,CAAC,KAAK,GAAG,IAAI,CAAC,WAAW,EAAE,CAAA;IAClC,CAAC,CAAC,CAAA;IACF,cAAc,CAAC,WAAW,CAAC,CAAA;IAC3B,OAAO,KAAK,CAAA;AACd,CAAC","sourcesContent":["import { onScopeDispose, shallowRef, type ShallowRef } from 'vue'\n\nimport type { LiveView, LiveViewSnapshot } from './live-view'\n\n/** Vue owns reactivity; LiveView supplies immutable external snapshots. */\nexport function useLiveView<T>(\n view: LiveView<T>,\n): ShallowRef<LiveViewSnapshot<T>> {\n const state = shallowRef(view.getSnapshot()) as ShallowRef<\n LiveViewSnapshot<T>\n >\n const unsubscribe = view.subscribe(() => {\n state.value = view.getSnapshot()\n })\n onScopeDispose(unsubscribe)\n return state\n}\n"]}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { AnyRouter, RouterClient } from '@orpc/server';
|
|
2
|
+
import { type RouterUtils } from '@orpc/tanstack-query';
|
|
3
|
+
import { type Fetch } from '../client/rpc-client.js';
|
|
4
|
+
export interface ApiClientOptions {
|
|
5
|
+
baseUrl?: string;
|
|
6
|
+
fetch?: Fetch;
|
|
7
|
+
}
|
|
8
|
+
export type ApiQueryUtils<TRouter extends AnyRouter> = RouterUtils<RouterClient<TRouter>>;
|
|
9
|
+
export declare function createApiClient<TRouter extends AnyRouter = AnyRouter>(options?: ApiClientOptions): ApiQueryUtils<TRouter>;
|
|
10
|
+
//# sourceMappingURL=api.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../src/query/api.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AAE3D,OAAO,EAEL,KAAK,WAAW,EACjB,MAAM,sBAAsB,CAAA;AAE7B,OAAO,EAAmB,KAAK,KAAK,EAAE,MAAM,sBAAsB,CAAA;AAElE,MAAM,WAAW,gBAAgB;IAC/B,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,KAAK,CAAC,EAAE,KAAK,CAAA;CACd;AAED,MAAM,MAAM,aAAa,CAAC,OAAO,SAAS,SAAS,IAAI,WAAW,CAChE,YAAY,CAAC,OAAO,CAAC,CACtB,CAAA;AAED,wBAAgB,eAAe,CAAC,OAAO,SAAS,SAAS,GAAG,SAAS,EACnE,OAAO,GAAE,gBAAqB,GAC7B,aAAa,CAAC,OAAO,CAAC,CAGxB"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { createTanstackQueryUtils, } from '@orpc/tanstack-query';
|
|
2
|
+
import { createRpcClient } from '../client/rpc-client.js';
|
|
3
|
+
export function createApiClient(options = {}) {
|
|
4
|
+
const client = createRpcClient(options);
|
|
5
|
+
return createTanstackQueryUtils(client);
|
|
6
|
+
}
|
|
7
|
+
//# sourceMappingURL=api.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api.js","sourceRoot":"","sources":["../../src/query/api.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,wBAAwB,GAEzB,MAAM,sBAAsB,CAAA;AAE7B,OAAO,EAAE,eAAe,EAAc,MAAM,sBAAsB,CAAA;AAWlE,MAAM,UAAU,eAAe,CAC7B,UAA4B,EAAE;IAE9B,MAAM,MAAM,GAAG,eAAe,CAAU,OAAO,CAAC,CAAA;IAChD,OAAO,wBAAwB,CAAC,MAA0C,CAAC,CAAA;AAC7E,CAAC","sourcesContent":["import type { AnyRouter, RouterClient } from '@orpc/server'\n\nimport {\n createTanstackQueryUtils,\n type RouterUtils,\n} from '@orpc/tanstack-query'\n\nimport { createRpcClient, type Fetch } from '../client/rpc-client'\n\nexport interface ApiClientOptions {\n baseUrl?: string\n fetch?: Fetch\n}\n\nexport type ApiQueryUtils<TRouter extends AnyRouter> = RouterUtils<\n RouterClient<TRouter>\n>\n\nexport function createApiClient<TRouter extends AnyRouter = AnyRouter>(\n options: ApiClientOptions = {},\n): ApiQueryUtils<TRouter> {\n const client = createRpcClient<TRouter>(options)\n return createTanstackQueryUtils(client as unknown as RouterClient<TRouter>)\n}\n"]}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { QueryClient } from '@tanstack/query-core';
|
|
2
|
+
import type { ApiQueryUtils } from './api.js';
|
|
3
|
+
import type { AnyBunderstackApp, InferApiRouter, InferBuckets } from './infer.js';
|
|
4
|
+
import { type TransportFetch } from './fetch.js';
|
|
5
|
+
export type ClientOptions = {
|
|
6
|
+
baseUrl?: string;
|
|
7
|
+
fetch?: TransportFetch;
|
|
8
|
+
queryClient?: QueryClient;
|
|
9
|
+
};
|
|
10
|
+
export type FileTransformOptions = {
|
|
11
|
+
w?: number;
|
|
12
|
+
h?: number;
|
|
13
|
+
format?: 'webp' | 'jpeg' | 'png' | 'avif';
|
|
14
|
+
};
|
|
15
|
+
export type UploadedFile = {
|
|
16
|
+
fileId: string;
|
|
17
|
+
url: string;
|
|
18
|
+
name: string;
|
|
19
|
+
};
|
|
20
|
+
export type FileBucketHelpers = {
|
|
21
|
+
url(idOrFileId: string, transforms?: FileTransformOptions): string;
|
|
22
|
+
upload(file: File): Promise<UploadedFile>;
|
|
23
|
+
delete(idOrFileId: string): Promise<void>;
|
|
24
|
+
};
|
|
25
|
+
type FileHelpers<TBuckets extends string> = {
|
|
26
|
+
files: {
|
|
27
|
+
[K in TBuckets]: FileBucketHelpers;
|
|
28
|
+
};
|
|
29
|
+
};
|
|
30
|
+
export type BunderstackClient<TApp extends AnyBunderstackApp> = ApiQueryUtils<InferApiRouter<TApp>> & FileHelpers<InferBuckets<TApp>>;
|
|
31
|
+
export declare function createClient<TApp extends AnyBunderstackApp>(options?: ClientOptions): BunderstackClient<TApp>;
|
|
32
|
+
export {};
|
|
33
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/query/client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAA;AAEvD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,OAAO,CAAA;AAC1C,OAAO,KAAK,EAAE,iBAAiB,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AAG9E,OAAO,EAAe,KAAK,cAAc,EAAE,MAAM,SAAS,CAAA;AAE1D,MAAM,MAAM,aAAa,GAAG;IAC1B,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,KAAK,CAAC,EAAE,cAAc,CAAA;IACtB,WAAW,CAAC,EAAE,WAAW,CAAA;CAC1B,CAAA;AAED,MAAM,MAAM,oBAAoB,GAAG;IACjC,CAAC,CAAC,EAAE,MAAM,CAAA;IACV,CAAC,CAAC,EAAE,MAAM,CAAA;IACV,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,KAAK,GAAG,MAAM,CAAA;CAC1C,CAAA;AAED,MAAM,MAAM,YAAY,GAAG;IACzB,MAAM,EAAE,MAAM,CAAA;IACd,GAAG,EAAE,MAAM,CAAA;IACX,IAAI,EAAE,MAAM,CAAA;CACb,CAAA;AAED,MAAM,MAAM,iBAAiB,GAAG;IAC9B,GAAG,CAAC,UAAU,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,oBAAoB,GAAG,MAAM,CAAA;IAClE,MAAM,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,YAAY,CAAC,CAAA;IACzC,MAAM,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;CAC1C,CAAA;AAED,KAAK,WAAW,CAAC,QAAQ,SAAS,MAAM,IAAI;IAC1C,KAAK,EAAE;SAAG,CAAC,IAAI,QAAQ,GAAG,iBAAiB;KAAE,CAAA;CAC9C,CAAA;AAED,MAAM,MAAM,iBAAiB,CAAC,IAAI,SAAS,iBAAiB,IAAI,aAAa,CAC3E,cAAc,CAAC,IAAI,CAAC,CACrB,GACC,WAAW,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,CAAA;AAiHjC,wBAAgB,YAAY,CAAC,IAAI,SAAS,iBAAiB,EACzD,OAAO,GAAE,aAAkB,GAC1B,iBAAiB,CAAC,IAAI,CAAC,CAKzB"}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { createApiClient } from './api.js';
|
|
2
|
+
import { createFetch } from './fetch.js';
|
|
3
|
+
function trimSlash(value) {
|
|
4
|
+
return value.replace(/\/$/, '');
|
|
5
|
+
}
|
|
6
|
+
function relativeId(bucket, idOrFileId) {
|
|
7
|
+
const prefix = `${bucket}/`;
|
|
8
|
+
return idOrFileId.startsWith(prefix)
|
|
9
|
+
? idOrFileId.slice(prefix.length)
|
|
10
|
+
: idOrFileId;
|
|
11
|
+
}
|
|
12
|
+
function encodeFilePath(value) {
|
|
13
|
+
return value
|
|
14
|
+
.split('/')
|
|
15
|
+
.map((segment) => encodeURIComponent(decodeURIComponent(segment)).replace(/\./g, '%2E'))
|
|
16
|
+
.join('/');
|
|
17
|
+
}
|
|
18
|
+
function attachFileHelpers(utils, options) {
|
|
19
|
+
const baseUrl = trimSlash(options.baseUrl ?? '/api');
|
|
20
|
+
const fetch = createFetch(options.fetch);
|
|
21
|
+
const files = utils['files'];
|
|
22
|
+
if (!files)
|
|
23
|
+
return utils;
|
|
24
|
+
const buckets = new Map();
|
|
25
|
+
const filesProxy = new Proxy(files, {
|
|
26
|
+
get(target, property, receiver) {
|
|
27
|
+
if (typeof property !== 'string')
|
|
28
|
+
return Reflect.get(target, property, receiver);
|
|
29
|
+
const cached = buckets.get(property);
|
|
30
|
+
if (cached)
|
|
31
|
+
return cached;
|
|
32
|
+
const procedures = Reflect.get(target, property, receiver);
|
|
33
|
+
const root = `${baseUrl}/files/${property}`;
|
|
34
|
+
const url = (idOrFileId, transforms = {}) => {
|
|
35
|
+
const path = encodeFilePath(relativeId(property, idOrFileId));
|
|
36
|
+
const params = new URLSearchParams();
|
|
37
|
+
if (transforms.w !== undefined)
|
|
38
|
+
params.set('w', String(transforms.w));
|
|
39
|
+
if (transforms.h !== undefined)
|
|
40
|
+
params.set('h', String(transforms.h));
|
|
41
|
+
if (transforms.format)
|
|
42
|
+
params.set('format', transforms.format);
|
|
43
|
+
const query = params.toString();
|
|
44
|
+
return `${root}/${path}${query ? `?${query}` : ''}`;
|
|
45
|
+
};
|
|
46
|
+
const upload = async (file) => {
|
|
47
|
+
const prepared = await procedures.prepareUpload.call({
|
|
48
|
+
filename: file.name,
|
|
49
|
+
contentType: file.type || undefined,
|
|
50
|
+
});
|
|
51
|
+
if (prepared.mode === 'proxy') {
|
|
52
|
+
const result = await procedures.upload.call({
|
|
53
|
+
params: {},
|
|
54
|
+
query: {},
|
|
55
|
+
headers: {},
|
|
56
|
+
body: { file },
|
|
57
|
+
});
|
|
58
|
+
return {
|
|
59
|
+
...result.body,
|
|
60
|
+
url: url(result.body.fileId),
|
|
61
|
+
name: file.name,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
const uploaded = await fetch(prepared.uploadUrl, {
|
|
65
|
+
method: prepared.method,
|
|
66
|
+
body: file,
|
|
67
|
+
headers: file.type ? { 'Content-Type': file.type } : undefined,
|
|
68
|
+
});
|
|
69
|
+
if (!uploaded.ok) {
|
|
70
|
+
throw new Error(`File upload failed (${uploaded.status})`);
|
|
71
|
+
}
|
|
72
|
+
const result = await procedures.confirmUpload.call({
|
|
73
|
+
id: prepared.fileId,
|
|
74
|
+
});
|
|
75
|
+
return { ...result, url: url(result.fileId), name: file.name };
|
|
76
|
+
};
|
|
77
|
+
const deleteFile = (idOrFileId) => procedures.delete.call({ path: relativeId(property, idOrFileId) });
|
|
78
|
+
const bucket = Object.assign(Object.create(procedures), {
|
|
79
|
+
url,
|
|
80
|
+
upload,
|
|
81
|
+
delete: deleteFile,
|
|
82
|
+
});
|
|
83
|
+
buckets.set(property, bucket);
|
|
84
|
+
return bucket;
|
|
85
|
+
},
|
|
86
|
+
});
|
|
87
|
+
return new Proxy(utils, {
|
|
88
|
+
get(target, property, receiver) {
|
|
89
|
+
return property === 'files'
|
|
90
|
+
? filesProxy
|
|
91
|
+
: Reflect.get(target, property, receiver);
|
|
92
|
+
},
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
export function createClient(options = {}) {
|
|
96
|
+
return attachFileHelpers(createApiClient(options), options);
|
|
97
|
+
}
|
|
98
|
+
//# sourceMappingURL=client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","sourceRoot":"","sources":["../../src/query/client.ts"],"names":[],"mappings":"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","sourcesContent":["import type { QueryClient } from '@tanstack/query-core'\n\nimport type { ApiQueryUtils } from './api'\nimport type { AnyBunderstackApp, InferApiRouter, InferBuckets } from './infer'\n\nimport { createApiClient } from './api'\nimport { createFetch, type TransportFetch } from './fetch'\n\nexport type ClientOptions = {\n baseUrl?: string\n fetch?: TransportFetch\n queryClient?: QueryClient\n}\n\nexport type FileTransformOptions = {\n w?: number\n h?: number\n format?: 'webp' | 'jpeg' | 'png' | 'avif'\n}\n\nexport type UploadedFile = {\n fileId: string\n url: string\n name: string\n}\n\nexport type FileBucketHelpers = {\n url(idOrFileId: string, transforms?: FileTransformOptions): string\n upload(file: File): Promise<UploadedFile>\n delete(idOrFileId: string): Promise<void>\n}\n\ntype FileHelpers<TBuckets extends string> = {\n files: { [K in TBuckets]: FileBucketHelpers }\n}\n\nexport type BunderstackClient<TApp extends AnyBunderstackApp> = ApiQueryUtils<\n InferApiRouter<TApp>\n> &\n FileHelpers<InferBuckets<TApp>>\n\nfunction trimSlash(value: string): string {\n return value.replace(/\\/$/, '')\n}\n\nfunction relativeId(bucket: string, idOrFileId: string): string {\n const prefix = `${bucket}/`\n return idOrFileId.startsWith(prefix)\n ? idOrFileId.slice(prefix.length)\n : idOrFileId\n}\n\nfunction encodeFilePath(value: string): string {\n return value\n .split('/')\n .map((segment) =>\n encodeURIComponent(decodeURIComponent(segment)).replace(/\\./g, '%2E'),\n )\n .join('/')\n}\n\nfunction attachFileHelpers<T extends object>(\n utils: T,\n options: ClientOptions,\n): T {\n const baseUrl = trimSlash(options.baseUrl ?? '/api')\n const fetch = createFetch(options.fetch)\n const files = (utils as Record<string, unknown>)['files'] as\n | Record<string, unknown>\n | undefined\n if (!files) return utils\n\n const buckets = new Map<string, unknown>()\n const filesProxy = new Proxy(files, {\n get(target, property, receiver) {\n if (typeof property !== 'string')\n return Reflect.get(target, property, receiver)\n const cached = buckets.get(property)\n if (cached) return cached\n\n const procedures = Reflect.get(target, property, receiver) as Record<\n string,\n { call(input: unknown): Promise<any> }\n >\n const root = `${baseUrl}/files/${property}`\n const url = (\n idOrFileId: string,\n transforms: FileTransformOptions = {},\n ) => {\n const path = encodeFilePath(relativeId(property, idOrFileId))\n const params = new URLSearchParams()\n if (transforms.w !== undefined) params.set('w', String(transforms.w))\n if (transforms.h !== undefined) params.set('h', String(transforms.h))\n if (transforms.format) params.set('format', transforms.format)\n const query = params.toString()\n return `${root}/${path}${query ? `?${query}` : ''}`\n }\n const upload = async (file: File): Promise<UploadedFile> => {\n const prepared = await procedures.prepareUpload!.call({\n filename: file.name,\n contentType: file.type || undefined,\n })\n if (prepared.mode === 'proxy') {\n const result = await procedures.upload!.call({\n params: {},\n query: {},\n headers: {},\n body: { file },\n })\n return {\n ...result.body,\n url: url(result.body.fileId),\n name: file.name,\n }\n }\n\n const uploaded = await fetch(prepared.uploadUrl, {\n method: prepared.method,\n body: file,\n headers: file.type ? { 'Content-Type': file.type } : undefined,\n })\n if (!uploaded.ok) {\n throw new Error(`File upload failed (${uploaded.status})`)\n }\n const result = await procedures.confirmUpload!.call({\n id: prepared.fileId,\n })\n return { ...result, url: url(result.fileId), name: file.name }\n }\n\n const deleteFile = (idOrFileId: string) =>\n procedures.delete!.call({ path: relativeId(property, idOrFileId) })\n\n const bucket = Object.assign(Object.create(procedures), {\n url,\n upload,\n delete: deleteFile,\n })\n buckets.set(property, bucket)\n return bucket\n },\n })\n\n return new Proxy(utils, {\n get(target, property, receiver) {\n return property === 'files'\n ? filesProxy\n : Reflect.get(target, property, receiver)\n },\n })\n}\n\nexport function createClient<TApp extends AnyBunderstackApp>(\n options: ClientOptions = {},\n): BunderstackClient<TApp> {\n return attachFileHelpers(\n createApiClient<InferApiRouter<TApp>>(options),\n options,\n ) as BunderstackClient<TApp>\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fetch.d.ts","sourceRoot":"","sources":["../../src/query/fetch.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,cAAc,GAAG,CAC3B,KAAK,EAAE,WAAW,GAAG,GAAG,EACxB,IAAI,CAAC,EAAE,WAAW,KACf,OAAO,CAAC,QAAQ,CAAC,CAAA;AAEtB,wBAAgB,WAAW,CAAC,KAAK,CAAC,EAAE,cAAc,GAAG,cAAc,CAElE"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fetch.js","sourceRoot":"","sources":["../../src/query/fetch.ts"],"names":[],"mappings":"AAKA,MAAM,UAAU,WAAW,CAAC,KAAsB;IAChD,OAAO,KAAK,IAAI,CAAC,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,CAAC,UAAU,CAAC,KAAK,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC,CAAA;AAClE,CAAC","sourcesContent":["export type TransportFetch = (\n input: RequestInfo | URL,\n init?: RequestInit,\n) => Promise<Response>\n\nexport function createFetch(fetch?: TransportFetch): TransportFetch {\n return fetch ?? ((input, init) => globalThis.fetch(input, init))\n}\n"]}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { createClient } from './client.js';
|
|
2
|
+
export type { BunderstackClient, ClientOptions, FileBucketHelpers, FileTransformOptions, UploadedFile, } from './client.js';
|
|
3
|
+
export { createApiClient } from './api.js';
|
|
4
|
+
export type { ApiClientOptions, ApiQueryUtils } from './api.js';
|
|
5
|
+
export type { AnyBunderstackApp, ClientCarrier, ExposedTables, InferApiRouter, InferBuckets, InferInsert, InferSelect, InferSchema, InferTables, } from './infer.js';
|
|
6
|
+
export { syncRealtime } from './realtime.js';
|
|
7
|
+
export type { NotifyScheduler, RealtimeApplyStrategy, RealtimeChange, RealtimeEvent, RealtimeHeartbeat, RealtimeProcedure, RealtimeQueryApi, RealtimeSyncHandle, RealtimeSyncOptions, RealtimeClock, } from './realtime.js';
|
|
8
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/query/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAA;AACvC,YAAY,EACV,iBAAiB,EACjB,aAAa,EACb,iBAAiB,EACjB,oBAAoB,EACpB,YAAY,GACb,MAAM,UAAU,CAAA;AACjB,OAAO,EAAE,eAAe,EAAE,MAAM,OAAO,CAAA;AACvC,YAAY,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,OAAO,CAAA;AAC5D,YAAY,EACV,iBAAiB,EACjB,aAAa,EACb,aAAa,EACb,cAAc,EACd,YAAY,EACZ,WAAW,EACX,WAAW,EACX,WAAW,EACX,WAAW,GACZ,MAAM,SAAS,CAAA;AAChB,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AACzC,YAAY,EACV,eAAe,EACf,qBAAqB,EACrB,cAAc,EACd,aAAa,EACb,iBAAiB,EACjB,iBAAiB,EACjB,gBAAgB,EAChB,kBAAkB,EAClB,mBAAmB,EACnB,aAAa,GACd,MAAM,YAAY,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/query/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAA;AAQvC,OAAO,EAAE,eAAe,EAAE,MAAM,OAAO,CAAA;AAavC,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA","sourcesContent":["export { createClient } from './client'\nexport type {\n BunderstackClient,\n ClientOptions,\n FileBucketHelpers,\n FileTransformOptions,\n UploadedFile,\n} from './client'\nexport { createApiClient } from './api'\nexport type { ApiClientOptions, ApiQueryUtils } from './api'\nexport type {\n AnyBunderstackApp,\n ClientCarrier,\n ExposedTables,\n InferApiRouter,\n InferBuckets,\n InferInsert,\n InferSelect,\n InferSchema,\n InferTables,\n} from './infer'\nexport { syncRealtime } from './realtime'\nexport type {\n NotifyScheduler,\n RealtimeApplyStrategy,\n RealtimeChange,\n RealtimeEvent,\n RealtimeHeartbeat,\n RealtimeProcedure,\n RealtimeQueryApi,\n RealtimeSyncHandle,\n RealtimeSyncOptions,\n RealtimeClock,\n} from './realtime'\n"]}
|