@absolutejs/sync 1.8.1 → 1.9.1

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.
@@ -0,0 +1,138 @@
1
+ /**
2
+ * Sync packs — Convex Components without the lock-in. A pack bundles
3
+ * schemas + collections + mutations + scheduled + permissions + readers/
4
+ * writers + CRDT field declarations into one npm-distributable module
5
+ * registered with a single {@link SyncEngine.registerPack} call.
6
+ *
7
+ * See `syncPacks.design.md` for the rationale and the worked examples.
8
+ *
9
+ * Pack design rules (enforced at register time):
10
+ *
11
+ * - A pack declares which tables it `ownsTables`. Two registered packs
12
+ * cannot claim the same table; the host app's directly-registered
13
+ * tables are NOT counted (host registrations always win).
14
+ * - Name construction (namespacing) is the pack's job — each published
15
+ * pack ships as a factory `create<Name>Pack(config)` that builds names
16
+ * from a `tablePrefix` and the host's user/auth context.
17
+ * - Packs compose via the subscription layer (read each other's
18
+ * collections), never via cross-pack `runMutation` calls.
19
+ */
20
+ import type { CollectionDefinition, JoinCollectionDefinition } from './collection';
21
+ import type { GraphCollectionDefinition } from './graph';
22
+ import type { MutationDefinition, TableWriter } from './mutation';
23
+ import type { PermissionsDefinition } from './permissions';
24
+ import type { ReactiveQueryDefinition, TableReader } from './reactive';
25
+ import type { ScheduleDefinition } from './schedule';
26
+ import type { SchemaDefinition } from './schema';
27
+ import type { SearchCollectionDefinition } from './search';
28
+ import type { CrdtMergeable } from '../crdt';
29
+ /**
30
+ * Same shape as the engine's per-table CRDT field map (see
31
+ * {@link SyncEngine.registerCrdt}). A pack declares CRDT field types
32
+ * here; the engine wires them on registration.
33
+ */
34
+ export type CrdtFieldsMap = Record<string, Record<string, CrdtMergeable<unknown>>>;
35
+ /**
36
+ * A pack — a self-contained bundle of every registration the engine
37
+ * already accepts. The engine's `registerPack(pack)` dispatches each
38
+ * field to the matching `engine.register*` method. There is no new
39
+ * persistence path; packs are pure composition.
40
+ */
41
+ export type SyncPack = {
42
+ /**
43
+ * Pack identifier. Used for devtools labelling and conflict
44
+ * diagnostics. Should match the npm package name (e.g.
45
+ * "@absolutejs/sync-pack-presence").
46
+ */
47
+ name: string;
48
+ /**
49
+ * Pack semver. Surfaced in {@link EngineInspection.packs} and in
50
+ * conflict diagnostics (e.g. "table 'comments' is owned by
51
+ * sync-pack-comments@2.1.0").
52
+ */
53
+ version: string;
54
+ /**
55
+ * Tables this pack OWNS. The engine rejects another pack that also
56
+ * claims one of these. Direct host registrations (e.g.
57
+ * `engine.registerSchema("foo", ...)`) are NOT tracked as ownership,
58
+ * so the host can still extend a pack's table or override its
59
+ * schema/permissions.
60
+ */
61
+ ownsTables: string[];
62
+ /**
63
+ * Tables this pack reads but does NOT own (e.g. a comments pack
64
+ * reads the host's `users` table for author info). Reported in
65
+ * {@link EngineInspection.packs}; not enforced unless
66
+ * {@link requireDependencies} is `true`.
67
+ */
68
+ readsTables?: string[];
69
+ /**
70
+ * When `true`, the engine throws {@link PackMissingDependencyError}
71
+ * at register time if any table in `readsTables` has no registered
72
+ * reader. Default `false` — host-app reads can be wired lazily.
73
+ */
74
+ requireDependencies?: boolean;
75
+ schemas?: SchemaDefinition;
76
+ permissions?: PermissionsDefinition<any>;
77
+ readers?: Record<string, TableReader<any>>;
78
+ writers?: Record<string, TableWriter<any, any, any>>;
79
+ crdt?: CrdtFieldsMap;
80
+ collections?: CollectionDefinition<any, any, any>[];
81
+ joinCollections?: JoinCollectionDefinition<any, any, any, any, any>[];
82
+ graphCollections?: GraphCollectionDefinition<any, any, any>[];
83
+ searchCollections?: SearchCollectionDefinition<any, any, any>[];
84
+ reactiveQueries?: ReactiveQueryDefinition<any, any, any>[];
85
+ mutations?: MutationDefinition<any, any, any>[];
86
+ schedules?: ScheduleDefinition[];
87
+ };
88
+ /**
89
+ * Pack metadata stored on the engine and surfaced via
90
+ * {@link EngineInspection.packs}.
91
+ */
92
+ export type RegisteredPack = {
93
+ name: string;
94
+ version: string;
95
+ ownsTables: string[];
96
+ readsTables: string[];
97
+ };
98
+ /**
99
+ * Thrown by {@link SyncEngine.registerPack} when a pack claims a table
100
+ * that another registered pack already owns. The message names both
101
+ * packs and the colliding table so the operator can pick a
102
+ * `tablePrefix`.
103
+ */
104
+ export declare class PackTableConflictError extends Error {
105
+ readonly table: string;
106
+ readonly existingPack: string;
107
+ readonly newPack: string;
108
+ constructor(table: string, existingPack: string, newPack: string);
109
+ }
110
+ /**
111
+ * Thrown by {@link SyncEngine.registerPack} when a pack has
112
+ * `requireDependencies: true` and at least one table in
113
+ * {@link SyncPack.readsTables} has no registered reader at register
114
+ * time. Pack authors opt into this when their pack cannot function
115
+ * without the host having wired the dependency up front.
116
+ */
117
+ export declare class PackMissingDependencyError extends Error {
118
+ readonly pack: string;
119
+ readonly missingTable: string;
120
+ constructor(pack: string, missingTable: string);
121
+ }
122
+ /**
123
+ * Identity helper. A pack is plain data — the helper exists for type
124
+ * inference, not for runtime behavior.
125
+ *
126
+ * @example
127
+ * export const createPresencePack = (config: PresencePackConfig) =>
128
+ * defineSyncPack({
129
+ * name: '@absolutejs/sync-pack-presence',
130
+ * version: '0.1.0',
131
+ * ownsTables: [resolveTableName('presence', config.tablePrefix)],
132
+ * schemas: { ... },
133
+ * collections: [ ... ],
134
+ * mutations: [ ... ],
135
+ * schedules: [ ... ],
136
+ * });
137
+ */
138
+ export declare const defineSyncPack: (pack: SyncPack) => SyncPack;
@@ -1,5 +1,6 @@
1
1
  import type { MutationActions } from './mutation';
