@absolutejs/sync 0.4.0 → 0.6.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.
@@ -0,0 +1,39 @@
1
+ import type { MutationActions } from './mutation';
2
+ import type { ReadHandle } from './reactive';
3
+ /**
4
+ * Scheduled functions — server-triggered work whose effects flow through the
5
+ * change feed, so what a schedule writes goes live to subscribers with no extra
6
+ * wiring. The engine owns running them (and making them reactive); the trigger
7
+ * is `@elysiajs/cron` via the `scheduled` plugin — cron decides *when*, the
8
+ * engine makes the effect *live*. For durable, retryable work, a schedule can
9
+ * call into `@absolutejs/queue` (cron enqueues; the queue guarantees it runs).
10
+ */
11
+ /** What a scheduled function's `run` receives — read current state, write live. */
12
+ export type ScheduleContext = {
13
+ /** Read through registered table readers (unscoped — schedules are trusted). */
14
+ db: ReadHandle;
15
+ /**
16
+ * Persist + emit changes, exactly like a mutation handler: every
17
+ * insert/update/delete goes live as one atomic batch. Write permission rules
18
+ * are not applied (a schedule is trusted server code).
19
+ */
20
+ actions: MutationActions;
21
+ };
22
+ export type ScheduleDefinition = {
23
+ /** Schedule name — its identity (the cron job is registered under it). */
24
+ name: string;
25
+ /**
26
+ * Cron pattern (`@elysiajs/cron` / croner syntax; the optional 6th leading
27
+ * field is seconds), e.g. `'0 8 * * 1'` (Mondays 08:00) or `'*\/5 * * * * *'`
28
+ * (every 5 seconds).
29
+ */
30
+ pattern: string;
31
+ /** The work to run on each fire. Writes via `ctx.actions` go live. */
32
+ run: (ctx: ScheduleContext) => Promise<void> | void;
33
+ };
34
+ /**
35
+ * Define a scheduled function. Identity at runtime (for type inference). Register
36
+ * it with {@link SyncEngine.registerSchedule} and wire the triggers with the
37
+ * `scheduled` Elysia plugin.
38
+ */
39
+ export declare const defineSchedule: (definition: ScheduleDefinition) => ScheduleDefinition;
@@ -0,0 +1,61 @@
1
+ import type { CollectionContext } from './collection';
2
+ import type { RowKey } from './types';
3
+ /**
4
+ * A scored search result: the matched row and its relevance score (higher is
5
+ * more relevant). A search collection sorts hits descending and tags each
6
+ * emitted row with its score (see {@link SEARCH_SCORE_FIELD}).
7
+ */
8
+ export type SearchHit<T> = {
9
+ row: T;
10
+ score: number;
11
+ };
12
+ /**
13
+ * An incremental search index over a row set, queried by `Q` (a string for
14
+ * full-text, a vector for similarity). Maintained as rows are added/removed, so
15
+ * the collection that owns it stays live as the corpus changes.
16
+ * {@link createTextIndex} and {@link createVectorIndex} implement it.
17
+ */
18
+ export type SearchIndex<T, Q> = {
19
+ /** Add or replace a row (upsert by key). */
20
+ add: (row: T) => void;
21
+ /** Remove a row by key. */
22
+ remove: (key: RowKey) => void;
23
+ /** Top-`limit` hits for `query`, sorted by descending score. */
24
+ search: (query: Q, limit: number) => SearchHit<T>[];
25
+ /** Number of indexed rows. */
26
+ size: () => number;
27
+ /** Drop every indexed row. */
28
+ clear: () => void;
29
+ };
30
+ /** The field a search collection adds to each emitted row carrying its score. */
31
+ export declare const SEARCH_SCORE_FIELD = "_score";
32
+ export type SearchCollectionDefinition<T, Query = string, Ctx = CollectionContext> = {
33
+ /** Collection name — its identity for subscribe. */
34
+ name: string;
35
+ kind: 'search';
36
+ /** Source table whose committed changes keep the index live. */
37
+ table: string;
38
+ /** Build the (empty) index — e.g. `() => createTextIndex({ ... })`. */
39
+ index: () => SearchIndex<T, Query>;
40
+ /** The full corpus to index on first subscribe (e.g. a DB read). */
41
+ source: () => Promise<Iterable<T>> | Iterable<T>;
42
+ /** Row identity. */
43
+ key: (row: T) => RowKey;
44
+ /** Max results returned. Defaults to 20. */
45
+ limit?: number;
46
+ /** Access control: return `false` (or throw) to deny the subscription. */
47
+ authorize?: (query: Query, ctx: Ctx) => boolean | Promise<boolean>;
48
+ };
49
+ /**
50
+ * Define a live search collection: an index (full-text via {@link createTextIndex}
51
+ * or vector via {@link createVectorIndex}) maintained from a source table's
52
+ * change feed. The subscription's `params` *are* the query — a string for
53
+ * full-text, a vector for similarity. Register it with
54
+ * {@link SyncEngine.registerSearch}; the client receives the ranked top-K as a
55
+ * normal collection, re-ranked live as rows change. Each emitted row carries its
56
+ * relevance under {@link SEARCH_SCORE_FIELD}, so the client can sort by it.
57
+ *
58
+ * The corpus is the whole table; a row-level read permission on the table (see
59
+ * {@link definePermissions}) still filters a caller's hits.
60
+ */
61
+ export declare const defineSearchCollection: <T, Query = string, Ctx = CollectionContext>(definition: Omit<SearchCollectionDefinition<T, Query, Ctx>, "kind">) => SearchCollectionDefinition<T, Query, Ctx>;
@@ -3,6 +3,8 @@ import type { GraphCollectionDefinition } from './graph';
3
3
  import type { MutationDefinition, TableWriter, TransactionRunner } from './mutation';
4
4
  import type { ReactiveQueryDefinition, TableReader } from './reactive';
5
5
  import type { PermissionsDefinition, TablePermissions } from './permissions';
6
+ import type { SearchCollectionDefinition } from './search';
7
+ import type { ScheduleDefinition } from './schedule';
6
8
  import type { ClusterBus } from './cluster';
7
9
  import type { ChangeSource, RowChange, ViewDiff } from './types';
8
10
  /**
@@ -46,6 +48,26 @@ export type SyncEngine = {
46
48
  registerJoin: <L, R, Out, P = void, Ctx = CollectionContext>(collection: JoinCollectionDefinition<L, R, Out, P, Ctx>) => void;
47
49
  /** Register an operator-graph collection (see {@link defineGraphCollection}). */
48
50
  registerGraph: <Out, P = void, Ctx = CollectionContext>(collection: GraphCollectionDefinition<Out, P, Ctx>) => void;
