@fatagnus/dink-convex 1.0.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.
Files changed (49) hide show
  1. package/LICENSE +190 -0
  2. package/README.md +282 -0
  3. package/convex/convex.config.ts +23 -0
  4. package/convex/crons.ts +37 -0
  5. package/convex/http.ts +421 -0
  6. package/convex/index.ts +20 -0
  7. package/convex/install.ts +172 -0
  8. package/convex/outbox.ts +198 -0
  9. package/convex/outboxProcessor.ts +240 -0
  10. package/convex/schema.ts +97 -0
  11. package/convex/sync.ts +327 -0
  12. package/dist/component.d.ts +34 -0
  13. package/dist/component.d.ts.map +1 -0
  14. package/dist/component.js +35 -0
  15. package/dist/component.js.map +1 -0
  16. package/dist/crdt.d.ts +82 -0
  17. package/dist/crdt.d.ts.map +1 -0
  18. package/dist/crdt.js +134 -0
  19. package/dist/crdt.js.map +1 -0
  20. package/dist/factories.d.ts +80 -0
  21. package/dist/factories.d.ts.map +1 -0
  22. package/dist/factories.js +159 -0
  23. package/dist/factories.js.map +1 -0
  24. package/dist/http.d.ts +238 -0
  25. package/dist/http.d.ts.map +1 -0
  26. package/dist/http.js +222 -0
  27. package/dist/http.js.map +1 -0
  28. package/dist/httpFactory.d.ts +39 -0
  29. package/dist/httpFactory.d.ts.map +1 -0
  30. package/dist/httpFactory.js +128 -0
  31. package/dist/httpFactory.js.map +1 -0
  32. package/dist/index.d.ts +68 -0
  33. package/dist/index.d.ts.map +1 -0
  34. package/dist/index.js +73 -0
  35. package/dist/index.js.map +1 -0
  36. package/dist/schema.d.ts +217 -0
  37. package/dist/schema.d.ts.map +1 -0
  38. package/dist/schema.js +195 -0
  39. package/dist/schema.js.map +1 -0
  40. package/dist/syncFactories.d.ts +240 -0
  41. package/dist/syncFactories.d.ts.map +1 -0
  42. package/dist/syncFactories.js +623 -0
  43. package/dist/syncFactories.js.map +1 -0
  44. package/dist/triggers.d.ts +442 -0
  45. package/dist/triggers.d.ts.map +1 -0
  46. package/dist/triggers.js +705 -0
  47. package/dist/triggers.js.map +1 -0
  48. package/package.json +108 -0
  49. package/scripts/check-peer-deps.cjs +132 -0
