@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
package/dist/http.js ADDED
@@ -0,0 +1,222 @@
1
+ /**
2
+ * HTTP endpoint utilities for dinkd communication.
3
+ *
4
+ * Provides helpers for the /api/dink/applyDelta HTTP endpoint
5
+ * that receives CRDT deltas from edge devices.
6
+ *
7
+ * @module http
8
+ */
9
+ import * as Y from "yjs";
10
+ /**
11
+ * Validate the Authorization header for applyDelta endpoint.
12
+ *
13
+ * Expects: Authorization: Bearer <appSyncKey>
14
+ *
15
+ * @param header - The Authorization header value
16
+ * @param expectedKey - The expected app sync key
17
+ * @returns Validation result
18
+ */
19
+ export function validateAuthHeader(header, expectedKey) {
20
+ if (!header || header.trim() === "") {
21
+ return { valid: false, error: "Missing Authorization header" };
22
+ }
23
+ if (!header.startsWith("Bearer ")) {
24
+ return { valid: false, error: "Authorization header must use Bearer scheme" };
25
+ }
26
+ const token = header.slice(7).trim();
27
+ if (!token || token !== expectedKey) {
28
+ return { valid: false, error: "Invalid authorization token" };
29
+ }
30
+ return { valid: true };
31
+ }
32
+ /**
33
+ * Parse and validate the getSnapshot query parameters.
34
+ *
35
+ * @param params - The query parameters
36
+ * @returns Parsed parameters result
37
+ */
38
+ export function parseGetSnapshotParams(params) {
39
+ if (!params || typeof params !== "object") {
40
+ return { success: false, error: "Invalid params: expected object" };
41
+ }
42
+ if (typeof params.collection !== "string" || !params.collection) {
43
+ return { success: false, error: "Invalid params: collection is required" };
44
+ }
45
+ if (typeof params.docId !== "string" || !params.docId) {
46
+ return { success: false, error: "Invalid params: docId is required" };
47
+ }
48
+ return {
49
+ success: true,
50
+ data: {
51
+ collection: params.collection,
52
+ docId: params.docId,
53
+ },
54
+ };
55
+ }
56
+ /**
57
+ * Parse and validate the listDocuments query parameters.
58
+ *
59
+ * @param params - The query parameters
60
+ * @returns Parsed parameters result
61
+ */
62
+ export function parseListDocumentsParams(params) {
63
+ if (!params || typeof params !== "object") {
64
+ return { success: false, error: "Invalid params: expected object" };
65
+ }
66
+ if (typeof params.collection !== "string" || !params.collection) {
67
+ return { success: false, error: "Invalid params: collection is required" };
68
+ }
69
+ const result = {
70
+ collection: params.collection,
71
+ };
72
+ if (params.cursor !== undefined) {
73
+ result.cursor = params.cursor;
74
+ }
75
+ if (params.limit !== undefined) {
76
+ result.limit = params.limit;
77
+ }
78
+ return {
79
+ success: true,
80
+ data: result,
81
+ };
82
+ }
83
+ /**
84
+ * Parse and validate the applyDelta request payload.
85
+ *
86
+ * @param payload - The raw request payload
87
+ * @returns Parsed payload result
88
+ */
89
+ export function parseApplyDeltaPayload(payload) {
90
+ if (!payload || typeof payload !== "object") {
91
+ return { success: false, error: "Invalid payload: expected object" };
92
+ }
93
+ if (typeof payload.collection !== "string" || !payload.collection) {
94
+ return { success: false, error: "Invalid payload: collection is required" };
95
+ }
96
+ if (typeof payload.docId !== "string" || !payload.docId) {
97
+ return { success: false, error: "Invalid payload: docId is required" };
98
+ }
99
+ if (!Array.isArray(payload.bytes)) {
100
+ return { success: false, error: "Invalid payload: bytes must be an array" };
101
+ }
102
+ // Convert number array to Uint8Array
103
+ const bytes = new Uint8Array(payload.bytes);
104
+ // Handle optional edgeId - only include if it's a string
105
+ const edgeId = typeof payload.edgeId === "string" ? payload.edgeId : undefined;
106
+ return {
107
+ success: true,
108
+ data: {
109
+ collection: payload.collection,
110
+ docId: payload.docId,
111
+ bytes,
112
+ edgeId,
113
+ },
114
+ };
115
+ }
116
+ /**
117
+ * Apply a CRDT delta to an existing document state.
118
+ *
119
+ * Merges the incoming delta with the existing state using Yjs CRDT semantics.
120
+ * This is the core merge function used by the HTTP endpoint.
121
+ *
122
+ * @param delta - The incoming CRDT delta (Yjs update)
123
+ * @param existingState - The existing document state, or null for new document
124
+ * @returns Merged state result with fromEdge flag for skipping outbox
125
+ */
126
+ export function applyDeltaToDocument(delta, existingState) {
127
+ try {
128
+ const ydoc = new Y.Doc();
129
+ // Apply existing state first if present
130
+ if (existingState) {
131
+ Y.applyUpdate(ydoc, existingState);
132
+ }
133
+ // Apply the incoming delta
134
+ Y.applyUpdate(ydoc, delta);
135
+ // Encode the merged state
136
+ const mergedState = Y.encodeStateAsUpdate(ydoc);
137
+ return {
138
+ success: true,
139
+ mergedState,
140
+ fromEdge: true, // Flag to indicate this came from edge, skip outbox
141
+ };
142
+ }
143
+ catch (error) {
144
+ const message = error instanceof Error ? error.message : "Unknown error";
145
+ return {
146
+ success: false,
147
+ error: `Failed to apply delta: ${message}`,
148
+ };
149
+ }
150
+ }
151
+ /**
152
+ * Create a JSON response with proper headers.
153
+ *
154
+ * @param data - Response data to serialize
155
+ * @param status - HTTP status code
156
+ * @returns Response object
157
+ */
158
+ export function createJsonResponse(data, status) {
159
+ return new Response(JSON.stringify(data), {
160
+ status,
161
+ headers: {
162
+ "Content-Type": "application/json",
163
+ },
164
+ });
165
+ }
166
+ /**
167
+ * Create an error response with proper headers.
168
+ *
169
+ * @param error - Error message
170
+ * @param status - HTTP status code
171
+ * @returns Response object
172
+ */
173
+ export function createErrorResponse(error, status) {
174
+ return createJsonResponse({ success: false, error }, status);
175
+ }
176
+ // Import serializeValue from crdt module to avoid duplication
177
+ import { serializeValue } from "./crdt.js";
178
+ /**
179
+ * Encode a document's data as a Yjs snapshot.
180
+ *
181
+ * Creates a Yjs document with all fields from the document data,
182
+ * compatible with the edge SDK's YjsDocument format.
183
+ *
184
+ * @param documentData - The document data to encode
185
+ * @returns Encoded snapshot result with Uint8Array bytes
186
+ *
187
+ * @example
188
+ * ```typescript
189
+ * const result = encodeDocumentAsSnapshot({
190
+ * _collection: "tasks",
191
+ * syncId: "sync-123",
192
+ * title: "My Task",
193
+ * completed: false,
194
+ * });
195
+ * ```
196
+ */
197
+ export function encodeDocumentAsSnapshot(documentData) {
198
+ try {
199
+ const ydoc = new Y.Doc();
200
+ const fields = ydoc.getMap("fields");
201
+ // Add all document fields
202
+ for (const [key, value] of Object.entries(documentData)) {
203
+ if (value !== undefined) {
204
+ fields.set(key, serializeValue(value));
205
+ }
206
+ }
207
+ // Encode the state as an update (snapshot)
208
+ const bytes = Y.encodeStateAsUpdate(ydoc);
209
+ return {
210
+ success: true,
211
+ bytes,
212
+ };
213
+ }
214
+ catch (error) {
215
+ const message = error instanceof Error ? error.message : "Unknown error";
216
+ return {
217
+ success: false,
218
+ error: `Failed to encode snapshot: ${message}`,
219
+ };
220
+ }
221
+ }
222
+ //# sourceMappingURL=http.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http.js","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,39 @@
1
+ /**
2
+ * HTTP router factory for simplified @fatagnus/dink-convex setup.
3
+ *
4
+ * @module httpFactory
5
+ */
6
+ import { httpRouter } from "convex/server";
7
+ /**
8
+ * API type expected by the HTTP router factory.
9
+ */
10
+ interface SyncApi {
11
+ sync: {
12
+ applyDeltaFromEdge: unknown;
13
+ };
14
+ }
15
+ /**
16
+ * Create a sync HTTP router with /dink/applyDelta endpoint.
17
+ *
18
+ * This factory creates an HTTP router pre-configured with the
19
+ * /dink/applyDelta endpoint for receiving CRDT deltas from edge devices.
20
+ *
21
+ * @param httpAction - The httpAction function from _generated/server
22
+ * @param api - The api from _generated/api
23
+ * @returns HTTP router to export as default
24
+ *
25
+ * @example
26
+ * ```typescript
27
+ * // convex/http.ts
28
+ * import { createSyncHttpRouter } from "@fatagnus/dink-convex";
29
+ * import { httpAction } from "./_generated/server";
30
+ * import { api } from "./_generated/api";
31
+ *
32
+ * export default createSyncHttpRouter(httpAction, api);
33
+ * ```
34
+ */
35
+ export declare function createSyncHttpRouter(httpAction: (handler: (ctx: {
36
+ runMutation: (fn: unknown, args: unknown) => Promise<unknown>;
37
+ }, request: Request) => Promise<Response>) => unknown, api: SyncApi): ReturnType<typeof httpRouter>;
38
+ export {};
39
+ //# sourceMappingURL=httpFactory.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"httpFactory.d.ts","sourceRoot":"","sources":["../src/httpFactory.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAE3C;;GAEG;AACH,UAAU,OAAO;IACf,IAAI,EAAE;QACJ,kBAAkB,EAAE,OAAO,CAAC;KAC7B,CAAC;CACH;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,oBAAoB,CAClC,UAAU,EAAE,CACV,OAAO,EAAE,CACP,GAAG,EAAE;IAAE,WAAW,EAAE,CAAC,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,KAAK,OAAO,CAAC,OAAO,CAAC,CAAA;CAAE,EACtE,OAAO,EAAE,OAAO,KACb,OAAO,CAAC,QAAQ,CAAC,KACnB,OAAO,EACZ,GAAG,EAAE,OAAO,GACX,UAAU,CAAC,OAAO,UAAU,CAAC,CAoI/B"}
@@ -0,0 +1,128 @@
1
+ /**
2
+ * HTTP router factory for simplified @fatagnus/dink-convex setup.
3
+ *
4
+ * @module httpFactory
5
+ */
6
+ import { httpRouter } from "convex/server";
7
+ /**
8
+ * Create a sync HTTP router with /dink/applyDelta endpoint.
9
+ *
10
+ * This factory creates an HTTP router pre-configured with the
11
+ * /dink/applyDelta endpoint for receiving CRDT deltas from edge devices.
12
+ *
13
+ * @param httpAction - The httpAction function from _generated/server
14
+ * @param api - The api from _generated/api
15
+ * @returns HTTP router to export as default
16
+ *
17
+ * @example
18
+ * ```typescript
19
+ * // convex/http.ts
20
+ * import { createSyncHttpRouter } from "@fatagnus/dink-convex";
21
+ * import { httpAction } from "./_generated/server";
22
+ * import { api } from "./_generated/api";
23
+ *
24
+ * export default createSyncHttpRouter(httpAction, api);
25
+ * ```
26
+ */
27
+ export function createSyncHttpRouter(httpAction, api) {
28
+ const http = httpRouter();
29
+ http.route({
30
+ path: "/dink/applyDelta",
31
+ method: "POST",
32
+ handler: httpAction(async (ctx, request) => {
33
+ // Validate Authorization header
34
+ const authHeader = request.headers.get("Authorization");
35
+ if (!authHeader || !authHeader.startsWith("Bearer ")) {
36
+ return new Response(JSON.stringify({
37
+ success: false,
38
+ error: "Missing or invalid Authorization header",
39
+ }), {
40
+ status: 401,
41
+ headers: { "Content-Type": "application/json" },
42
+ });
43
+ }
44
+ const token = authHeader.slice(7).trim();
45
+ if (!token) {
46
+ return new Response(JSON.stringify({
47
+ success: false,
48
+ error: "Missing authorization token",
49
+ }), {
50
+ status: 401,
51
+ headers: { "Content-Type": "application/json" },
52
+ });
53
+ }
54
+ // Parse request body
55
+ let payload;
56
+ try {
57
+ payload = await request.json();
58
+ }
59
+ catch {
60
+ return new Response(JSON.stringify({
61
+ success: false,
62
+ error: "Invalid JSON payload",
63
+ }), {
64
+ status: 400,
65
+ headers: { "Content-Type": "application/json" },
66
+ });
67
+ }
68
+ // Validate payload
69
+ if (!payload.collection || typeof payload.collection !== "string") {
70
+ return new Response(JSON.stringify({
71
+ success: false,
72
+ error: "Missing or invalid collection field",
73
+ }), {
74
+ status: 400,
75
+ headers: { "Content-Type": "application/json" },
76
+ });
77
+ }
78
+ if (!payload.docId || typeof payload.docId !== "string") {
79
+ return new Response(JSON.stringify({
80
+ success: false,
81
+ error: "Missing or invalid docId field",
82
+ }), {
83
+ status: 400,
84
+ headers: { "Content-Type": "application/json" },
85
+ });
86
+ }
87
+ if (!Array.isArray(payload.bytes)) {
88
+ return new Response(JSON.stringify({
89
+ success: false,
90
+ error: "Missing or invalid bytes field",
91
+ }), {
92
+ status: 400,
93
+ headers: { "Content-Type": "application/json" },
94
+ });
95
+ }
96
+ // Convert bytes array to ArrayBuffer for Convex
97
+ const bytes = new Uint8Array(payload.bytes).buffer;
98
+ try {
99
+ const result = (await ctx.runMutation(api.sync.applyDeltaFromEdge, {
100
+ collection: payload.collection,
101
+ docId: payload.docId,
102
+ bytes,
103
+ authToken: token,
104
+ edgeId: payload.edgeId,
105
+ }));
106
+ return new Response(JSON.stringify({
107
+ success: true,
108
+ seq: result.seq,
109
+ }), {
110
+ status: 200,
111
+ headers: { "Content-Type": "application/json" },
112
+ });
113
+ }
114
+ catch (error) {
115
+ const message = error instanceof Error ? error.message : "Unknown error";
116
+ return new Response(JSON.stringify({
117
+ success: false,
118
+ error: message,
119
+ }), {
120
+ status: 500,
121
+ headers: { "Content-Type": "application/json" },
122
+ });
123
+ }
124
+ }),
125
+ });
126
+ return http;
127
+ }
128
+ //# sourceMappingURL=httpFactory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"httpFactory.js","sourceRoot":"","sources":["../src/httpFactory.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAW3C;;;;;;;;;;;;;;;;;;;GAmBG;AACH,MAAM,UAAU,oBAAoB,CAClC,UAKY,EACZ,GAAY;IAEZ,MAAM,IAAI,GAAG,UAAU,EAAE,CAAC;IAE1B,IAAI,CAAC,KAAK,CAAC;QACT,IAAI,EAAE,kBAAkB;QACxB,MAAM,EAAE,MAAM;QACd,OAAO,EAAE,UAAU,CAAC,KAAK,EAAE,GAAG,EAAE,OAAO,EAAE,EAAE;YACzC,gCAAgC;YAChC,MAAM,UAAU,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC;YACxD,IAAI,CAAC,UAAU,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;gBACrD,OAAO,IAAI,QAAQ,CACjB,IAAI,CAAC,SAAS,CAAC;oBACb,OAAO,EAAE,KAAK;oBACd,KAAK,EAAE,yCAAyC;iBACjD,CAAC,EACF;oBACE,MAAM,EAAE,GAAG;oBACX,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;iBAChD,CACF,CAAC;YACJ,CAAC;YAED,MAAM,KAAK,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;YACzC,IAAI,CAAC,KAAK,EAAE,CAAC;gBACX,OAAO,IAAI,QAAQ,CACjB,IAAI,CAAC,SAAS,CAAC;oBACb,OAAO,EAAE,KAAK;oBACd,KAAK,EAAE,6BAA6B;iBACrC,CAAC,EACF;oBACE,MAAM,EAAE,GAAG;oBACX,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;iBAChD,CACF,CAAC;YACJ,CAAC;YAED,qBAAqB;YACrB,IAAI,OAAmF,CAAC;YACxF,IAAI,CAAC;gBACH,OAAO,GAAG,MAAM,OAAO,CAAC,IAAI,EAAoB,CAAC;YACnD,CAAC;YAAC,MAAM,CAAC;gBACP,OAAO,IAAI,QAAQ,CACjB,IAAI,CAAC,SAAS,CAAC;oBACb,OAAO,EAAE,KAAK;oBACd,KAAK,EAAE,sBAAsB;iBAC9B,CAAC,EACF;oBACE,MAAM,EAAE,GAAG;oBACX,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;iBAChD,CACF,CAAC;YACJ,CAAC;YAED,mBAAmB;YACnB,IAAI,CAAC,OAAO,CAAC,UAAU,IAAI,OAAO,OAAO,CAAC,UAAU,KAAK,QAAQ,EAAE,CAAC;gBAClE,OAAO,IAAI,QAAQ,CACjB,IAAI,CAAC,SAAS,CAAC;oBACb,OAAO,EAAE,KAAK;oBACd,KAAK,EAAE,qCAAqC;iBAC7C,CAAC,EACF;oBACE,MAAM,EAAE,GAAG;oBACX,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;iBAChD,CACF,CAAC;YACJ,CAAC;YAED,IAAI,CAAC,OAAO,CAAC,KAAK,IAAI,OAAO,OAAO,CAAC,KAAK,KAAK,QAAQ,EAAE,CAAC;gBACxD,OAAO,IAAI,QAAQ,CACjB,IAAI,CAAC,SAAS,CAAC;oBACb,OAAO,EAAE,KAAK;oBACd,KAAK,EAAE,gCAAgC;iBACxC,CAAC,EACF;oBACE,MAAM,EAAE,GAAG;oBACX,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;iBAChD,CACF,CAAC;YACJ,CAAC;YAED,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;gBAClC,OAAO,IAAI,QAAQ,CACjB,IAAI,CAAC,SAAS,CAAC;oBACb,OAAO,EAAE,KAAK;oBACd,KAAK,EAAE,gCAAgC;iBACxC,CAAC,EACF;oBACE,MAAM,EAAE,GAAG;oBACX,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;iBAChD,CACF,CAAC;YACJ,CAAC;YAED,gDAAgD;YAChD,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC;YAEnD,IAAI,CAAC;gBACH,MAAM,MAAM,GAAG,CAAC,MAAM,GAAG,CAAC,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,kBAAkB,EAAE;oBACjE,UAAU,EAAE,OAAO,CAAC,UAAU;oBAC9B,KAAK,EAAE,OAAO,CAAC,KAAK;oBACpB,KAAK;oBACL,SAAS,EAAE,KAAK;oBAChB,MAAM,EAAE,OAAO,CAAC,MAAM;iBACvB,CAAC,CAAoB,CAAC;gBAEvB,OAAO,IAAI,QAAQ,CACjB,IAAI,CAAC,SAAS,CAAC;oBACb,OAAO,EAAE,IAAI;oBACb,GAAG,EAAE,MAAM,CAAC,GAAG;iBAChB,CAAC,EACF;oBACE,MAAM,EAAE,GAAG;oBACX,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;iBAChD,CACF,CAAC;YACJ,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe,CAAC;gBACzE,OAAO,IAAI,QAAQ,CACjB,IAAI,CAAC,SAAS,CAAC;oBACb,OAAO,EAAE,KAAK;oBACd,KAAK,EAAE,OAAO;iBACf,CAAC,EACF;oBACE,MAAM,EAAE,GAAG;oBACX,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;iBAChD,CACF,CAAC;YACJ,CAAC;QACH,CAAC,CAAU;KACZ,CAAC,CAAC;IAEH,OAAO,IAAI,CAAC;AACd,CAAC"}
@@ -0,0 +1,68 @@
1
+ /**
2
+ * @fatagnus/dink-convex - Dink Convex Sync Component
3
+ *
4
+ * Provides bidirectional sync between Convex and edge devices using CRDT deltas.
5
+ * Minimal boilerplate - each user file is just ~5 lines!
6
+ *
7
+ * ## Quick Start
8
+ *
9
+ * 1. Define your schema with syncedTable:
10
+ * ```typescript
11
+ * // convex/schema.ts
12
+ * import { syncedTable, internalSyncSchema } from "@fatagnus/dink-convex";
13
+ * export default defineSchema({
14
+ * tasks: syncedTable({ title: v.string(), completed: v.boolean() }),
15
+ * ...internalSyncSchema,
16
+ * });
17
+ * ```
18
+ *
19
+ * 2. Create sync mutations (5 lines!):
20
+ * ```typescript
21
+ * // convex/sync.ts
22
+ * import { createSyncMutations } from "@fatagnus/dink-convex";
23
+ * import { mutation, query } from "./_generated/server";
24
+ *
25
+ * export const { applyDeltaFromEdge, getDocumentState, listDocuments, listDocumentsPaginated } =
26
+ * createSyncMutations({ mutation, query });
27
+ * ```
28
+ *
29
+ * 3. Create outbox processor (5 lines!):
30
+ * ```typescript
31
+ * // convex/outboxProcessor.ts
32
+ * import { createOutboxProcessor } from "@fatagnus/dink-convex";
33
+ * import { internalAction, internalMutation, internalQuery } from "./_generated/server";
34
+ * import { internal } from "./_generated/api";
35
+ *
36
+ * export const { processOutboxBatch, queryPendingItems, updateItemStatus, scheduleOutboxProcessing } =
37
+ * createOutboxProcessor({ internalAction, internalMutation, internalQuery, internal });
38
+ * ```
39
+ *
40
+ * 4. Create HTTP router (4 lines!):
41
+ * ```typescript
42
+ * // convex/http.ts
43
+ * import { createSyncHttpRouter } from "@fatagnus/dink-convex";
44
+ * import { httpAction } from "./_generated/server";
45
+ * import { api } from "./_generated/api";
46
+ *
47
+ * export default createSyncHttpRouter({ httpAction, api });
48
+ * ```
49
+ *
50
+ * 5. Create crons (4 lines!):
51
+ * ```typescript
52
+ * // convex/crons.ts
53
+ * import { createSyncCrons } from "@fatagnus/dink-convex";
54
+ * import { internal } from "./_generated/api";
55
+ *
56
+ * export default createSyncCrons({ internal });
57
+ * ```
58
+ *
59
+ * @packageDocumentation
60
+ */
61
+ export { syncedTable, internalSyncSchema, registerSyncedTableName, getRegisteredSyncedTables, clearRegisteredSyncedTables, } from "./schema.js";
62
+ export { generateInsertDelta, generateUpdateDelta, generateDeleteDelta, type DocumentData, } from "./crdt.js";
63
+ export { createSyncMutations, createOutboxProcessor, createSyncHttpRouter, createSyncCrons, type SyncMutationsInput, type SyncMutationsOutput, type OutboxProcessorInput, type OutboxProcessorOutput, type SyncHttpRouterInput, type SyncCronsInput, } from "./syncFactories.js";
64
+ export { createSyncMutation, type SyncCronsConfig } from "./factories.js";
65
+ export { syncTriggers, registerSyncedTable, isSyncedTable, getSyncConfig, createDeltaRecord, createOutboxRecord, createSyncTriggerHandler, createSyncTriggerHandlerAsync, createSyncTriggerHandlerWithScheduling, scheduleImmediateProcessing, getNextSeq, getNextSeqAsync, createSequenceCounter, checkSyncConfigEnabled, createSyncConfigChecker, createConfigAwareSyncTriggerHandler, createSyncTriggers, createSyncTriggersWithHandlers, customMutation, type SequenceCounter, } from "./triggers.js";
66
+ export declare const VERSION = "0.3.0";
67
+ export type { SyncedTableConfig } from "./schema.js";
68
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2DG;AAGH,OAAO,EACL,WAAW,EACX,kBAAkB,EAClB,uBAAuB,EACvB,yBAAyB,EACzB,2BAA2B,GAC5B,MAAM,aAAa,CAAC;AAGrB,OAAO,EACL,mBAAmB,EACnB,mBAAmB,EACnB,mBAAmB,EACnB,KAAK,YAAY,GAClB,MAAM,WAAW,CAAC;AAGnB,OAAO,EACL,mBAAmB,EACnB,qBAAqB,EACrB,oBAAoB,EACpB,eAAe,EACf,KAAK,kBAAkB,EACvB,KAAK,mBAAmB,EACxB,KAAK,oBAAoB,EACzB,KAAK,qBAAqB,EAC1B,KAAK,mBAAmB,EACxB,KAAK,cAAc,GACpB,MAAM,oBAAoB,CAAC;AAG5B,OAAO,EAAE,kBAAkB,EAAE,KAAK,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAG1E,OAAO,EACL,YAAY,EACZ,mBAAmB,EACnB,aAAa,EACb,aAAa,EACb,iBAAiB,EACjB,kBAAkB,EAClB,wBAAwB,EACxB,6BAA6B,EAC7B,sCAAsC,EACtC,2BAA2B,EAC3B,UAAU,EACV,eAAe,EACf,qBAAqB,EACrB,sBAAsB,EACtB,uBAAuB,EACvB,mCAAmC,EACnC,kBAAkB,EAClB,8BAA8B,EAC9B,cAAc,EACd,KAAK,eAAe,GACrB,MAAM,eAAe,CAAC;AAGvB,eAAO,MAAM,OAAO,UAAU,CAAC;AAG/B,YAAY,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,73 @@
1
+ /**
2
+ * @fatagnus/dink-convex - Dink Convex Sync Component
3
+ *
4
+ * Provides bidirectional sync between Convex and edge devices using CRDT deltas.
5
+ * Minimal boilerplate - each user file is just ~5 lines!
6
+ *
7
+ * ## Quick Start
8
+ *
9
+ * 1. Define your schema with syncedTable:
10
+ * ```typescript
11
+ * // convex/schema.ts
12
+ * import { syncedTable, internalSyncSchema } from "@fatagnus/dink-convex";
13
+ * export default defineSchema({
14
+ * tasks: syncedTable({ title: v.string(), completed: v.boolean() }),
15
+ * ...internalSyncSchema,
16
+ * });
17
+ * ```
18
+ *
19
+ * 2. Create sync mutations (5 lines!):
20
+ * ```typescript
21
+ * // convex/sync.ts
22
+ * import { createSyncMutations } from "@fatagnus/dink-convex";
23
+ * import { mutation, query } from "./_generated/server";
24
+ *
25
+ * export const { applyDeltaFromEdge, getDocumentState, listDocuments, listDocumentsPaginated } =
26
+ * createSyncMutations({ mutation, query });
27
+ * ```
28
+ *
29
+ * 3. Create outbox processor (5 lines!):
30
+ * ```typescript
31
+ * // convex/outboxProcessor.ts
32
+ * import { createOutboxProcessor } from "@fatagnus/dink-convex";
33
+ * import { internalAction, internalMutation, internalQuery } from "./_generated/server";
34
+ * import { internal } from "./_generated/api";
35
+ *
36
+ * export const { processOutboxBatch, queryPendingItems, updateItemStatus, scheduleOutboxProcessing } =
37
+ * createOutboxProcessor({ internalAction, internalMutation, internalQuery, internal });
38
+ * ```
39
+ *
40
+ * 4. Create HTTP router (4 lines!):
41
+ * ```typescript
42
+ * // convex/http.ts
43
+ * import { createSyncHttpRouter } from "@fatagnus/dink-convex";
44
+ * import { httpAction } from "./_generated/server";
45
+ * import { api } from "./_generated/api";
46
+ *
47
+ * export default createSyncHttpRouter({ httpAction, api });
48
+ * ```
49
+ *
50
+ * 5. Create crons (4 lines!):
51
+ * ```typescript
52
+ * // convex/crons.ts
53
+ * import { createSyncCrons } from "@fatagnus/dink-convex";
54
+ * import { internal } from "./_generated/api";
55
+ *
56
+ * export default createSyncCrons({ internal });
57
+ * ```
58
+ *
59
+ * @packageDocumentation
60
+ */
61
+ // Schema helpers
62
+ export { syncedTable, internalSyncSchema, registerSyncedTableName, getRegisteredSyncedTables, clearRegisteredSyncedTables, } from "./schema.js";
63
+ // CRDT utilities
64
+ export { generateInsertDelta, generateUpdateDelta, generateDeleteDelta, } from "./crdt.js";
65
+ // Factory functions for minimal boilerplate DX (object-based API - recommended)
66
+ export { createSyncMutations, createOutboxProcessor, createSyncHttpRouter, createSyncCrons, } from "./syncFactories.js";
67
+ // Sync mutation wrapper for user mutations (wraps with CRDT triggers)
68
+ export { createSyncMutation } from "./factories.js";
69
+ // Legacy triggers exports (for advanced usage)
70
+ export { syncTriggers, registerSyncedTable, isSyncedTable, getSyncConfig, createDeltaRecord, createOutboxRecord, createSyncTriggerHandler, createSyncTriggerHandlerAsync, createSyncTriggerHandlerWithScheduling, scheduleImmediateProcessing, getNextSeq, getNextSeqAsync, createSequenceCounter, checkSyncConfigEnabled, createSyncConfigChecker, createConfigAwareSyncTriggerHandler, createSyncTriggers, createSyncTriggersWithHandlers, customMutation, } from "./triggers.js";
71
+ // Component version
72
+ export const VERSION = "0.3.0";
73
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2DG;AAEH,iBAAiB;AACjB,OAAO,EACL,WAAW,EACX,kBAAkB,EAClB,uBAAuB,EACvB,yBAAyB,EACzB,2BAA2B,GAC5B,MAAM,aAAa,CAAC;AAErB,iBAAiB;AACjB,OAAO,EACL,mBAAmB,EACnB,mBAAmB,EACnB,mBAAmB,GAEpB,MAAM,WAAW,CAAC;AAEnB,gFAAgF;AAChF,OAAO,EACL,mBAAmB,EACnB,qBAAqB,EACrB,oBAAoB,EACpB,eAAe,GAOhB,MAAM,oBAAoB,CAAC;AAE5B,sEAAsE;AACtE,OAAO,EAAE,kBAAkB,EAAwB,MAAM,gBAAgB,CAAC;AAE1E,+CAA+C;AAC/C,OAAO,EACL,YAAY,EACZ,mBAAmB,EACnB,aAAa,EACb,aAAa,EACb,iBAAiB,EACjB,kBAAkB,EAClB,wBAAwB,EACxB,6BAA6B,EAC7B,sCAAsC,EACtC,2BAA2B,EAC3B,UAAU,EACV,eAAe,EACf,qBAAqB,EACrB,sBAAsB,EACtB,uBAAuB,EACvB,mCAAmC,EACnC,kBAAkB,EAClB,8BAA8B,EAC9B,cAAc,GAEf,MAAM,eAAe,CAAC;AAEvB,oBAAoB;AACpB,MAAM,CAAC,MAAM,OAAO,GAAG,OAAO,CAAC"}