51
+ /**
52
+ * Register a live search collection (see {@link defineSearchCollection}): a
53
+ * full-text or vector index maintained from a source table's change feed and
54
+ * queried by the subscription's params, returning the ranked top-K live.
55
+ */
56
+ registerSearch: <T, Query = string, Ctx = CollectionContext>(collection: SearchCollectionDefinition<T, Query, Ctx>) => void;
57
+ /**
58
+ * Register a scheduled function (see {@link defineSchedule}): server-triggered
59
+ * work whose `actions` writes go live through the change feed. Wire the cron
60
+ * triggers with the `scheduled` Elysia plugin.
61
+ */
62
+ registerSchedule: (schedule: ScheduleDefinition) => void;
63
+ /**
64
+ * Run a registered schedule's handler now: its writes commit (in the
65
+ * configured transaction) and emit as one live batch. The `scheduled` plugin
66
+ * calls this on each cron fire; call it directly to trigger on demand.
67
+ */
68
+ runSchedule: (name: string) => Promise<void>;
69
+ /** Registered schedules (name + cron pattern) — used by the `scheduled` plugin. */
70
+ listSchedules: () => ScheduleDefinition[];
49
71
  /**
50
72
  * Open a live subscription: authorize, hydrate the initial set, and stream
51
73
  * diffs as changes arrive. Rejects with {@link UnauthorizedError} on deny.
@@ -0,0 +1,33 @@
1
+ import type { RowKey } from './types';
2
+ import type { SearchIndex } from './search';
3
+ /**
4
+ * An incremental full-text index with BM25 ranking — the keyword-search half of
5
+ * the search surface (see {@link createVectorIndex} for semantic search). Pure
6
+ * and dependency-free: an in-memory inverted index maintained as rows are
7
+ * added/removed, so a {@link defineSearchCollection} stays live as the corpus
8
+ * changes. For a large corpus back it with your DB's FTS instead; this is the
9
+ * BYO, no-extension default.
10
+ */
11
+ export type TextIndexOptions<T> = {
12
+ /** Row identity. */
13
+ key: (row: T) => RowKey;
14
+ /** Fields whose text is indexed. Their values are stringified and joined. */
15
+ fields: (keyof T)[];
16
+ /**
17
+ * Split text into terms. Defaults to lowercase alphanumeric runs. Provide your
18
+ * own for stemming, n-grams, or a different alphabet — used for both indexing
19
+ * and querying, so the two always agree.
20
+ */
21
+ tokenize?: (text: string) => string[];
22
+ /** Terms to drop (e.g. `the`, `a`). Applied after `tokenize`. */
23
+ stopwords?: Iterable<string>;
24
+ /** BM25 term-frequency saturation. Defaults to 1.5. */
25
+ k1?: number;
26
+ /** BM25 length normalization (0–1). Defaults to 0.75. */
27
+ b?: number;
28
+ };
29
+ /**
30
+ * Build an incremental BM25 full-text index over rows of `T`. Implements the
31
+ * {@link SearchIndex} interface, so it plugs straight into a search collection.
32
+ */
33
+ export declare const createTextIndex: <T>(options: TextIndexOptions<T>) => SearchIndex<T, string>;
@@ -0,0 +1,27 @@
1
+ import type { RowKey } from './types';
2
+ import type { SearchIndex } from './search';
3
+ /**
4
+ * An incremental vector index for semantic / similarity search — the embeddings
5
+ * half of the search surface (see {@link createTextIndex} for keyword search).
6
+ * Pure and dependency-free: an exact (brute-force) k-NN over in-memory vectors,
7
+ * maintained as rows are added/removed, so a {@link defineSearchCollection}
8
+ * stays live. Pairs naturally with `@absolutejs/ai` / `@absolutejs/rag` for RAG
9
+ * retrieval on your own data. Exact search is O(n·d) per query — fine for tens
10
+ * of thousands of vectors; for more, back it with pgvector and a real ANN index.
11
+ */
12
+ /** Similarity metric. `cosine`/`dot` rank higher = closer; `euclidean` too (negated distance). */
13
+ export type VectorMetric = 'cosine' | 'dot' | 'euclidean';
14
+ export type VectorIndexOptions<T> = {
15
+ /** Row identity. */
16
+ key: (row: T) => RowKey;
17
+ /** Extract a row's embedding vector. */
18
+ embedding: (row: T) => number[];
19
+ /** Similarity metric. Defaults to `cosine`. */
20
+ metric?: VectorMetric;
21
+ };
22
+ /**
23
+ * Build an incremental vector index over rows of `T`. Implements the
24
+ * {@link SearchIndex} interface (queried by a query vector), so it plugs
25
+ * straight into a search collection.
26
+ */
27
+ export declare const createVectorIndex: <T>(options: VectorIndexOptions<T>) => SearchIndex<T, number[]>;
package/dist/index.d.ts CHANGED
@@ -6,5 +6,7 @@ export { sync } from './plugin';
6
6
  export type { SyncPluginOptions, SyncRequestContext } from './plugin';
7
7
  export { syncSocket } from './engine/socket';
8
8
  export type { SyncSocketOptions } from './engine/socket';
9
+ export { scheduled } from './scheduled';
10
+ export type { ScheduledOptions } from './scheduled';
9
11
  export { createPresenceHub } from './engine/presence';
10
12
  export type { PresenceDiff, PresenceHandle, PresenceHub, PresenceMember } from './engine/presence';
package/dist/index.js CHANGED
@@ -421,6 +421,32 @@ var syncSocket = ({
421
421
  }
422
422
  });
423
423
  };
424
+ // src/scheduled.ts
425
+ import { Elysia as Elysia3 } from "elysia";
426
+ import { cron } from "@elysiajs/cron";
427
+ var scheduled = ({
428
+ engine,
429
+ prefix = "sync",
430
+ onError
431
+ }) => {
432
+ const run = (name) => () => {
433
+ engine.runSchedule(name).catch((error) => {
434
+ if (onError === undefined) {
435
+ throw error;
436
+ }
437
+ onError(name, error);
438
+ });
439
+ };
440
+ const app = new Elysia3({ name: "@absolutejs/sync/scheduled" });
441
+ for (const schedule of engine.listSchedules()) {
442
+ app.use(cron({
443
+ name: `${prefix}:${schedule.name}`,
444
+ pattern: schedule.pattern,
445
+ run: run(schedule.name)
446
+ }));
447
+ }
448
+ return app;
449
+ };
424
450
  // src/engine/presence.ts