2
2
  import type { ReadHandle } from './reactive';
3
+ import type { RetryPolicy } from './retry';
3
4
  /**
4
5
  * Scheduled functions — server-triggered work whose effects flow through the
5
6
  * change feed, so what a schedule writes goes live to subscribers with no extra
@@ -30,6 +31,19 @@ export type ScheduleDefinition = {
30
31
  pattern: string;
31
32
  /** The work to run on each fire. Writes via `ctx.actions` go live. */
32
33
  run: (ctx: ScheduleContext) => Promise<void> | void;
34
+ /**
35
+ * Opt-in retry of the whole handler on classified-as-retryable errors —
36
+ * same shape and defaults as {@link MutationDefinition.retry}. When set
37
+ * and `run` throws a retryable error, the engine discards the buffered
38
+ * changes, awaits a backoff, and re-runs the handler with a fresh
39
+ * transaction. The handler MUST be idempotent under retry (external
40
+ * side effects fire more than once).
41
+ *
42
+ * For per-item retry (e.g. one of many emails failing), write that
43
+ * loop inside the handler — this outer retry covers transient
44
+ * infrastructure failures of the whole fire, not per-item logic.
45
+ */
46
+ retry?: RetryPolicy;
33
47
  };
34
48
  /**
35
49
  * Define a scheduled function. Identity at runtime (for type inference). Register
@@ -4,6 +4,7 @@ import type { MutationDefinition, TableWriter, TransactionRunner } from './mutat
4
4
  import type { ReactiveQueryDefinition, TableReader } from './reactive';
5
5
  import { type BridgeFetchConfig, type HandlerMetricsHook } from './sandbox';
6
6
  import type { PermissionsDefinition, TablePermissions } from './permissions';
7
+ import type { SyncPack } from './pack';
7
8
  import type { SearchCollectionDefinition } from './search';
8
9
  import type { ScheduleDefinition } from './schedule';
9
10
  import type { EngineActivity, EngineInspection } from './devtools';
@@ -213,6 +214,18 @@ export type SyncEngine = {
213
214
  * }
214
215
  */