@@ -0,0 +1,217 @@
1
+ /**
2
+ * Schema helpers for defining synced tables.
3
+ *
4
+ * Provides:
5
+ * - `syncedTable` helper that wraps table definitions to enable automatic sync
6
+ * - `internalSyncSchema` that users spread into their schema for internal sync tables
7
+ *
8
+ * @module schema
9
+ */
10
+ import { defineTable } from "convex/server";
11
+ /**
12
+ * Internal sync tables schema.
13
+ *
14
+ * These tables are required by the @fatagnus/dink-convex sync system.
15
+ * Spread this into your app's schema to include all internal tables:
16
+ *
17
+ * @example
18
+ * ```typescript
19
+ * // convex/schema.ts
20
+ * import { defineSchema } from "convex/server";
21
+ * import { syncedTable, internalSyncSchema } from "@fatagnus/dink-convex";
22
+ *
23
+ * export default defineSchema({
24
+ * // Your synced tables
25
+ * tasks: syncedTable({ title: v.string(), completed: v.boolean() }),
26
+ *
27
+ * // Spread internal sync tables (required)
28
+ * ...internalSyncSchema,
29
+ * });
30
+ * ```
31
+ *
32
+ * ## Why These Tables Are in Your Schema (Not Hidden in the Component)
33
+ *
34
+ * You might wonder why `@fatagnus/dink-convex` requires spreading these tables into your
35
+ * schema rather than hiding them entirely inside the component. This is due to
36
+ * a fundamental Convex constraint:
37
+ *
38
+ * **Convex mutations cannot call `ctx.runMutation()` to invoke other mutations.**
39
+ *
40
+ * This matters because:
41
+ * 1. Sync triggers run inside your mutation context (via convex-helpers Triggers)
42
+ * 2. Triggers must write delta/outbox records atomically in the same transaction
43
+ * 3. Component-internal tables can only be written via component mutations
44
+ * 4. Since triggers can't call component mutations, tables must be in app schema
45
+ *
46
+ * This ensures atomicity: your data changes and sync metadata are written together.
47
+ * If we used actions instead, a failure after your mutation but before recording
48
+ * the delta would leave data that never syncs.
49
+ *
50
+ * The practical impact is minimal - just one spread line in your schema. You never
51
+ * interact with these tables directly; they're managed entirely by the sync system.
52
+ */
53
+ export declare const internalSyncSchema: {
54
+ /**
55
+ * sync_deltas - Stores CRDT delta updates for sync operations.
56
+ * Each delta represents a change that needs to be synchronized.
57
+ */
58
+ readonly sync_deltas: import("convex/server").TableDefinition<import("convex/values").VObject<{
59
+ edgeId?: string | undefined;
60
+ bytes: ArrayBuffer;
61
+ collection: string;
62
+ docId: string;
63
+ seq: number;
64
+ timestamp: number;
65
+ }, {
66
+ /** Collection name (user table) */
67
+ collection: import("convex/values").VString<string, "required">;
68
+ /** Document sync ID (syncId from user table) */
69
+ docId: import("convex/values").VString<string, "required">;
70
+ /** CRDT delta bytes (Yjs update) */
71
+ bytes: import("convex/values").VBytes<ArrayBuffer, "required">;
72
+ /** Sequence number for ordering */
73
+ seq: import("convex/values").VFloat64<number, "required">;
74
+ /** Timestamp when delta was created */
75
+ timestamp: import("convex/values").VFloat64<number, "required">;
76
+ /** Edge ID that originated this delta (optional) */
77
+ edgeId: import("convex/values").VString<string | undefined, "optional">;
78
+ }, "required", "bytes" | "collection" | "docId" | "seq" | "timestamp" | "edgeId">, {
79
+ by_collection: ["collection", "_creationTime"];
80
+ by_docId: ["collection", "docId", "_creationTime"];
81
+ by_seq: ["collection", "seq", "_creationTime"];
82
+ }, {}, {}>;
83
+ /**
84
+ * sync_outbox - Queue of deltas pending push to dinkd.
85
+ * Deltas are queued here when triggers fire, then processed by
86
+ * the scheduled outbox processor which pushes them to dinkd.
87
+ */
88
+ readonly sync_outbox: import("convex/server").TableDefinition<import("convex/values").VObject<{
89
+ lastAttempt?: number | undefined;
90
+ collection: string;
91
+ docId: string;
92
+ seq: number;
93
+ delta: ArrayBuffer;
94
+ status: string;
95
+ retries: number;
96
+ createdAt: number;
97
+ }, {
98
+ /** Collection name (user table) */
99
+ collection: import("convex/values").VString<string, "required">;
100
+ /** Document sync ID (syncId from user table) */
101
+ docId: import("convex/values").VString<string, "required">;
102
+ /** CRDT delta bytes to send */
103
+ delta: import("convex/values").VBytes<ArrayBuffer, "required">;
104
+ /** Sequence number for deduplication */
105
+ seq: import("convex/values").VFloat64<number, "required">;
106
+ /** Status: "pending" | "sent" | "failed" */
107
+ status: import("convex/values").VString<string, "required">;
108
+ /** Number of retry attempts */
109
+ retries: import("convex/values").VFloat64<number, "required">;
110
+ /** Timestamp when item was created */
111
+ createdAt: import("convex/values").VFloat64<number, "required">;
112
+ /** Timestamp of last send attempt (optional) */
113
+ lastAttempt: import("convex/values").VFloat64<number | undefined, "optional">;
114
+ }, "required", "collection" | "docId" | "seq" | "delta" | "status" | "retries" | "createdAt" | "lastAttempt">, {
115
+ by_status: ["status", "_creationTime"];
116
+ by_collection: ["collection", "_creationTime"];
117
+ by_docId_seq: ["docId", "seq", "_creationTime"];
118
+ }, {}, {}>;
119
+ /**
120
+ * sync_sessions - Tracks connected sessions and their sync state.
121
+ */
122
+ readonly sync_sessions: import("convex/server").TableDefinition<import("convex/values").VObject<{
123
+ collection: string;
124
+ sessionId: string;
125
+ lastSeq: number;
126
+ lastSeen: number;
127
+ }, {
128
+ /** Unique session identifier */
129
+ sessionId: import("convex/values").VString<string, "required">;
130
+ /** Collection being synced */
131
+ collection: import("convex/values").VString<string, "required">;
132
+ /** Last sequence number acknowledged by this session */
133
+ lastSeq: import("convex/values").VFloat64<number, "required">;
134
+ /** Timestamp when session was last active */
135
+ lastSeen: import("convex/values").VFloat64<number, "required">;
136
+ }, "required", "collection" | "sessionId" | "lastSeq" | "lastSeen">, {
137
+ by_sessionId: ["sessionId", "_creationTime"];
138
+ by_collection: ["collection", "_creationTime"];
139
+ }, {}, {}>;
140
+ /**
141
+ * sync_config - Configuration for which tables have sync enabled.
142
+ */
143
+ readonly sync_config: import("convex/server").TableDefinition<import("convex/values").VObject<{
144
+ collection: string;
145
+ enabled: boolean;
146
+ }, {
147
+ /** Collection name (user table) */
148
+ collection: import("convex/values").VString<string, "required">;
149
+ /** Whether sync is enabled for this collection */
150
+ enabled: import("convex/values").VBoolean<boolean, "required">;
151
+ }, "required", "collection" | "enabled">, {
152
+ by_collection: ["collection", "_creationTime"];
153
+ }, {}, {}>;
154
+ };
155
+ /**
156
+ * Configuration options for syncedTable.
157
+ */
158
+ export interface SyncedTableConfig {
159
+ /** Optional custom sync ID field name (defaults to syncId) */
160
+ syncIdField?: string;
161
+ }
162
+ /**
163
+ * Register a table name as a synced table.
164
+ * Called internally or by developers to track which tables need sync enabled.
165
+ *
166
+ * @param tableName - The name of the table to register
167
+ */
168
+ export declare function registerSyncedTableName(tableName: string): void;
169
+ /**
170
+ * Get all registered synced table names.
171
+ * Used during installation to auto-enable sync for all registered tables.
172
+ *
173
+ * @returns Array of registered table names
174
+ */
175
+ export declare function getRegisteredSyncedTables(): string[];
176
+ /**
177
+ * Clear all registered synced tables.
178
+ * Useful for testing.
179
+ */
180
+ export declare function clearRegisteredSyncedTables(): void;
181
+ /**
182
+ * The return type of defineTable - a table definition that can have indexes added.
183
+ */
184
+ type TableDefinition = ReturnType<typeof defineTable>;
185
+ /**
186
+ * Field validators type - the object passed to defineTable
187
+ * Using a simple record type since Convex validators are complex
188
+ */
189
+ type ValidatorFields = Record<string, any>;
190
+ /**
191
+ * Creates a synced Convex table definition with sync-related fields and indexes.
192
+ *
193
+ * This helper automatically adds:
194
+ * - A sync ID field (default: syncId) for tracking document sync state
195
+ * - An index on the sync ID field for efficient sync queries
196
+ *
197
+ * @param fields - The field validators for the table (same as what you'd pass to defineTable)
198
+ * @param config - Optional configuration for sync behavior
199
+ * @returns A table definition with sync fields and indexes included
200
+ *
201
+ * @example
202
+ * ```typescript
203
+ * import { defineSchema } from "convex/server";
204
+ * import { v } from "convex/values";
205
+ * import { syncedTable } from "@fatagnus/dink-convex";
206
+ *
207
+ * export default defineSchema({
208
+ * tasks: syncedTable({
209
+ * text: v.string(),
210
+ * isCompleted: v.boolean(),
211
+ * }),
212
+ * });
213
+ * ```
214
+ */
215
+ export declare function syncedTable<T extends ValidatorFields>(fields: T, config?: SyncedTableConfig): TableDefinition;
216
+ export {};
217
+ //# sourceMappingURL=schema.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAG5C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AACH,eAAO,MAAM,kBAAkB;IAC7B;;;OAGG;;;;;;;;;QAED,mCAAmC;;QAEnC,gDAAgD;;QAEhD,oCAAoC;;QAEpC,mCAAmC;;QAEnC,uCAAuC;;QAEvC,oDAAoD;;;;;;;IAOtD;;;;OAIG;;;;;;;;;;;QAED,mCAAmC;;QAEnC,gDAAgD;;QAEhD,+BAA+B;;QAE/B,wCAAwC;;QAExC,4CAA4C;;QAE5C,+BAA+B;;QAE/B,sCAAsC;;QAEtC,gDAAgD;;;;;;;IAOlD;;OAEG;;;;;;;QAED,gCAAgC;;QAEhC,8BAA8B;;QAE9B,wDAAwD;;QAExD,6CAA6C;;;;;;IAM/C;;OAEG;;;;;QAED,mCAAmC;;QAEnC,kDAAkD;;;;;CAG5C,CAAC;AAEX;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,8DAA8D;IAC9D,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAQD;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAE/D;AAED;;;;;GAKG;AACH,wBAAgB,yBAAyB,IAAI,MAAM,EAAE,CAEpD;AAED;;;GAGG;AACH,wBAAgB,2BAA2B,IAAI,IAAI,CAElD;AAED;;GAEG;AACH,KAAK,eAAe,GAAG,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC;AAEtD;;;GAGG;AAEH,KAAK,eAAe,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;AAE3C;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,WAAW,CAAC,CAAC,SAAS,eAAe,EACnD,MAAM,EAAE,CAAC,EACT,MAAM,CAAC,EAAE,iBAAiB,GACzB,eAAe,CAcjB"}
package/dist/schema.js ADDED
@@ -0,0 +1,195 @@
1
+ /**
2
+ * Schema helpers for defining synced tables.
3
+ *
4
+ * Provides:
5
+ * - `syncedTable` helper that wraps table definitions to enable automatic sync
6
+ * - `internalSyncSchema` that users spread into their schema for internal sync tables
7
+ *
8
+ * @module schema
9
+ */
10
+ import { defineTable } from "convex/server";
11
+ import { v } from "convex/values";
12
+ /**
13
+ * Internal sync tables schema.
14
+ *
15
+ * These tables are required by the @fatagnus/dink-convex sync system.
16
+ * Spread this into your app's schema to include all internal tables:
17
+ *
18
+ * @example
19
+ * ```typescript
20
+ * // convex/schema.ts
21
+ * import { defineSchema } from "convex/server";
22
+ * import { syncedTable, internalSyncSchema } from "@fatagnus/dink-convex";
23
+ *
24
+ * export default defineSchema({
25
+ * // Your synced tables
26
+ * tasks: syncedTable({ title: v.string(), completed: v.boolean() }),
27
+ *
28
+ * // Spread internal sync tables (required)
29
+ * ...internalSyncSchema,
30
+ * });
31
+ * ```
32
+ *
33
+ * ## Why These Tables Are in Your Schema (Not Hidden in the Component)
34
+ *
35
+ * You might wonder why `@fatagnus/dink-convex` requires spreading these tables into your
36
+ * schema rather than hiding them entirely inside the component. This is due to
37
+ * a fundamental Convex constraint:
38
+ *
39
+ * **Convex mutations cannot call `ctx.runMutation()` to invoke other mutations.**
40
+ *
41
+ * This matters because:
42
+ * 1. Sync triggers run inside your mutation context (via convex-helpers Triggers)
43
+ * 2. Triggers must write delta/outbox records atomically in the same transaction
44
+ * 3. Component-internal tables can only be written via component mutations
45
+ * 4. Since triggers can't call component mutations, tables must be in app schema
46
+ *
47
+ * This ensures atomicity: your data changes and sync metadata are written together.
48
+ * If we used actions instead, a failure after your mutation but before recording
49
+ * the delta would leave data that never syncs.
50
+ *
51
+ * The practical impact is minimal - just one spread line in your schema. You never
52
+ * interact with these tables directly; they're managed entirely by the sync system.
53
+ */
54
+ export const internalSyncSchema = {
55
+ /**
56
+ * sync_deltas - Stores CRDT delta updates for sync operations.
57
+ * Each delta represents a change that needs to be synchronized.
58
+ */
59
+ sync_deltas: defineTable({
60
+ /** Collection name (user table) */
61
+ collection: v.string(),
62
+ /** Document sync ID (syncId from user table) */
63
+ docId: v.string(),
64
+ /** CRDT delta bytes (Yjs update) */
65
+ bytes: v.bytes(),
66
+ /** Sequence number for ordering */
67
+ seq: v.number(),
68
+ /** Timestamp when delta was created */
69
+ timestamp: v.number(),
70
+ /** Edge ID that originated this delta (optional) */
71
+ edgeId: v.optional(v.string()),
72
+ })
73
+ .index("by_collection", ["collection"])
74
+ .index("by_docId", ["collection", "docId"])
75
+ .index("by_seq", ["collection", "seq"]),
76
+ /**
77
+ * sync_outbox - Queue of deltas pending push to dinkd.
78
+ * Deltas are queued here when triggers fire, then processed by
79
+ * the scheduled outbox processor which pushes them to dinkd.
80
+ */
81
+ sync_outbox: defineTable({
82
+ /** Collection name (user table) */
83
+ collection: v.string(),
84
+ /** Document sync ID (syncId from user table) */
85
+ docId: v.string(),
86
+ /** CRDT delta bytes to send */
87
+ delta: v.bytes(),
88
+ /** Sequence number for deduplication */
89
+ seq: v.number(),
90
+ /** Status: "pending" | "sent" | "failed" */
91
+ status: v.string(),
92
+ /** Number of retry attempts */
93
+ retries: v.number(),
94
+ /** Timestamp when item was created */
95
+ createdAt: v.number(),
96
+ /** Timestamp of last send attempt (optional) */
97
+ lastAttempt: v.optional(v.number()),
98
+ })
99
+ .index("by_status", ["status"])
100
+ .index("by_collection", ["collection"])
101
+ .index("by_docId_seq", ["docId", "seq"]),
102
+ /**
103
+ * sync_sessions - Tracks connected sessions and their sync state.
104
+ */
105
+ sync_sessions: defineTable({
106
+ /** Unique session identifier */
107
+ sessionId: v.string(),
108
+ /** Collection being synced */
109
+ collection: v.string(),
110
+ /** Last sequence number acknowledged by this session */
111
+ lastSeq: v.number(),
112
+ /** Timestamp when session was last active */
113
+ lastSeen: v.number(),
114
+ })
115
+ .index("by_sessionId", ["sessionId"])
116
+ .index("by_collection", ["collection"]),
117
+ /**
118
+ * sync_config - Configuration for which tables have sync enabled.
119
+ */
120
+ sync_config: defineTable({
121
+ /** Collection name (user table) */
122
+ collection: v.string(),
123
+ /** Whether sync is enabled for this collection */
124
+ enabled: v.boolean(),
125
+ }).index("by_collection", ["collection"]),
126
+ };
127
+ /**
128
+ * Registry of table names that use syncedTable().
129
+ * Used during installation to auto-register collections in _sync_config.
130
+ */
131
+ const registeredSyncedTables = new Set();
132
+ /**
133
+ * Register a table name as a synced table.
134
+ * Called internally or by developers to track which tables need sync enabled.
135
+ *
136
+ * @param tableName - The name of the table to register
137
+ */
138
+ export function registerSyncedTableName(tableName) {
139
+ registeredSyncedTables.add(tableName);
140
+ }
141
+ /**
142
+ * Get all registered synced table names.
143
+ * Used during installation to auto-enable sync for all registered tables.
144
+ *
145
+ * @returns Array of registered table names
146
+ */
147
+ export function getRegisteredSyncedTables() {
148
+ return Array.from(registeredSyncedTables);
149
+ }
150
+ /**
151
+ * Clear all registered synced tables.
152
+ * Useful for testing.
153
+ */
154
+ export function clearRegisteredSyncedTables() {
155
+ registeredSyncedTables.clear();
156
+ }
157
+ /**
158
+ * Creates a synced Convex table definition with sync-related fields and indexes.
159
+ *
160
+ * This helper automatically adds:
161
+ * - A sync ID field (default: syncId) for tracking document sync state
162
+ * - An index on the sync ID field for efficient sync queries
163
+ *
164
+ * @param fields - The field validators for the table (same as what you'd pass to defineTable)
165
+ * @param config - Optional configuration for sync behavior
166
+ * @returns A table definition with sync fields and indexes included
167
+ *
168
+ * @example
169
+ * ```typescript
170
+ * import { defineSchema } from "convex/server";
171
+ * import { v } from "convex/values";
172
+ * import { syncedTable } from "@fatagnus/dink-convex";
173
+ *
174
+ * export default defineSchema({
175
+ * tasks: syncedTable({
176
+ * text: v.string(),
177
+ * isCompleted: v.boolean(),
178
+ * }),
179
+ * });
180
+ * ```
181
+ */
182
+ export function syncedTable(fields, config) {
183
+ // Use "syncId" without underscore prefix since Convex doesn't allow reserved fields in indexes
184
+ const syncIdField = config?.syncIdField ?? "syncId";
185
+ const indexName = `by_${syncIdField}`;
186
+ // Create table with original fields plus sync ID field
187
+ const extendedFields = {
188
+ ...fields,
189
+ [syncIdField]: v.optional(v.string()),
190
+ };
191
+ // Create table definition using Convex's defineTable and add the sync index
192
+ // Cast to any to work around strict typing on index fields
193
+ return defineTable(extendedFields).index(indexName, [syncIdField]);
194
+ }
195
+ //# sourceMappingURL=schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.js","sourceRoot":"","sources":["../src/schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,CAAC,EAAE,MAAM,eAAe,CAAC;AAElC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG;IAChC;;;OAGG;IACH,WAAW,EAAE,WAAW,CAAC;QACvB,mCAAmC;QACnC,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE;QACtB,gDAAgD;QAChD,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE;QACjB,oCAAoC;QACpC,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE;QAChB,mCAAmC;QACnC,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE;QACf,uCAAuC;QACvC,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE;QACrB,oDAAoD;QACpD,MAAM,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;KAC/B,CAAC;SACC,KAAK,CAAC,eAAe,EAAE,CAAC,YAAY,CAAC,CAAC;SACtC,KAAK,CAAC,UAAU,EAAE,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;SAC1C,KAAK,CAAC,QAAQ,EAAE,CAAC,YAAY,EAAE,KAAK,CAAC,CAAC;IAEzC;;;;OAIG;IACH,WAAW,EAAE,WAAW,CAAC;QACvB,mCAAmC;QACnC,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE;QACtB,gDAAgD;QAChD,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE;QACjB,+BAA+B;QAC/B,KAAK,EAAE,CAAC,CAAC,KAAK,EAAE;QAChB,wCAAwC;QACxC,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE;QACf,4CAA4C;QAC5C,MAAM,EAAE,CAAC,CAAC,MAAM,EAAE;QAClB,+BAA+B;QAC/B,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE;QACnB,sCAAsC;QACtC,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE;QACrB,gDAAgD;QAChD,WAAW,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;KACpC,CAAC;SACC,KAAK,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,CAAC;SAC9B,KAAK,CAAC,eAAe,EAAE,CAAC,YAAY,CAAC,CAAC;SACtC,KAAK,CAAC,cAAc,EAAE,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;IAE1C;;OAEG;IACH,aAAa,EAAE,WAAW,CAAC;QACzB,gCAAgC;QAChC,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE;QACrB,8BAA8B;QAC9B,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE;QACtB,wDAAwD;QACxD,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE;QACnB,6CAA6C;QAC7C,QAAQ,EAAE,CAAC,CAAC,MAAM,EAAE;KACrB,CAAC;SACC,KAAK,CAAC,cAAc,EAAE,CAAC,WAAW,CAAC,CAAC;SACpC,KAAK,CAAC,eAAe,EAAE,CAAC,YAAY,CAAC,CAAC;IAEzC;;OAEG;IACH,WAAW,EAAE,WAAW,CAAC;QACvB,mCAAmC;QACnC,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE;QACtB,kDAAkD;QAClD,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE;KACrB,CAAC,CAAC,KAAK,CAAC,eAAe,EAAE,CAAC,YAAY,CAAC,CAAC;CACjC,CAAC;AAUX;;;GAGG;AACH,MAAM,sBAAsB,GAAG,IAAI,GAAG,EAAU,CAAC;AAEjD;;;;;GAKG;AACH,MAAM,UAAU,uBAAuB,CAAC,SAAiB;IACvD,sBAAsB,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;AACxC,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,yBAAyB;IACvC,OAAO,KAAK,CAAC,IAAI,CAAC,sBAAsB,CAAC,CAAC;AAC5C,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,2BAA2B;IACzC,sBAAsB,CAAC,KAAK,EAAE,CAAC;AACjC,CAAC;AAcD;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,MAAM,UAAU,WAAW,CACzB,MAAS,EACT,MAA0B;IAE1B,+FAA+F;IAC/F,MAAM,WAAW,GAAG,MAAM,EAAE,WAAW,IAAI,QAAQ,CAAC;IACpD,MAAM,SAAS,GAAG,MAAM,WAAW,EAAE,CAAC;IAEtC,uDAAuD;IACvD,MAAM,cAAc,GAAG;QACrB,GAAG,MAAM;QACT,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;KACtC,CAAC;IAEF,4EAA4E;IAC5E,2DAA2D;IAC3D,OAAO,WAAW,CAAC,cAAc,CAAC,CAAC,KAAK,CAAC,SAAS,EAAE,CAAC,WAAW,CAAQ,CAAC,CAAC;AAC5E,CAAC"}
@@ -0,0 +1,240 @@
1
+ /**
2
+ * Sync factories for @fatagnus/dink-convex component.
3
+ *
4
+ * These factories create Convex function definitions using the user's
5
+ * _generated imports, keeping all complex logic in the package while
6
+ * reducing user boilerplate to ~5 lines per file.
7
+ *
8
+ * @module syncFactories
9
+ */
10
+ import { httpRouter, cronJobs } from "convex/server";
11
+ /**
12
+ * Convex mutation builder type from _generated/server.
13
+ */
14
+ type MutationBuilder = <Args extends Record<string, unknown>, Returns>(def: {
15
+ args: Record<string, unknown>;
16
+ returns: unknown;
17
+ handler: (ctx: MutationCtx, args: Args) => Promise<Returns>;
18
+ }) => unknown;
19
+ /**
20
+ * Convex query builder type from _generated/server.
21
+ */
22
+ type QueryBuilder = <Args extends Record<string, unknown>, Returns>(def: {
23
+ args: Record<string, unknown>;
24
+ returns: unknown;
25
+ handler: (ctx: QueryCtx, args: Args) => Promise<Returns>;
26
+ }) => unknown;
27
+ /**
28
+ * Convex internalAction builder type from _generated/server.
29
+ */
30
+ type InternalActionBuilder = <Args extends Record<string, unknown>, Returns>(def: {
31
+ args: Record<string, unknown>;
32
+ returns: unknown;
33
+ handler: (ctx: ActionCtx, args: Args) => Promise<Returns>;
34
+ }) => unknown;
35
+ /**
36
+ * Convex internalMutation builder type from _generated/server.
37
+ */
38
+ type InternalMutationBuilder = <Args extends Record<string, unknown>, Returns>(def: {
39
+ args: Record<string, unknown>;
40
+ returns: unknown;
41
+ handler: (ctx: MutationCtx, args: Args) => Promise<Returns>;
42
+ }) => unknown;
43
+ /**
44
+ * Convex internalQuery builder type from _generated/server.
45
+ */
46
+ type InternalQueryBuilder = <Args extends Record<string, unknown>, Returns>(def: {
47
+ args: Record<string, unknown>;
48
+ returns: unknown;
49
+ handler: (ctx: QueryCtx, args: Args) => Promise<Returns>;
50
+ }) => unknown;
51
+ /**
52
+ * Convex httpAction builder type from _generated/server.
53
+ */
54
+ type HttpActionBuilder = (handler: (ctx: ActionCtx, request: Request) => Promise<Response>) => unknown;
55
+ /**
56
+ * Mutation context type.
57
+ */
58
+ interface MutationCtx {
59
+ db: {
60
+ insert: (table: string, doc: Record<string, unknown>) => Promise<string>;
61
+ patch: (id: string, fields: Record<string, unknown>) => Promise<void>;
62
+ delete: (id: string) => Promise<void>;
63
+ query: (table: string) => {
64
+ order: (dir: "asc" | "desc") => {
65
+ first: () => Promise<Record<string, unknown> | null>;
66
+ };
67
+ withIndex: (indexName: string, queryFn: (q: {
68
+ eq: (field: string, value: unknown) => unknown;
69
+ }) => unknown) => {
70
+ first: () => Promise<Record<string, unknown> | null>;
71
+ collect: () => Promise<Array<Record<string, unknown>>>;
72
+ };
73
+ collect: () => Promise<Array<Record<string, unknown>>>;
74
+ };
75
+ };
76
+ scheduler: {
77
+ runAfter: (delay: number, fn: unknown, args: unknown) => Promise<unknown>;
78
+ };
79
+ }
80
+ /**
81
+ * Query context type.
82
+ */
83
+ interface QueryCtx {
84
+ db: MutationCtx["db"];
85
+ }
86
+ /**
87
+ * Action context type.
88
+ */
89
+ interface ActionCtx {
90
+ runQuery: (fn: unknown, args: unknown) => Promise<unknown>;
91
+ runMutation: (fn: unknown, args: unknown) => Promise<unknown>;
92
+ }
93
+ /**
94
+ * Input for createSyncMutations factory.
95
+ */
96
+ export interface SyncMutationsInput {
97
+ /** mutation builder from _generated/server */
98
+ mutation: MutationBuilder;
99
+ /** query builder from _generated/server */
100
+ query: QueryBuilder;
101
+ }
102
+ /**
103
+ * Output from createSyncMutations factory.
104
+ */
105
+ export interface SyncMutationsOutput {
106
+ applyDeltaFromEdge: unknown;
107
+ getDocumentState: unknown;
108
+ listDocuments: unknown;
109
+ listDocumentsPaginated: unknown;
110
+ }
111
+ /**
112
+ * Create sync mutations and queries.
113
+ *
114
+ * Returns the Convex function definitions for handling CRDT delta operations.
115
+ * The applyDeltaFromEdge mutation skips outbox queueing to prevent sync loops.
116
+ *
117
+ * @param input - Object containing mutation and query builders from _generated/server
118
+ * @returns Object with applyDeltaFromEdge, getDocumentState, listDocuments, listDocumentsPaginated
119
+ *
120
+ * @example
121
+ * ```typescript
122
+ * // convex/sync.ts (~5 lines!)
123
+ * import { createSyncMutations } from "@fatagnus/dink-convex";
124
+ * import { mutation, query } from "./_generated/server";
125
+ *
126
+ * export const { applyDeltaFromEdge, getDocumentState, listDocuments, listDocumentsPaginated } =
127
+ * createSyncMutations({ mutation, query });
128
+ * ```
129
+ */
130
+ export declare function createSyncMutations(input: SyncMutationsInput): SyncMutationsOutput;
131
+ /**
132
+ * Input for createOutboxProcessor factory.
133
+ */
134
+ export interface OutboxProcessorInput {
135
+ /** internalAction builder from _generated/server */
136
+ internalAction: InternalActionBuilder;
137
+ /** internalMutation builder from _generated/server */
138
+ internalMutation: InternalMutationBuilder;
139
+ /** internalQuery builder from _generated/server */
140
+ internalQuery: InternalQueryBuilder;
141
+ /** internal API from _generated/api */
142
+ internal: {
143
+ outboxProcessor: {
144
+ queryPendingItems: unknown;
145
+ updateItemStatus: unknown;
146
+ processOutboxBatch: unknown;
147
+ };
148
+ };
149
+ }
150
+ /**
151
+ * Output from createOutboxProcessor factory.
152
+ */
153
+ export interface OutboxProcessorOutput {
154
+ processOutboxBatch: unknown;
155
+ queryPendingItems: unknown;
156
+ updateItemStatus: unknown;
157
+ scheduleOutboxProcessing: unknown;
158
+ }
159
+ /**
160
+ * Create outbox processor functions.
161
+ *
162
+ * Returns Convex function definitions for processing the sync_outbox table
163
+ * and pushing deltas to dinkd.
164
+ *
165
+ * @param input - Object containing internalAction, internalMutation, internalQuery, and internal API
166
+ * @returns Object with processOutboxBatch, queryPendingItems, updateItemStatus, scheduleOutboxProcessing
167
+ *
168
+ * @example
169
+ * ```typescript
170
+ * // convex/outboxProcessor.ts (~5 lines!)
171
+ * import { createOutboxProcessor } from "@fatagnus/dink-convex";
172
+ * import { internalAction, internalMutation, internalQuery } from "./_generated/server";
173
+ * import { internal } from "./_generated/api";
174
+ *
175
+ * export const { processOutboxBatch, queryPendingItems, updateItemStatus, scheduleOutboxProcessing } =
176
+ * createOutboxProcessor({ internalAction, internalMutation, internalQuery, internal });
177
+ * ```
178
+ */
179
+ export declare function createOutboxProcessor(input: OutboxProcessorInput): OutboxProcessorOutput;
180
+ /**
181
+ * Input for createSyncHttpRouter factory.
182
+ */
183
+ export interface SyncHttpRouterInput {
184
+ /** httpAction builder from _generated/server */
185
+ httpAction: HttpActionBuilder;
186
+ /** api from _generated/api */
187
+ api: {
188
+ sync: {
189
+ applyDeltaFromEdge: unknown;
190
+ };
191
+ };
192
+ }
193
+ /**
194
+ * Create sync HTTP router with /dink/applyDelta endpoint.
195
+ *
196
+ * @param input - Object containing httpAction builder and api
197
+ * @returns HTTP router to export as default
198
+ *
199
+ * @example
200
+ * ```typescript
201
+ * // convex/http.ts (~5 lines!)
202
+ * import { createSyncHttpRouter } from "@fatagnus/dink-convex";
203
+ * import { httpAction } from "./_generated/server";
204
+ * import { api } from "./_generated/api";
205
+ *
206
+ * export default createSyncHttpRouter({ httpAction, api });
207
+ * ```
208
+ */
209
+ export declare function createSyncHttpRouter(input: SyncHttpRouterInput): ReturnType<typeof httpRouter>;
210
+ /**
211
+ * Input for createSyncCrons factory.
212
+ */
213
+ export interface SyncCronsInput {
214
+ /** internal API from _generated/api */
215
+ internal: {
216
+ outboxProcessor: {
217
+ processOutboxBatch: unknown;
218
+ };
219
+ };
220
+ /** Cron interval in minutes (default: 1) */
221
+ intervalMinutes?: number;
222
+ }
223
+ /**
224
+ * Create sync cron jobs for outbox processing.
225
+ *
226
+ * @param input - Object containing internal API
227
+ * @returns Cron jobs to export as default
228
+ *
229
+ * @example
230
+ * ```typescript
231
+ * // convex/crons.ts (~4 lines!)
232
+ * import { createSyncCrons } from "@fatagnus/dink-convex";
233
+ * import { internal } from "./_generated/api";
234
+ *
235
+ * export default createSyncCrons({ internal });
236
+ * ```
237
+ */
238
+ export declare function createSyncCrons(input: SyncCronsInput): ReturnType<typeof cronJobs>;
239
+ export {};
240
+ //# sourceMappingURL=syncFactories.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"syncFactories.d.ts","sourceRoot":"","sources":["../src/syncFactories.ts"],"names":[],"mappings":"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"}