425
451
  var createPresenceHub = () => {
426
452
  const rooms = new Map;
@@ -491,10 +517,11 @@ var createPresenceHub = () => {
491
517
  export {
492
518
  syncSocket,
493
519
  sync,
520
+ scheduled,
494
521
  createWriteBehindCache,
495
522
  createReactiveHub,
496
523
  createPresenceHub
497
524
  };
498
525
 
499
- //# debugId=0AE0815066390A8564756E2164756E21
526
+ //# debugId=B7C5CACFF33C242F64756E2164756E21
500
527
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1,15 +1,16 @@
1
1
  {
2
2
  "version": 3,
3
- "sources": ["../src/writeBehindCache.ts", "../src/reactiveHub.ts", "../src/plugin.ts", "../src/engine/socket.ts", "../src/engine/connection.ts", "../src/engine/presence.ts"],
3
+ "sources": ["../src/writeBehindCache.ts", "../src/reactiveHub.ts", "../src/plugin.ts", "../src/engine/socket.ts", "../src/engine/connection.ts", "../src/scheduled.ts", "../src/engine/presence.ts"],
4
4
  "sourcesContent": [
5
5
  "export type WriteBehindCacheOptions<K, V> = {\n\t/**\n\t * Read a value from the durable store on a cache miss. Called at most once per\n\t * key until the entry is evicted.\n\t */\n\tload: (key: K) => Promise<V | undefined> | V | undefined;\n\t/** Persist a value to the durable store. Runs in the background (write-behind). */\n\tpersist: (key: K, value: V) => Promise<void> | void;\n\t/** Remove a value from the durable store. */\n\tremove?: (key: K) => Promise<void> | void;\n\t/**\n\t * Coalesce writes: each key persists at most once per window. A burst of\n\t * `set`s collapses into a single durable write. Defaults to 250ms.\n\t */\n\tdebounceMs?: number;\n\t/**\n\t * After a key persists, return true to drop it from the in-memory cache so the\n\t * cache stays bounded to \"hot\" entries (e.g. evict terminal sessions). The next\n\t * `get` reloads it via `load`. Defaults to never evicting.\n\t */\n\tevict?: (value: V, key: K) => boolean;\n\t/**\n\t * Called when a background persist throws. The cache stays authoritative and the\n\t * key re-persists on its next `set`, so a transient durable-store blip does not\n\t * drop live state. Defaults to a no-op.\n\t */\n\tonPersistError?: (error: unknown, key: K) => void;\n};\n\nexport type WriteBehindCache<K, V> = {\n\t/** Cached value, or load-through from the durable store on a miss. */\n\tget: (key: K) => Promise<V | undefined>;\n\t/** Cached value only — synchronous, never touches the durable store. */\n\tpeek: (key: K) => V | undefined;\n\thas: (key: K) => boolean;\n\t/** Write to memory immediately and schedule a coalesced durable persist. */\n\tset: (key: K, value: V) => void;\n\t/** Drop from cache and the durable store. */\n\tdelete: (key: K) => Promise<void>;\n\tkeys: () => IterableIterator<K>;\n\tvalues: () => IterableIterator<V>;\n\tsize: () => number;\n\t/** Persist every pending key to the durable store now. Call on shutdown. */\n\tflush: () => Promise<void>;\n};\n\n/**\n * Wrap a durable store (Postgres, SQLite, Drizzle, Prisma, file, S3, an HTTP API …)\n * with an in-memory hot cache and write-behind persistence.\n *\n * Reads are served from memory; writes hit memory synchronously and are flushed to\n * the durable store in coalesced background batches. The durable store stays the\n * source of truth for history and cross-instance reads, while a latency-sensitive\n * hot path (a per-frame voice session, presence, cursors, game state) stays fast.\n *\n * This is the \"fast authoritative local state, durable persistence synced behind it\"\n * split a sync engine like Convex makes — without adopting a whole sync-engine\n * backend. Bring your own store via `load`/`persist`/`remove`.\n */\nexport const createWriteBehindCache = <K, V>(\n\toptions: WriteBehindCacheOptions<K, V>\n): WriteBehindCache<K, V> => {\n\tconst debounceMs = options.debounceMs ?? 250;\n\tconst cache = new Map<K, V>();\n\tconst timers = new Map<K, ReturnType<typeof setTimeout>>();\n\n\tconst persist = async (key: K) => {\n\t\ttimers.delete(key);\n\t\tconst value = cache.get(key);\n\t\tif (value === undefined) {\n\t\t\treturn;\n\t\t}\n\t\ttry {\n\t\t\tawait options.persist(key, value);\n\t\t\tif (options.evict?.(value, key)) {\n\t\t\t\tcache.delete(key);\n\t\t\t}\n\t\t} catch (error) {\n\t\t\toptions.onPersistError?.(error, key);\n\t\t}\n\t};\n\n\tconst schedulePersist = (key: K) => {\n\t\tif (timers.has(key)) {\n\t\t\treturn;\n\t\t}\n\t\ttimers.set(\n\t\t\tkey,\n\t\t\tsetTimeout(() => {\n\t\t\t\tvoid persist(key);\n\t\t\t}, debounceMs)\n\t\t);\n\t};\n\n\treturn {\n\t\tget: async (key) => {\n\t\t\tconst cached = cache.get(key);\n\t\t\tif (cached !== undefined) {\n\t\t\t\treturn cached;\n\t\t\t}\n\t\t\tconst loaded = await options.load(key);\n\t\t\tif (loaded !== undefined) {\n\t\t\t\tcache.set(key, loaded);\n\t\t\t}\n\t\t\treturn loaded;\n\t\t},\n\t\tpeek: (key) => cache.get(key),\n\t\thas: (key) => cache.has(key),\n\t\tset: (key, value) => {\n\t\t\tcache.set(key, value);\n\t\t\tschedulePersist(key);\n\t\t},\n\t\tdelete: async (key) => {\n\t\t\tconst timer = timers.get(key);\n\t\t\tif (timer) {\n\t\t\t\tclearTimeout(timer);\n\t\t\t\ttimers.delete(key);\n\t\t\t}\n\t\t\tcache.delete(key);\n\t\t\tawait options.remove?.(key);\n\t\t},\n\t\tkeys: () => cache.keys(),\n\t\tvalues: () => cache.values(),\n\t\tsize: () => cache.size,\n\t\tflush: async () => {\n\t\t\tfor (const timer of timers.values()) {\n\t\t\t\tclearTimeout(timer);\n\t\t\t}\n\t\t\ttimers.clear();\n\t\t\tawait Promise.all([...cache.keys()].map((key) => persist(key)));\n\t\t}\n\t};\n};\n",
6
6
  "/**\n * Topic of the synthetic frame the SSE plugin emits when a stream opens (and\n * re-opens after a reconnect). Clients use it to tell \"the stream connected\"\n * apart from a real data-change event.\n */\nexport const SYNC_OPEN_TOPIC = '@absolutejs/sync:open';\n\nexport type ReactiveEvent<TPayload = unknown> = {\n\ttopic: string;\n\tat: number;\n\tpayload?: TPayload;\n};\n\nexport type ReactiveListener<TPayload = unknown> = (\n\tevent: ReactiveEvent<TPayload>\n) => void;\n\nexport type ReactiveHub = {\n\t/**\n\t * Notify every subscriber of `topic` (and any prefix-wildcard subscriber that\n\t * matches it). Call this from a mutation after the durable write commits.\n\t */\n\tpublish: (topic: string, payload?: unknown) => void;\n\t/**\n\t * Listen on one or more topics. A topic ending in `*` matches every topic that\n\t * starts with the prefix before it (e.g. `voice:session:*`). Returns an\n\t * unsubscribe function.\n\t */\n\tsubscribe: (topics: string[], listener: ReactiveListener) => () => void;\n\t/** Number of active subscribers, optionally for a single exact topic. */\n\tsubscriberCount: (topic?: string) => number;\n};\n\ntype Subscription = {\n\texact: Set<string>;\n\tprefixes: string[];\n\tlistener: ReactiveListener;\n};\n\n/**\n * An in-memory topic pub/sub for reactive, push-on-change updates.\n *\n * The pattern that replaces polling: a query/widget subscribes to the topics its\n * data depends on; a mutation `publish`es those topics after it writes; subscribers\n * are notified immediately and refetch (or receive the pushed payload) — instead of\n * every client hammering the server on a timer.\n *\n * Dependencies are explicit (you name the topics) rather than auto-tracked from a\n * query's read set — deliberately small, with no sandbox or query interception.\n * Pair it with the {@link sync} Elysia plugin to stream events to browsers over SSE.\n */\nexport const createReactiveHub = (): ReactiveHub => {\n\tconst subscriptions = new Set<Subscription>();\n\n\tconst matches = (subscription: Subscription, topic: string) => {\n\t\tif (subscription.exact.has(topic)) {\n\t\t\treturn true;\n\t\t}\n\t\treturn subscription.prefixes.some((prefix) => topic.startsWith(prefix));\n\t};\n\n\treturn {\n\t\tpublish: (topic, payload) => {\n\t\t\tconst event: ReactiveEvent = { topic, at: Date.now(), payload };\n\t\t\tfor (const subscription of subscriptions) {\n\t\t\t\tif (matches(subscription, topic)) {\n\t\t\t\t\tsubscription.listener(event);\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t\tsubscribe: (topics, listener) => {\n\t\t\tconst exact = new Set<string>();\n\t\t\tconst prefixes: string[] = [];\n\t\t\tfor (const topic of topics) {\n\t\t\t\tif (topic.endsWith('*')) {\n\t\t\t\t\tprefixes.push(topic.slice(0, -1));\n\t\t\t\t} else {\n\t\t\t\t\texact.add(topic);\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst subscription: Subscription = { exact, prefixes, listener };\n\t\t\tsubscriptions.add(subscription);\n\t\t\treturn () => {\n\t\t\t\tsubscriptions.delete(subscription);\n\t\t\t};\n\t\t},\n\t\tsubscriberCount: (topic) => {\n\t\t\tif (topic === undefined) {\n\t\t\t\treturn subscriptions.size;\n\t\t\t}\n\t\t\tlet count = 0;\n\t\t\tfor (const subscription of subscriptions) {\n\t\t\t\tif (matches(subscription, topic)) {\n\t\t\t\t\tcount += 1;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn count;\n\t\t}\n\t};\n};\n",
7
7
  "import { Elysia } from 'elysia';\nimport { SYNC_OPEN_TOPIC } from './reactiveHub';\nimport type { ReactiveEvent, ReactiveHub } from './reactiveHub';\n\nexport type SyncRequestContext = {\n\tquery: Record<string, string | undefined>;\n\trequest: Request;\n};\n\nexport type SyncPluginOptions = {\n\thub: ReactiveHub;\n\t/** Route the SSE stream is served from. Defaults to `/sync`. */\n\tpath?: string;\n\t/**\n\t * Which topics a connection subscribes to. Defaults to a comma-separated\n\t * `?topics=a,b,c` query param. Override to derive topics from the session,\n\t * params, or auth instead of trusting the client.\n\t */\n\tresolveTopics?: (context: SyncRequestContext) => string[];\n\t/**\n\t * Server→client heartbeat comment, so idle proxies don't drop the SSE stream.\n\t * Defaults to 25000ms.\n\t */\n\theartbeatMs?: number;\n};\n\nconst defaultResolveTopics = (context: SyncRequestContext) =>\n\t(context.query.topics ?? '')\n\t\t.split(',')\n\t\t.map((topic) => topic.trim())\n\t\t.filter(Boolean);\n\n/**\n * Elysia plugin that streams {@link ReactiveHub} events to browsers over Server-Sent\n * Events. Mount it once, point {@link createSyncSubscriber} at the same path, and\n * `hub.publish(topic)` from your mutations — subscribed clients are notified the\n * moment data changes, so they can refetch (or read the pushed payload) instead of\n * polling on a timer.\n */\nexport const sync = ({\n\thub,\n\tpath = '/sync',\n\tresolveTopics = defaultResolveTopics,\n\theartbeatMs = 25_000\n}: SyncPluginOptions) =>\n\tnew Elysia({ name: '@absolutejs/sync' }).get(path, (context) => {\n\t\tconst topics = resolveTopics({\n\t\t\tquery: context.query as Record<string, string | undefined>,\n\t\t\trequest: context.request\n\t\t});\n\t\tconst encoder = new TextEncoder();\n\n\t\tconst stream = new ReadableStream<Uint8Array>({\n\t\t\tstart(controller) {\n\t\t\t\tconst write = (chunk: string) => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tcontroller.enqueue(encoder.encode(chunk));\n\t\t\t\t\t} catch {\n\t\t\t\t\t\t// controller already closed by an abort race\n\t\t\t\t\t}\n\t\t\t\t};\n\t\t\t\tconst send = (event: ReactiveEvent) => {\n\t\t\t\t\twrite(`data: ${JSON.stringify(event)}\\n\\n`);\n\t\t\t\t};\n\n\t\t\t\tsend({\n\t\t\t\t\ttopic: SYNC_OPEN_TOPIC,\n\t\t\t\t\tat: Date.now(),\n\t\t\t\t\tpayload: { topics }\n\t\t\t\t});\n\n\t\t\t\tconst unsubscribe =\n\t\t\t\t\ttopics.length > 0 ? hub.subscribe(topics, send) : () => {};\n\t\t\t\tconst heartbeat = setInterval(\n\t\t\t\t\t() => write(': ping\\n\\n'),\n\t\t\t\t\theartbeatMs\n\t\t\t\t);\n\n\t\t\t\tcontext.request.signal.addEventListener(\n\t\t\t\t\t'abort',\n\t\t\t\t\t() => {\n\t\t\t\t\t\tclearInterval(heartbeat);\n\t\t\t\t\t\tunsubscribe();\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\tcontroller.close();\n\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\t// already closed\n\t\t\t\t\t\t}\n\t\t\t\t\t},\n\t\t\t\t\t{ once: true }\n\t\t\t\t);\n\t\t\t}\n\t\t});\n\n\t\treturn new Response(stream, {\n\t\t\theaders: {\n\t\t\t\t'cache-control': 'no-cache, no-transform',\n\t\t\t\tconnection: 'keep-alive',\n\t\t\t\t'content-type': 'text/event-stream'\n\t\t\t}\n\t\t});\n\t});\n",
8
8
  "import { Elysia } from 'elysia';\nimport { createSyncConnection } from './connection';\nimport type { SyncConnection } from './connection';\nimport type { PresenceHub } from './presence';\nimport type { SyncEngine } from './syncEngine';\n\nexport type SyncSocketOptions = {\n\t/** The sync engine whose collections this socket serves. */\n\tengine: SyncEngine;\n\t/** WebSocket route. Defaults to `/sync/ws`. */\n\tpath?: string;\n\t/** Optional presence hub; enables `presence-*` frames on this socket. */\n\tpresence?: PresenceHub;\n\t/**\n\t * Build the per-connection auth context from the upgrade request data\n\t * (`ws.data`: query, headers, cookies, and anything you `derive`d/`resolve`d\n\t * earlier in the chain). Whatever you return is the `ctx` passed to every\n\t * collection's `authorize`/`hydrate`/`match`. Defaults to an empty object.\n\t */\n\tresolveContext?: (\n\t\tdata: Record<string, unknown>\n\t) => unknown | Promise<unknown>;\n};\n\n/**\n * Elysia WebSocket plugin for the Tier 3 sync engine. One socket multiplexes any\n * number of collection subscriptions: the client sends `subscribe`/`unsubscribe`\n * frames and receives `snapshot`/`diff`/`error` frames (see\n * {@link createSyncConnection}). Mount it once and drive `engine.applyChange`\n * from your mutations.\n *\n * Uses Elysia's first-class `.ws()` rather than a hand-rolled stream — the\n * bidirectional channel carries both subscriptions and (later) mutations, and\n * `ws.send` serializes frames for us.\n */\nexport const syncSocket = ({\n\tengine,\n\tpath = '/sync/ws',\n\tresolveContext,\n\tpresence\n}: SyncSocketOptions) => {\n\tconst connections = new Map<string, SyncConnection>();\n\n\treturn new Elysia({ name: '@absolutejs/sync/socket' }).ws(path, {\n\t\tasync open(ws) {\n\t\t\tconst ctx = resolveContext\n\t\t\t\t? await resolveContext(ws.data as Record<string, unknown>)\n\t\t\t\t: {};\n\t\t\tconnections.set(\n\t\t\t\tws.id,\n\t\t\t\tcreateSyncConnection({\n\t\t\t\t\tengine,\n\t\t\t\t\tctx,\n\t\t\t\t\tpresence,\n\t\t\t\t\tsend: (frame) => {\n\t\t\t\t\t\tws.send(frame);\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t);\n\t\t},\n\t\tasync message(ws, message) {\n\t\t\tawait connections.get(ws.id)?.handle(message);\n\t\t},\n\t\tclose(ws) {\n\t\t\tconnections.get(ws.id)?.close();\n\t\t\tconnections.delete(ws.id);\n\t\t}\n\t});\n};\n",
9
9
  "import type { PresenceHandle, PresenceHub, PresenceMember } from './presence';\nimport type { Subscription, SyncEngine } from './syncEngine';\n\n/**\n * Wire protocol for the sync-engine WebSocket. One connection multiplexes many\n * collection subscriptions, each tagged with a client-chosen `id`.\n */\n\n/** Client → server. */\nexport type ClientFrame =\n\t| {\n\t\t\ttype: 'subscribe';\n\t\t\tid: string;\n\t\t\tcollection: string;\n\t\t\tparams?: unknown;\n\t\t\t/** Resume from a version already applied (catch-up instead of snapshot). */\n\t\t\tsince?: number;\n\t }\n\t| { type: 'unsubscribe'; id: string }\n\t| { type: 'mutate'; mutationId: number; name: string; args?: unknown }\n\t| { type: 'presence-join'; room: string; memberId: string; state: unknown }\n\t| { type: 'presence-set'; room: string; state: unknown }\n\t| { type: 'presence-leave'; room: string };\n\n/** One subscription's delta within a {@link ServerFrame} `frame`. */\nexport type FrameDiff<T = unknown> = {\n\tid: string;\n\tadded: T[];\n\tremoved: T[];\n\tchanged: T[];\n};\n\n/** Server → client. `version` is the change-feed watermark this frame brings. */\nexport type ServerFrame<T = unknown> =\n\t| { type: 'snapshot'; id: string; rows: T[]; version?: number }\n\t| {\n\t\t\ttype: 'diff';\n\t\t\tid: string;\n\t\t\tadded: T[];\n\t\t\tremoved: T[];\n\t\t\tchanged: T[];\n\t\t\tversion?: number;\n\t }\n\t| {\n\t\t\t// One atomic batch (e.g. a transactional mutation) that touched several\n\t\t\t// subscriptions — bundled into one message so the client applies them in\n\t\t\t// a single frame, never showing a torn cross-collection intermediate.\n\t\t\ttype: 'frame';\n\t\t\tversion?: number;\n\t\t\tdiffs: FrameDiff<T>[];\n\t }\n\t| {\n\t\t\t// A presence room changed: members joined, updated state, or left.\n\t\t\ttype: 'presence';\n\t\t\troom: string;\n\t\t\tjoined: PresenceMember<T>[];\n\t\t\tupdated: PresenceMember<T>[];\n\t\t\tleft: string[];\n\t }\n\t| { type: 'error'; id?: string; message: string }\n\t| { type: 'ack'; mutationId: number; result?: unknown }\n\t| { type: 'reject'; mutationId: number; message: string };\n\nexport type SyncConnectionOptions = {\n\tengine: SyncEngine;\n\t/** Resolved auth context for this connection; passed to every subscribe. */\n\tctx: unknown;\n\t/** Send a frame to the client (the transport serializes it). */\n\tsend: (frame: ServerFrame) => void;\n\t/** Optional presence hub; enables the `presence-*` frames (see createPresenceHub). */\n\tpresence?: PresenceHub;\n};\n\nexport type SyncConnection = {\n\t/** Handle one client frame (a parsed object or a raw JSON string). */\n\thandle: (raw: unknown) => Promise<void>;\n\t/** Tear down every subscription on this connection (call on socket close). */\n\tclose: () => void;\n};\n\nconst parseFrame = (raw: unknown): ClientFrame | undefined => {\n\tlet value: unknown = raw;\n\tif (typeof value === 'string') {\n\t\ttry {\n\t\t\tvalue = JSON.parse(value);\n\t\t} catch {\n\t\t\treturn undefined;\n\t\t}\n\t}\n\tif (typeof value !== 'object' || value === null) {\n\t\treturn undefined;\n\t}\n\tconst frame = value as {\n\t\ttype?: unknown;\n\t\tid?: unknown;\n\t\tcollection?: unknown;\n\t\tparams?: unknown;\n\t\tsince?: unknown;\n\t\tmutationId?: unknown;\n\t\tname?: unknown;\n\t\targs?: unknown;\n\t\troom?: unknown;\n\t\tmemberId?: unknown;\n\t\tstate?: unknown;\n\t};\n\tif (frame.type === 'subscribe') {\n\t\treturn typeof frame.id === 'string' &&\n\t\t\ttypeof frame.collection === 'string'\n\t\t\t? {\n\t\t\t\t\ttype: 'subscribe',\n\t\t\t\t\tid: frame.id,\n\t\t\t\t\tcollection: frame.collection,\n\t\t\t\t\tparams: frame.params,\n\t\t\t\t\tsince:\n\t\t\t\t\t\ttypeof frame.since === 'number'\n\t\t\t\t\t\t\t? frame.since\n\t\t\t\t\t\t\t: undefined\n\t\t\t\t}\n\t\t\t: undefined;\n\t}\n\tif (frame.type === 'unsubscribe') {\n\t\treturn typeof frame.id === 'string'\n\t\t\t? { type: 'unsubscribe', id: frame.id }\n\t\t\t: undefined;\n\t}\n\tif (frame.type === 'mutate') {\n\t\treturn typeof frame.mutationId === 'number' &&\n\t\t\ttypeof frame.name === 'string'\n\t\t\t? {\n\t\t\t\t\ttype: 'mutate',\n\t\t\t\t\tmutationId: frame.mutationId,\n\t\t\t\t\tname: frame.name,\n\t\t\t\t\targs: frame.args\n\t\t\t\t}\n\t\t\t: undefined;\n\t}\n\tif (frame.type === 'presence-join') {\n\t\treturn typeof frame.room === 'string' &&\n\t\t\ttypeof frame.memberId === 'string'\n\t\t\t? {\n\t\t\t\t\ttype: 'presence-join',\n\t\t\t\t\troom: frame.room,\n\t\t\t\t\tmemberId: frame.memberId,\n\t\t\t\t\tstate: frame.state\n\t\t\t\t}\n\t\t\t: undefined;\n\t}\n\tif (frame.type === 'presence-set') {\n\t\treturn typeof frame.room === 'string'\n\t\t\t? { type: 'presence-set', room: frame.room, state: frame.state }\n\t\t\t: undefined;\n\t}\n\tif (frame.type === 'presence-leave') {\n\t\treturn typeof frame.room === 'string'\n\t\t\t? { type: 'presence-leave', room: frame.room }\n\t\t\t: undefined;\n\t}\n\treturn undefined;\n};\n\n/**\n * The per-connection protocol handler — transport-agnostic glue between a single\n * client socket and the {@link SyncEngine}. It owns that connection's\n * subscriptions: a `subscribe` frame authorizes + hydrates and replies with a\n * `snapshot`, then streams `diff` frames; `unsubscribe`/`close` release views.\n *\n * Pure (no WebSocket import) so it can be unit-tested with a fake `send`; the\n * Elysia `syncSocket` plugin is the thin adapter that feeds it socket events.\n */\nexport const createSyncConnection = ({\n\tengine,\n\tctx,\n\tsend,\n\tpresence\n}: SyncConnectionOptions): SyncConnection => {\n\tconst subscriptions = new Map<string, Subscription<unknown>>();\n\t// This connection's presence memberships (one per room), torn down on close.\n\tconst presenceRooms = new Map<string, PresenceHandle<unknown>>();\n\n\t// Diffs from one atomic batch (a mutation, or a single applyChange) arrive via\n\t// onDiff synchronously and share a version. Buffer them and flush as one\n\t// message: a lone diff stays a plain `diff` (so single-collection clients are\n\t// unchanged); several become one `frame` the client applies atomically.\n\tlet pending: FrameDiff[] = [];\n\tlet pendingVersion: number | undefined;\n\tlet flushScheduled = false;\n\n\tconst flush = () => {\n\t\tif (pending.length === 0) {\n\t\t\treturn;\n\t\t}\n\t\tconst diffs = pending;\n\t\tconst version = pendingVersion;\n\t\tpending = [];\n\t\tpendingVersion = undefined;\n\t\tif (diffs.length === 1) {\n\t\t\tconst only = diffs[0]!;\n\t\t\tsend({\n\t\t\t\ttype: 'diff',\n\t\t\t\tid: only.id,\n\t\t\t\tadded: only.added,\n\t\t\t\tremoved: only.removed,\n\t\t\t\tchanged: only.changed,\n\t\t\t\tversion\n\t\t\t});\n\t\t} else {\n\t\t\tsend({ type: 'frame', diffs, version });\n\t\t}\n\t};\n\n\tconst scheduleFlush = () => {\n\t\tif (flushScheduled) {\n\t\t\treturn;\n\t\t}\n\t\tflushScheduled = true;\n\t\tqueueMicrotask(() => {\n\t\t\tflushScheduled = false;\n\t\t\tflush();\n\t\t});\n\t};\n\n\tconst bufferDiff = (diff: FrameDiff, diffVersion: number) => {\n\t\t// A new version means a new batch — flush the previous one first.\n\t\tif (pending.length > 0 && pendingVersion !== diffVersion) {\n\t\t\tflush();\n\t\t}\n\t\tpending.push(diff);\n\t\tpendingVersion = diffVersion;\n\t\tscheduleFlush();\n\t};\n\n\tconst handle = async (raw: unknown) => {\n\t\tconst frame = parseFrame(raw);\n\t\tif (frame === undefined) {\n\t\t\tsend({ type: 'error', message: 'Malformed sync frame' });\n\t\t\treturn;\n\t\t}\n\n\t\tif (frame.type === 'mutate') {\n\t\t\ttry {\n\t\t\t\tconst result = await engine.runMutation(\n\t\t\t\t\tframe.name,\n\t\t\t\t\tframe.args,\n\t\t\t\t\tctx\n\t\t\t\t);\n\t\t\t\t// The mutation's diffs were buffered during runMutation; flush them\n\t\t\t\t// (as one frame) before the ack so the ack always arrives after.\n\t\t\t\tflush();\n\t\t\t\tsend({ type: 'ack', mutationId: frame.mutationId, result });\n\t\t\t} catch (error) {\n\t\t\t\tsend({\n\t\t\t\t\ttype: 'reject',\n\t\t\t\t\tmutationId: frame.mutationId,\n\t\t\t\t\tmessage:\n\t\t\t\t\t\terror instanceof Error ? error.message : String(error)\n\t\t\t\t});\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tif (frame.type === 'unsubscribe') {\n\t\t\tsubscriptions.get(frame.id)?.unsubscribe();\n\t\t\tsubscriptions.delete(frame.id);\n\t\t\treturn;\n\t\t}\n\n\t\tif (frame.type === 'presence-join') {\n\t\t\tif (presence === undefined) {\n\t\t\t\tsend({ type: 'error', message: 'Presence is not enabled' });\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t// A re-join replaces the prior membership for this room.\n\t\t\tpresenceRooms.get(frame.room)?.leave();\n\t\t\tconst handle = presence.join(\n\t\t\t\tframe.room,\n\t\t\t\tframe.memberId,\n\t\t\t\tframe.state,\n\t\t\t\t(diff) => {\n\t\t\t\t\tsend({\n\t\t\t\t\t\ttype: 'presence',\n\t\t\t\t\t\troom: frame.room,\n\t\t\t\t\t\tjoined: diff.joined,\n\t\t\t\t\t\tupdated: diff.updated,\n\t\t\t\t\t\tleft: diff.left\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t);\n\t\t\tpresenceRooms.set(frame.room, handle);\n\t\t\t// Initial snapshot to the joiner (peers got a `joined` diff instead).\n\t\t\tsend({\n\t\t\t\ttype: 'presence',\n\t\t\t\troom: frame.room,\n\t\t\t\tjoined: handle.members,\n\t\t\t\tupdated: [],\n\t\t\t\tleft: []\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\n\t\tif (frame.type === 'presence-set') {\n\t\t\tpresenceRooms.get(frame.room)?.set(frame.state);\n\t\t\treturn;\n\t\t}\n\n\t\tif (frame.type === 'presence-leave') {\n\t\t\tpresenceRooms.get(frame.room)?.leave();\n\t\t\tpresenceRooms.delete(frame.room);\n\t\t\treturn;\n\t\t}\n\n\t\tif (subscriptions.has(frame.id)) {\n\t\t\tsend({\n\t\t\t\ttype: 'error',\n\t\t\t\tid: frame.id,\n\t\t\t\tmessage: `Subscription id \"${frame.id}\" already in use`\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\n\t\ttry {\n\t\t\tconst subscription = await engine.subscribe({\n\t\t\t\tcollection: frame.collection,\n\t\t\t\tparams: frame.params,\n\t\t\t\tctx,\n\t\t\t\tsince: frame.since,\n\t\t\t\tonDiff: (diff, diffVersion) => {\n\t\t\t\t\tbufferDiff(\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tid: frame.id,\n\t\t\t\t\t\t\tadded: diff.added,\n\t\t\t\t\t\t\tremoved: diff.removed,\n\t\t\t\t\t\t\tchanged: diff.changed\n\t\t\t\t\t\t},\n\t\t\t\t\t\tdiffVersion\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t});\n\t\t\tsubscriptions.set(frame.id, subscription);\n\t\t\t// No await between subscribe resolving and this send, so the initial\n\t\t\t// reply always precedes any diff for this subscription.\n\t\t\tif (subscription.catchup !== undefined) {\n\t\t\t\t// Resumed: a catch-up diff applied on top of the client's set.\n\t\t\t\tsend({\n\t\t\t\t\ttype: 'diff',\n\t\t\t\t\tid: frame.id,\n\t\t\t\t\tadded: subscription.catchup.added,\n\t\t\t\t\tremoved: subscription.catchup.removed,\n\t\t\t\t\tchanged: subscription.catchup.changed,\n\t\t\t\t\tversion: subscription.version\n\t\t\t\t});\n\t\t\t} else {\n\t\t\t\tsend({\n\t\t\t\t\ttype: 'snapshot',\n\t\t\t\t\tid: frame.id,\n\t\t\t\t\trows: subscription.initial,\n\t\t\t\t\tversion: subscription.version\n\t\t\t\t});\n\t\t\t}\n\t\t} catch (error) {\n\t\t\tsend({\n\t\t\t\ttype: 'error',\n\t\t\t\tid: frame.id,\n\t\t\t\tmessage: error instanceof Error ? error.message : String(error)\n\t\t\t});\n\t\t}\n\t};\n\n\tconst close = () => {\n\t\tfor (const subscription of subscriptions.values()) {\n\t\t\tsubscription.unsubscribe();\n\t\t}\n\t\tsubscriptions.clear();\n\t\t// Drop this connection's presence so peers see it leave (auto-cleanup).\n\t\tfor (const handle of presenceRooms.values()) {\n\t\t\thandle.leave();\n\t\t}\n\t\tpresenceRooms.clear();\n\t};\n\n\treturn { handle, close };\n};\n",
10
+ "import { Elysia } from 'elysia';\nimport { cron } from '@elysiajs/cron';\nimport type { SyncEngine } from './engine/syncEngine';\n\nexport type ScheduledOptions = {\n\t/** The engine whose registered schedules (see `registerSchedule`) to run. */\n\tengine: SyncEngine;\n\t/** Prefix for the cron job names registered on Elysia's store. Default `sync`. */\n\tprefix?: string;\n\t/** Called when a scheduled run throws (the batch is rolled back). */\n\tonError?: (name: string, error: unknown) => void;\n};\n\n/**\n * Elysia plugin that fires the engine's registered scheduled functions on their\n * cron patterns, via `@elysiajs/cron`. Cron decides *when*; the engine runs the\n * schedule and makes its writes go live through the change feed (and a schedule\n * can `enqueue` into `@absolutejs/queue` for durable, retryable work).\n *\n * Register schedules with `engine.registerSchedule(...)` before mounting this, so\n * each one becomes a cron job named `<prefix>:<schedule.name>`. Mount once.\n */\nexport const scheduled = ({\n\tengine,\n\tprefix = 'sync',\n\tonError\n}: ScheduledOptions) => {\n\tconst run = (name: string) => () => {\n\t\tvoid engine.runSchedule(name).catch((error) => {\n\t\t\tif (onError === undefined) {\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t\tonError(name, error);\n\t\t});\n\t};\n\n\t// `.use` registers the cron job on this instance (mutating), so a loop builds\n\t// one plugin carrying every schedule's cron trigger.\n\tconst app = new Elysia({ name: '@absolutejs/sync/scheduled' });\n\tfor (const schedule of engine.listSchedules()) {\n\t\tapp.use(\n\t\t\tcron({\n\t\t\t\tname: `${prefix}:${schedule.name}`,\n\t\t\t\tpattern: schedule.pattern,\n\t\t\t\trun: run(schedule.name)\n\t\t\t})\n\t\t);\n\t}\n\treturn app;\n};\n",
10
11
  "/**\n * Presence — ephemeral, room-scoped state shared over the live socket (who's\n * online, who's typing, cursor positions). Unlike collections it is **not**\n * persisted: it lives only while a member is joined, and a member's state is\n * removed (and peers notified) the moment it leaves or its connection drops.\n *\n * A `room` is any string (a document id, a channel). Each `member` is one\n * participant (typically one connection) with a `state` it owns and updates;\n * everyone in the room sees the member set and its changes.\n */\n\nexport type PresenceMember<S = unknown> = { id: string; state: S };\n\n/** What changed in a room: members that joined, updated state, or left. */\nexport type PresenceDiff<S = unknown> = {\n\tjoined: PresenceMember<S>[];\n\tupdated: PresenceMember<S>[];\n\tleft: string[];\n};\n\nexport type PresenceHandle<S> = {\n\t/** The room's members at join time (including this one). */\n\tmembers: PresenceMember<S>[];\n\t/** Replace this member's state and notify the rest of the room. */\n\tset: (state: S) => void;\n\t/** Leave the room (remove this member; notify peers). */\n\tleave: () => void;\n};\n\nexport type PresenceHub = {\n\t/**\n\t * Join `room` as `memberId` with `state`; `onDiff` receives every later change\n\t * to the room (not this member's own join). Returns the current members and\n\t * handles to update/leave.\n\t */\n\tjoin: <S>(\n\t\troom: string,\n\t\tmemberId: string,\n\t\tstate: S,\n\t\tonDiff: (diff: PresenceDiff<S>) => void\n\t) => PresenceHandle<S>;\n\t/** Snapshot a room's members without joining. */\n\tmembers: <S = unknown>(room: string) => PresenceMember<S>[];\n\t/** Number of members in a room (0 if none). */\n\tcount: (room: string) => number;\n};\n\ntype RoomMember = {\n\tstate: unknown;\n\tonDiff: (diff: PresenceDiff<unknown>) => void;\n};\n\n/**\n * Create an in-process presence hub. Transport-agnostic (no socket import): the\n * sync connection wires client `presence-*` frames to it and tears down a\n * connection's memberships on close.\n */\nexport const createPresenceHub = (): PresenceHub => {\n\tconst rooms = new Map<string, Map<string, RoomMember>>();\n\n\tconst roomMembers = (room: string): PresenceMember<unknown>[] => {\n\t\tconst members = rooms.get(room);\n\t\tif (members === undefined) {\n\t\t\treturn [];\n\t\t}\n\t\treturn [...members].map(([id, member]) => ({\n\t\t\tid,\n\t\t\tstate: member.state\n\t\t}));\n\t};\n\n\t/** Notify everyone in `room` except the actor that caused the change. */\n\tconst notify = (\n\t\troom: string,\n\t\tdiff: PresenceDiff<unknown>,\n\t\texceptId: string\n\t) => {\n\t\tconst members = rooms.get(room);\n\t\tif (members === undefined) {\n\t\t\treturn;\n\t\t}\n\t\tfor (const [id, member] of members) {\n\t\t\tif (id !== exceptId) {\n\t\t\t\tmember.onDiff(diff);\n\t\t\t}\n\t\t}\n\t};\n\n\treturn {\n\t\tjoin: (room, memberId, state, onDiff) => {\n\t\t\tlet members = rooms.get(room);\n\t\t\tif (members === undefined) {\n\t\t\t\tmembers = new Map();\n\t\t\t\trooms.set(room, members);\n\t\t\t}\n\t\t\tmembers.set(memberId, {\n\t\t\t\tstate,\n\t\t\t\tonDiff: onDiff as (diff: PresenceDiff<unknown>) => void\n\t\t\t});\n\t\t\t// Peers learn this member joined; the joiner gets the snapshot instead.\n\t\t\tnotify(\n\t\t\t\troom,\n\t\t\t\t{ joined: [{ id: memberId, state }], updated: [], left: [] },\n\t\t\t\tmemberId\n\t\t\t);\n\t\t\tconst snapshot = roomMembers(room) as PresenceMember<\n\t\t\t\ttypeof state\n\t\t\t>[];\n\n\t\t\treturn {\n\t\t\t\tmembers: snapshot,\n\t\t\t\tset: (next) => {\n\t\t\t\t\tconst current = rooms.get(room)?.get(memberId);\n\t\t\t\t\tif (current === undefined) {\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tcurrent.state = next;\n\t\t\t\t\tnotify(\n\t\t\t\t\t\troom,\n\t\t\t\t\t\t{\n\t\t\t\t\t\t\tjoined: [],\n\t\t\t\t\t\t\tupdated: [{ id: memberId, state: next }],\n\t\t\t\t\t\t\tleft: []\n\t\t\t\t\t\t},\n\t\t\t\t\t\tmemberId\n\t\t\t\t\t);\n\t\t\t\t},\n\t\t\t\tleave: () => {\n\t\t\t\t\tconst roomNow = rooms.get(room);\n\t\t\t\t\tif (roomNow?.delete(memberId) !== true) {\n\t\t\t\t\t\treturn;\n\t\t\t\t\t}\n\t\t\t\t\tnotify(\n\t\t\t\t\t\troom,\n\t\t\t\t\t\t{ joined: [], updated: [], left: [memberId] },\n\t\t\t\t\t\tmemberId\n\t\t\t\t\t);\n\t\t\t\t\tif (roomNow.size === 0) {\n\t\t\t\t\t\trooms.delete(room);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t};\n\t\t},\n\t\tmembers: (room) => roomMembers(room) as PresenceMember<never>[],\n\t\tcount: (room) => rooms.get(room)?.size ?? 0\n\t};\n};\n"
11
12
  ],
12
- "mappings": "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",
13
- "debugId": "0AE0815066390A8564756E2164756E21",
13
+ "mappings": "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",
14
+ "debugId": "B7C5CACFF33C242F64756E2164756E21",
14
15
  "names": []
15
16
  }
@@ -0,0 +1,47 @@
1
+ import { Elysia } from 'elysia';
2
+ import type { SyncEngine } from './engine/syncEngine';
3
+ export type ScheduledOptions = {
4
+ /** The engine whose registered schedules (see `registerSchedule`) to run. */
5
+ engine: SyncEngine;
6
+ /** Prefix for the cron job names registered on Elysia's store. Default `sync`. */
7
+ prefix?: string;
8
+ /** Called when a scheduled run throws (the batch is rolled back). */
9
+ onError?: (name: string, error: unknown) => void;
10
+ };
11
+ /**
12
+ * Elysia plugin that fires the engine's registered scheduled functions on their
13
+ * cron patterns, via `@elysiajs/cron`. Cron decides *when*; the engine runs the
14
+ * schedule and makes its writes go live through the change feed (and a schedule
15
+ * can `enqueue` into `@absolutejs/queue` for durable, retryable work).
16
+ *
17
+ * Register schedules with `engine.registerSchedule(...)` before mounting this, so
18
+ * each one becomes a cron job named `<prefix>:<schedule.name>`. Mount once.
19
+ */
20
+ export declare const scheduled: ({ engine, prefix, onError }: ScheduledOptions) => Elysia<"", {
21
+ decorator: {};
22
+ store: {};
23
+ derive: {};
24
+ resolve: {};
25
+ }, {
26
+ typebox: {};
27
+ error: {};
28
+ }, {
29
+ schema: {};
30
+ standaloneSchema: {};
31
+ macro: {};
32
+ macroFn: {};
33
+ parser: {};
34
+ response: {};
35
+ }, {}, {
36
+ derive: {};
37
+ resolve: {};
38
+ schema: {};
39
+ standaloneSchema: {};
40
+ response: {};
41
+ }, {
42
+ derive: {};
43
+ resolve: {};
44
+ schema: {};
45
+ standaloneSchema: {};
46
+ response: {};
47
+ }>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@absolutejs/sync",
3
- "version": "0.4.0",
3
+ "version": "0.6.0",
4
4
  "description": "Lightweight reactive-push and write-behind-cache primitives for Elysia and the AbsoluteJS ecosystem — kill polling and keep a remote store off your hot path, without adopting a whole sync-engine backend.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -111,6 +111,7 @@
111
111
  ],
112
112
  "peerDependencies": {
113
113
  "@angular/core": ">= 21.0.0",
114
+ "@elysiajs/cron": ">= 1.4.0",
114
115
  "drizzle-orm": ">= 0.44.0",
115
116
  "elysia": ">= 1.4.26",
116
117
  "react": ">= 19.0.0",
@@ -121,6 +122,9 @@
121
122
  "@angular/core": {
122
123
  "optional": true
123
124
  },
125
+ "@elysiajs/cron": {
126
+ "optional": true
127
+ },
124
128
  "drizzle-orm": {
125
129
  "optional": true
126
130
  },
@@ -140,6 +144,7 @@
140
144
  "devDependencies": {
141
145
  "@absolutejs/absolute": "^0.19.0-beta.1051",
142
146
  "@angular/core": "^21.0.0",
147
+ "@elysiajs/cron": "^1.4.2",
143
148
  "@elysiajs/eden": "^1.4.9",
144
149
  "@types/bun": "latest",
145
150
  "@types/react": "^19.2.0",