215
216
  streamChanges: (options?: StreamChangesOptions) => AsyncIterable<LoggedChange>;
217
+ /**
218
+ * Register a {@link SyncPack} — a self-contained bundle of schemas,
219
+ * permissions, readers/writers, collections, mutations, and schedules.
220
+ * Dispatches each field to the matching `register*` method. Rejects
221
+ * with {@link PackTableConflictError} if the pack claims a table
222
+ * another registered pack already owns; with
223
+ * {@link PackMissingDependencyError} if `requireDependencies` is set
224
+ * and a `readsTables` entry has no registered reader.
225
+ *
226
+ * See `syncPacks.design.md` for the rationale.
227
+ */
228
+ registerPack: (pack: SyncPack) => void;
216
229
  };
217
230
  /**
218
231
  * A single committed change as it appears in the engine's change log and on
package/dist/index.js CHANGED
@@ -437,9 +437,6 @@ var syncSocket = ({
437
437
  }
438
438
  });
439
439
  };
440
- // src/engine/cdc.ts
441
- import { Elysia as Elysia3 } from "elysia";
442
-
443
440
  // src/engine/equiJoin.ts
444
441
  var shallowEqual = (a, b) => {
445
442
  if (a === b) {
@@ -935,6 +932,32 @@ var fireMetrics = (hook, record) => {
935
932
  }
936
933
  };
937
934
 
935
+ // src/engine/pack.ts
936
+ class PackTableConflictError extends Error {
937
+ table;
938
+ existingPack;
939
+ newPack;
940
+ constructor(table, existingPack, newPack) {
941
+ super(`Pack "${newPack}" claims table "${table}", but "${existingPack}" already owns it. Use a tablePrefix on one of them.`);
942
+ this.name = "PackTableConflictError";
943
+ this.table = table;
944
+ this.existingPack = existingPack;
945
+ this.newPack = newPack;
946
+ }
947
+ }
948
+
949
+ class PackMissingDependencyError extends Error {
950
+ pack;
951
+ missingTable;
952
+ constructor(pack, missingTable) {
953
+ super(`Pack "${pack}" requires a reader for table "${missingTable}" but none is registered. Call engine.registerReader("${missingTable}", ...) before engine.registerPack.`);
954
+ this.name = "PackMissingDependencyError";
955
+ this.pack = pack;
956
+ this.missingTable = missingTable;
957
+ }
958
+ }
959
+ var defineSyncPack = (pack) => pack;
960
+
938
961
  // src/engine/search.ts
939
962
  var SEARCH_SCORE_FIELD = "_score";
940
963
  var defineSearchCollection = (definition) => ({
@@ -1044,6 +1067,8 @@ var createSyncEngine = (options = {}) => {
1044
1067
  const writers = new Map;
1045
1068
  const readers = new Map;
1046
1069
  const schedules = new Map;
1070
+ const packTableOwners = new Map;
1071
+ const registeredPacks = [];
1047
1072
  const permissions = new Map;
1048
1073
  for (const [table, rules] of Object.entries(options.permissions ?? {})) {
1049
1074
  permissions.set(table, rules);
@@ -1775,7 +1800,7 @@ var createSyncEngine = (options = {}) => {
1775
1800
  }
1776
1801
  };
1777
1802
  };
1778
- return {
1803
+ const engine = {
1779
1804
  register: (collection) => {
1780
1805
  registry.set(collection.name, collection);
1781
1806
  for (const table of collection.tables ?? [collection.name]) {
@@ -2050,13 +2075,136 @@ var createSyncEngine = (options = {}) => {
2050
2075
  throw new Error(`Unknown schedule "${name}"`);
2051
2076
  }
2052
2077
  const runHandler = async (tx) => {
2053
- const { actions, buffered: buffered2 } = makeActions(tx, {}, false);
2078
+ const { actions, buffered } = makeActions(tx, {}, false);
2054
2079
  const db = makeReadHandle({}, new Set, new Set, [], false);
2055
2080
  await schedule.run({ actions, db });
2056
- return buffered2;
2081
+ return buffered;
2057
2082
  };
2058
- const buffered = runInTransaction !== undefined ? await runInTransaction((tx) => runHandler(tx)) : await runHandler(undefined);
2059
- await applyChangeBatch(buffered);
2083
+ const retry = schedule.retry;
2084
+ const maxAttempts = retry === undefined ? 1 : retry.maxAttempts ?? 5;
2085
+ const isRetryable = retry?.isRetryable ?? isSerializationFailure;
2086
+ const computeDelay = retry?.backoff ?? exponentialBackoff();
2087
+ const maxElapsedMs = retry?.maxElapsedMs ?? 30000;
2088
+ const startedAt = Date.now();
2089
+ let lastError;
2090
+ let attemptsMade = 0;
2091
+ for (let attempt = 1;attempt <= maxAttempts; attempt++) {
2092
+ attemptsMade = attempt;
2093
+ try {
2094
+ const buffered = runInTransaction !== undefined ? await runInTransaction((tx) => runHandler(tx)) : await runHandler(undefined);
2095
+ await applyChangeBatch(buffered);
2096
+ emitActivity({
2097
+ type: "schedule",
2098
+ at: Date.now(),
2099
+ name,
2100
+ status: "ok"
2101
+ });
2102
+ return;
2103
+ } catch (error) {
2104
+ lastError = error;
2105
+ const elapsedMs = Date.now() - startedAt;
2106
+ const canRetry = attempt < maxAttempts && isRetryable(error) && elapsedMs < maxElapsedMs;
2107
+ if (!canRetry)
2108
+ break;
2109
+ const rawDelay = computeDelay(attempt);
2110
+ const remaining = maxElapsedMs - elapsedMs;
2111
+ if (remaining <= 0)
2112
+ break;
2113
+ const delayMs = Math.max(0, Math.min(rawDelay, remaining));
2114
+ emitActivity({
2115
+ type: "scheduleRetry",
2116
+ at: Date.now(),
2117
+ name,
2118
+ attempt,
2119
+ delayMs,
2120
+ errorName: error instanceof Error ? error.name : "Error",
2121
+ errorMessage: error instanceof Error ? error.message : String(error)
2122
+ });
2123
+ if (delayMs > 0) {
2124
+ await new Promise((resolve) => setTimeout(resolve, delayMs));
2125
+ }
2126
+ }
2127
+ }
2128
+ emitActivity({
2129
+ type: "schedule",
2130
+ at: Date.now(),
2131
+ name,
2132
+ status: "error"
2133
+ });
2134
+ if (attemptsMade > 1) {
2135
+ throw new RetriesExhaustedError(attemptsMade, Date.now() - startedAt, lastError);
2136
+ }
2137
+ throw lastError;
2138
+ },
2139
+ registerPack: (pack) => {
2140
+ for (const table of pack.ownsTables) {
2141
+ const existing = packTableOwners.get(table);
2142
+ if (existing !== undefined) {
2143
+ throw new PackTableConflictError(table, existing, pack.name);
2144
+ }
2145
+ }
2146
+ if (pack.requireDependencies === true) {
2147
+ for (const table of pack.readsTables ?? []) {
2148
+ if (!readers.has(table)) {
2149
+ throw new PackMissingDependencyError(pack.name, table);
2150
+ }
2151
+ }
2152
+ }
2153
+ if (pack.schemas !== undefined) {
2154
+ for (const [table, schema] of Object.entries(pack.schemas)) {
2155
+ engine.registerSchema(table, schema);
2156
+ }
2157
+ }
2158
+ if (pack.permissions !== undefined) {
2159
+ for (const [table, rules] of Object.entries(pack.permissions)) {
2160
+ engine.registerPermissions(table, rules);
2161
+ }
2162
+ }
2163
+ if (pack.readers !== undefined) {
2164
+ for (const [table, reader] of Object.entries(pack.readers)) {
2165
+ engine.registerReader(table, reader);
2166
+ }
2167
+ }
2168
+ if (pack.writers !== undefined) {
2169
+ for (const [table, writer] of Object.entries(pack.writers)) {
2170
+ engine.registerWriter(table, writer);
2171
+ }
2172
+ }
2173
+ if (pack.crdt !== undefined) {
2174
+ for (const [table, fields] of Object.entries(pack.crdt)) {
2175
+ engine.registerCrdt(table, fields);
2176
+ }
2177
+ }
2178
+ for (const collection of pack.collections ?? []) {
2179
+ engine.register(collection);
2180
+ }
2181
+ for (const collection of pack.joinCollections ?? []) {
2182
+ engine.registerJoin(collection);
2183
+ }
2184
+ for (const collection of pack.graphCollections ?? []) {
2185
+ engine.registerGraph(collection);
2186
+ }
2187
+ for (const collection of pack.searchCollections ?? []) {
2188
+ engine.registerSearch(collection);
2189
+ }
2190
+ for (const query of pack.reactiveQueries ?? []) {
2191
+ engine.registerReactive(query);
2192
+ }
2193
+ for (const mutation of pack.mutations ?? []) {
2194
+ engine.registerMutation(mutation);
2195
+ }
2196
+ for (const schedule of pack.schedules ?? []) {
2197
+ engine.registerSchedule(schedule);
2198
+ }
2199
+ for (const table of pack.ownsTables) {
2200
+ packTableOwners.set(table, pack.name);
2201
+ }
2202
+ registeredPacks.push({
2203
+ name: pack.name,
2204
+ version: pack.version,
2205
+ ownsTables: [...pack.ownsTables],
2206
+ readsTables: [...pack.readsTables ?? []]
2207
+ });
2060
2208
  },
2061
2209
  inspect: () => {
2062
2210
  const collections = [...registry.entries()].map(([name, def]) => {
@@ -2096,6 +2244,12 @@ var createSyncEngine = (options = {}) => {
2096
2244
  version: entry.version,
2097
2245
  table: entry.table,
2098
2246
  op: entry.change.op
2247
+ })),
2248
+ packs: registeredPacks.map((pack) => ({
2249
+ name: pack.name,
2250
+ version: pack.version,
2251
+ ownsTables: [...pack.ownsTables],
2252
+ readsTables: [...pack.readsTables]
2099
2253
  }))
2100
2254
  };
2101
2255
  },
@@ -2182,9 +2336,18 @@ var createSyncEngine = (options = {}) => {
2182
2336
  };
2183
2337
  }
2184
2338
  };
2339
+ return engine;
2185
2340
  };
2186
2341
 
2187
2342
  // src/engine/cdc.ts
2343
+ var cachedElysia;
2344
+ var loadElysia = () => {
2345
+ if (cachedElysia !== undefined)
2346
+ return cachedElysia;
2347
+ const mod = __require("elysia");
2348
+ cachedElysia = mod.Elysia;
2349
+ return cachedElysia;
2350
+ };
2188
2351
  var parseSince = (query, lastEventId) => {
2189
2352
  const raw = query.since ?? lastEventId ?? "0";
2190
2353
  const parsed = Number(raw);
@@ -2206,70 +2369,73 @@ var syncCdc = ({
2206
2369
  path = "/sync/cdc",
2207
2370
  heartbeatMs = 25000,
2208
2371
  maxBuffer = 1e4
2209
- }) => new Elysia3({ name: "@absolutejs/sync/cdc" }).get(path, (context) => {
2210
- const lastEventId = context.request.headers.get("last-event-id");
2211
- const since = parseSince(context.query, lastEventId);
2212
- const encoder = new TextEncoder;
2213
- const stream = new ReadableStream({
2214
- async start(controller) {
2215
- const write = (chunk) => {
2216
- try {
2217
- controller.enqueue(encoder.encode(chunk));
2218
- } catch {}
2219
- };
2220
- write(encodeEvent("open", null, {
2221
- since,
2222
- at: Date.now()
2223
- }));
2224
- const heartbeat = setInterval(() => write(`: ping
2372
+ }) => {
2373
+ const Elysia3 = loadElysia();
2374
+ return new Elysia3({ name: "@absolutejs/sync/cdc" }).get(path, (context) => {
2375
+ const lastEventId = context.request.headers.get("last-event-id");
2376
+ const since = parseSince(context.query, lastEventId);
2377
+ const encoder = new TextEncoder;
2378
+ const stream = new ReadableStream({
2379
+ async start(controller) {
2380
+ const write = (chunk) => {
2381
+ try {
2382
+ controller.enqueue(encoder.encode(chunk));
2383
+ } catch {}
2384
+ };
2385
+ write(encodeEvent("open", null, {
2386
+ since,
2387
+ at: Date.now()
2388
+ }));
2389
+ const heartbeat = setInterval(() => write(`: ping
2225
2390
 
2226
2391
  `), heartbeatMs);
2227
- try {
2228
- for await (const entry of engine.streamChanges({
2229
- since,
2230
- signal: context.request.signal,
2231
- maxBuffer
2232
- })) {
2233
- write(encodeEvent("change", entry.version, entry));
2234
- }
2235
- } catch (error) {
2236
- if (error instanceof MissedChangesError) {
2237
- write(encodeEvent("error", null, {
2238
- name: "MissedChangesError",
2239
- message: error.message,
2240
- requestedSince: error.requestedSince,
2241
- availableSince: error.availableSince
2242
- }));
2243
- } else if (error instanceof CdcConsumerSlowError) {
2244
- write(encodeEvent("error", null, {
2245
- name: "CdcConsumerSlowError",
2246
- message: error.message,
2247
- lastDeliveredVersion: error.lastDeliveredVersion
2248
- }));
2249
- } else {
2250
- write(encodeEvent("error", null, {
2251
- name: error instanceof Error ? error.name : "Error",
2252
- message: error instanceof Error ? error.message : String(error)
2253
- }));
2254
- }
2255
- } finally {
2256
- clearInterval(heartbeat);
2257
2392
  try {
2258
- controller.close();
2259
- } catch {}
2393
+ for await (const entry of engine.streamChanges({
2394
+ since,
2395
+ signal: context.request.signal,
2396
+ maxBuffer
2397
+ })) {
2398
+ write(encodeEvent("change", entry.version, entry));
2399
+ }
2400
+ } catch (error) {
2401
+ if (error instanceof MissedChangesError) {
2402
+ write(encodeEvent("error", null, {
2403
+ name: "MissedChangesError",
2404
+ message: error.message,
2405
+ requestedSince: error.requestedSince,
2406
+ availableSince: error.availableSince
2407
+ }));
2408
+ } else if (error instanceof CdcConsumerSlowError) {
2409
+ write(encodeEvent("error", null, {
2410
+ name: "CdcConsumerSlowError",
2411
+ message: error.message,
2412
+ lastDeliveredVersion: error.lastDeliveredVersion
2413
+ }));
2414
+ } else {
2415
+ write(encodeEvent("error", null, {
2416
+ name: error instanceof Error ? error.name : "Error",
2417
+ message: error instanceof Error ? error.message : String(error)
2418
+ }));
2419
+ }
2420
+ } finally {
2421
+ clearInterval(heartbeat);
2422
+ try {
2423
+ controller.close();
2424
+ } catch {}
2425
+ }
2260
2426
  }
2261
- }
2262
- });
2263
- return new Response(stream, {
2264
- headers: {
2265
- "cache-control": "no-cache, no-transform",
2266
- connection: "keep-alive",
2267
- "content-type": "text/event-stream"
2268
- }
2427
+ });
2428
+ return new Response(stream, {
2429
+ headers: {
2430
+ "cache-control": "no-cache, no-transform",
2431
+ connection: "keep-alive",
2432
+ "content-type": "text/event-stream"
2433
+ }
2434
+ });
2269
2435
  });
2270
- });
2436
+ };
2271
2437
  // src/devtools.ts
2272
- import { Elysia as Elysia4 } from "elysia";
2438
+ import { Elysia as Elysia3 } from "elysia";
2273
2439
  var dashboardHtml = (streamPath) => `<!doctype html>
2274
2440
  <html lang="en"><head><meta charset="utf-8" />
2275
2441
  <meta name="viewport" content="width=device-width, initial-scale=1" />
@@ -2339,7 +2505,7 @@ var syncDevtools = ({
2339
2505
  snapshotMs = 2000
2340
2506
  }) => {
2341
2507
  const streamPath = `${path}/stream`;
2342
- return new Elysia4({ name: "@absolutejs/sync/devtools" }).get(path, () => new Response(dashboardHtml(streamPath), {
2508
+ return new Elysia3({ name: "@absolutejs/sync/devtools" }).get(path, () => new Response(dashboardHtml(streamPath), {
2343
2509
  headers: { "content-type": "text/html; charset=utf-8" }
2344
2510
  })).get(streamPath, (context) => {
2345
2511
  const encoder = new TextEncoder;
@@ -2460,5 +2626,5 @@ export {
2460
2626
  createPresenceHub
2461
2627
  };
2462
2628
 
2463
- //# debugId=DDC1D4CC66583DD464756E2164756E21
2629
+ //# debugId=2C8AB0508E99C22364756E2164756E21
2464
2630
  //# sourceMappingURL=index.js.map