@syncular/core 0.0.1 → 0.0.2-126

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 (70) hide show
  1. package/README.md +24 -0
  2. package/dist/blobs.d.ts +9 -4
  3. package/dist/blobs.d.ts.map +1 -1
  4. package/dist/blobs.js +0 -12
  5. package/dist/blobs.js.map +1 -1
  6. package/dist/column-codecs.d.ts +55 -0
  7. package/dist/column-codecs.d.ts.map +1 -0
  8. package/dist/column-codecs.js +124 -0
  9. package/dist/column-codecs.js.map +1 -0
  10. package/dist/index.d.ts +11 -9
  11. package/dist/index.d.ts.map +1 -1
  12. package/dist/index.js +21 -7
  13. package/dist/index.js.map +1 -1
  14. package/dist/kysely-serialize.d.ts +1 -1
  15. package/dist/kysely-serialize.d.ts.map +1 -1
  16. package/dist/logger.d.ts +7 -32
  17. package/dist/logger.d.ts.map +1 -1
  18. package/dist/logger.js +6 -40
  19. package/dist/logger.js.map +1 -1
  20. package/dist/proxy/types.d.ts +0 -9
  21. package/dist/proxy/types.d.ts.map +1 -1
  22. package/dist/schemas/index.js +3 -3
  23. package/dist/schemas/sync.d.ts +120 -6
  24. package/dist/schemas/sync.d.ts.map +1 -1
  25. package/dist/schemas/sync.js +17 -3
  26. package/dist/schemas/sync.js.map +1 -1
  27. package/dist/scopes/index.d.ts +39 -64
  28. package/dist/scopes/index.d.ts.map +1 -1
  29. package/dist/scopes/index.js +9 -154
  30. package/dist/scopes/index.js.map +1 -1
  31. package/dist/snapshot-chunks.d.ts +26 -0
  32. package/dist/snapshot-chunks.d.ts.map +1 -0
  33. package/dist/snapshot-chunks.js +89 -0
  34. package/dist/snapshot-chunks.js.map +1 -0
  35. package/dist/telemetry.d.ts +114 -0
  36. package/dist/telemetry.d.ts.map +1 -0
  37. package/dist/telemetry.js +113 -0
  38. package/dist/telemetry.js.map +1 -0
  39. package/dist/types.d.ts +12 -9
  40. package/dist/types.d.ts.map +1 -1
  41. package/dist/types.js.map +1 -1
  42. package/dist/utils/id.d.ts +2 -0
  43. package/dist/utils/id.d.ts.map +1 -0
  44. package/dist/utils/id.js +8 -0
  45. package/dist/utils/id.js.map +1 -0
  46. package/dist/utils/index.d.ts +3 -0
  47. package/dist/utils/index.d.ts.map +1 -0
  48. package/dist/utils/index.js +3 -0
  49. package/dist/utils/index.js.map +1 -0
  50. package/dist/utils/object.d.ts +2 -0
  51. package/dist/utils/object.d.ts.map +1 -0
  52. package/dist/utils/object.js +4 -0
  53. package/dist/utils/object.js.map +1 -0
  54. package/package.json +28 -8
  55. package/src/__tests__/telemetry.test.ts +170 -0
  56. package/src/__tests__/utils.test.ts +27 -0
  57. package/src/blobs.ts +15 -14
  58. package/src/column-codecs.ts +228 -0
  59. package/src/index.ts +15 -41
  60. package/src/kysely-serialize.ts +1 -1
  61. package/src/logger.ts +10 -68
  62. package/src/proxy/types.ts +0 -10
  63. package/src/schemas/sync.ts +27 -3
  64. package/src/scopes/index.ts +72 -200
  65. package/src/snapshot-chunks.ts +112 -0
  66. package/src/telemetry.ts +238 -0
  67. package/src/types.ts +14 -18
  68. package/src/utils/id.ts +7 -0
  69. package/src/utils/index.ts +2 -0
  70. package/src/utils/object.ts +3 -0
@@ -0,0 +1,238 @@
1
+ /**
2
+ * @syncular/core - Runtime telemetry abstraction
3
+ *
4
+ * Provides vendor-neutral logging, tracing, and metrics interfaces so
5
+ * Syncular libraries can emit telemetry without coupling to a specific SDK.
6
+ */
7
+
8
+ /**
9
+ * Supported log levels.
10
+ */
11
+ export type SyncTelemetryLevel =
12
+ | 'trace'
13
+ | 'debug'
14
+ | 'info'
15
+ | 'warn'
16
+ | 'error'
17
+ | 'fatal';
18
+
19
+ /**
20
+ * Primitive attribute value used by traces and metrics.
21
+ */
22
+ export type SyncTelemetryAttributeValue = string | number | boolean;
23
+
24
+ /**
25
+ * Attribute bag used by traces and metrics.
26
+ */
27
+ export type SyncTelemetryAttributes = Record<
28
+ string,
29
+ SyncTelemetryAttributeValue
30
+ >;
31
+
32
+ /**
33
+ * Structured sync log event.
34
+ */
35
+ export interface SyncTelemetryEvent {
36
+ event: string;
37
+ level?: SyncTelemetryLevel;
38
+ userId?: string;
39
+ durationMs?: number;
40
+ rowCount?: number;
41
+ resetRequired?: boolean;
42
+ error?: string;
43
+ [key: string]: unknown;
44
+ }
45
+
46
+ /**
47
+ * Span creation options.
48
+ */
49
+ export interface SyncSpanOptions {
50
+ name: string;
51
+ op?: string;
52
+ attributes?: SyncTelemetryAttributes;
53
+ }
54
+
55
+ /**
56
+ * Span API exposed to Syncular internals.
57
+ */
58
+ export interface SyncSpan {
59
+ setAttribute(name: string, value: SyncTelemetryAttributeValue): void;
60
+ setAttributes(attributes: SyncTelemetryAttributes): void;
61
+ setStatus(status: 'ok' | 'error'): void;
62
+ }
63
+
64
+ /**
65
+ * Tracing interface.
66
+ */
67
+ export interface SyncTracer {
68
+ startSpan<T>(options: SyncSpanOptions, callback: (span: SyncSpan) => T): T;
69
+ }
70
+
71
+ /**
72
+ * Metric record options.
73
+ */
74
+ export interface SyncMetricOptions {
75
+ attributes?: SyncTelemetryAttributes;
76
+ unit?: string;
77
+ }
78
+
79
+ /**
80
+ * Metrics interface.
81
+ */
82
+ export interface SyncMetrics {
83
+ count(name: string, value?: number, options?: SyncMetricOptions): void;
84
+ gauge(name: string, value: number, options?: SyncMetricOptions): void;
85
+ distribution(name: string, value: number, options?: SyncMetricOptions): void;
86
+ }
87
+
88
+ /**
89
+ * Unified telemetry interface.
90
+ */
91
+ export interface SyncTelemetry {
92
+ log(event: SyncTelemetryEvent): void;
93
+ tracer: SyncTracer;
94
+ metrics: SyncMetrics;
95
+ captureException(error: unknown, context?: Record<string, unknown>): void;
96
+ }
97
+
98
+ const noopSpan: SyncSpan = {
99
+ setAttribute() {},
100
+ setAttributes() {},
101
+ setStatus() {},
102
+ };
103
+
104
+ const noopTracer: SyncTracer = {
105
+ startSpan(_options, callback) {
106
+ return callback(noopSpan);
107
+ },
108
+ };
109
+
110
+ const noopMetrics: SyncMetrics = {
111
+ count() {},
112
+ gauge() {},
113
+ distribution() {},
114
+ };
115
+
116
+ function createConsoleLogger(): (event: SyncTelemetryEvent) => void {
117
+ const isNode =
118
+ typeof globalThis !== 'undefined' &&
119
+ typeof globalThis.setImmediate === 'function';
120
+
121
+ const defer = isNode
122
+ ? (fn: () => void) => globalThis.setImmediate(fn)
123
+ : (fn: () => void) => setTimeout(fn, 0);
124
+
125
+ return (event: SyncTelemetryEvent) => {
126
+ defer(() => {
127
+ const level = event.level ?? (event.error ? 'error' : 'info');
128
+ const payload = {
129
+ timestamp: new Date().toISOString(),
130
+ level,
131
+ ...event,
132
+ };
133
+ console.log(JSON.stringify(payload));
134
+ });
135
+ };
136
+ }
137
+
138
+ /**
139
+ * Create console-backed default telemetry (logs only; no-op tracing/metrics).
140
+ */
141
+ export function createDefaultSyncTelemetry(): SyncTelemetry {
142
+ const logger = createConsoleLogger();
143
+ return {
144
+ log(event) {
145
+ logger(event);
146
+ },
147
+ tracer: noopTracer,
148
+ metrics: noopMetrics,
149
+ captureException(error, context) {
150
+ const message =
151
+ error instanceof Error
152
+ ? error.message
153
+ : `Unknown error: ${String(error)}`;
154
+ logger({
155
+ event: 'sync.exception',
156
+ level: 'error',
157
+ error: message,
158
+ ...(context ?? {}),
159
+ });
160
+ },
161
+ };
162
+ }
163
+
164
+ let activeSyncTelemetry: SyncTelemetry = createDefaultSyncTelemetry();
165
+
166
+ /**
167
+ * Get currently configured telemetry backend.
168
+ */
169
+ export function getSyncTelemetry(): SyncTelemetry {
170
+ return activeSyncTelemetry;
171
+ }
172
+
173
+ /**
174
+ * Replace active telemetry backend.
175
+ */
176
+ export function configureSyncTelemetry(telemetry: SyncTelemetry): void {
177
+ activeSyncTelemetry = telemetry;
178
+ }
179
+
180
+ /**
181
+ * Reset telemetry backend to default console implementation.
182
+ */
183
+ export function resetSyncTelemetry(): void {
184
+ activeSyncTelemetry = createDefaultSyncTelemetry();
185
+ }
186
+
187
+ /**
188
+ * Capture an exception through the active telemetry backend.
189
+ */
190
+ export function captureSyncException(
191
+ error: unknown,
192
+ context?: Record<string, unknown>
193
+ ): void {
194
+ activeSyncTelemetry.captureException(error, context);
195
+ }
196
+
197
+ /**
198
+ * Start a span through the active telemetry backend.
199
+ */
200
+ export function startSyncSpan<T>(
201
+ options: SyncSpanOptions,
202
+ callback: (span: SyncSpan) => T
203
+ ): T {
204
+ return activeSyncTelemetry.tracer.startSpan(options, callback);
205
+ }
206
+
207
+ /**
208
+ * Record a counter metric through the active telemetry backend.
209
+ */
210
+ export function countSyncMetric(
211
+ name: string,
212
+ value?: number,
213
+ options?: SyncMetricOptions
214
+ ): void {
215
+ activeSyncTelemetry.metrics.count(name, value, options);
216
+ }
217
+
218
+ /**
219
+ * Record a gauge metric through the active telemetry backend.
220
+ */
221
+ export function gaugeSyncMetric(
222
+ name: string,
223
+ value: number,
224
+ options?: SyncMetricOptions
225
+ ): void {
226
+ activeSyncTelemetry.metrics.gauge(name, value, options);
227
+ }
228
+
229
+ /**
230
+ * Record a distribution metric through the active telemetry backend.
231
+ */
232
+ export function distributionSyncMetric(
233
+ name: string,
234
+ value: number,
235
+ options?: SyncMetricOptions
236
+ ): void {
237
+ activeSyncTelemetry.metrics.distribution(name, value, options);
238
+ }
package/src/types.ts CHANGED
@@ -5,12 +5,7 @@
5
5
  * Protocol types (SyncOp, SyncPushRequest, etc.) live in ./schemas/sync.ts
6
6
  */
7
7
 
8
- import type {
9
- SyncPullRequest,
10
- SyncPullResponse,
11
- SyncPushRequest,
12
- SyncPushResponse,
13
- } from './schemas/sync';
8
+ import type { SyncCombinedRequest, SyncCombinedResponse } from './schemas/sync';
14
9
 
15
10
  // ============================================================================
16
11
  // Conflict Detection Types
@@ -126,30 +121,31 @@ export interface SyncTransportBlobs {
126
121
  */
127
122
  export interface SyncTransport {
128
123
  /**
129
- * Pull commits/snapshots from the server.
124
+ * Combined push+pull in a single round-trip.
130
125
  */
131
- pull(
132
- request: SyncPullRequest,
126
+ sync(
127
+ request: SyncCombinedRequest,
133
128
  options?: SyncTransportOptions
134
- ): Promise<SyncPullResponse>;
129
+ ): Promise<SyncCombinedResponse>;
135
130
 
136
131
  /**
137
- * Push a client commit to the server.
132
+ * Download an encoded bootstrap snapshot chunk.
138
133
  */
139
- push(
140
- request: SyncPushRequest,
134
+ fetchSnapshotChunk(
135
+ request: { chunkId: string },
141
136
  options?: SyncTransportOptions
142
- ): Promise<SyncPushResponse>;
137
+ ): Promise<Uint8Array>;
143
138
 
144
139
  /**
145
- * Download an encoded bootstrap snapshot chunk.
140
+ * Optional streaming snapshot chunk download.
146
141
  *
147
- * The server must validate auth for the chunk.
142
+ * When implemented, clients can decode and apply large bootstrap chunks
143
+ * incrementally without materializing the entire chunk in memory.
148
144
  */
149
- fetchSnapshotChunk(
145
+ fetchSnapshotChunkStream?(
150
146
  request: { chunkId: string },
151
147
  options?: SyncTransportOptions
152
- ): Promise<Uint8Array>;
148
+ ): Promise<ReadableStream<Uint8Array>>;
153
149
 
154
150
  /**
155
151
  * Optional blob operations.
@@ -0,0 +1,7 @@
1
+ export function randomId(): string {
2
+ const cryptoObj = globalThis.crypto;
3
+ if (cryptoObj && typeof cryptoObj.randomUUID === 'function') {
4
+ return cryptoObj.randomUUID();
5
+ }
6
+ return `${Date.now()}-${Math.random().toString(16).slice(2)}`;
7
+ }
@@ -0,0 +1,2 @@
1
+ export * from './id';
2
+ export * from './object';
@@ -0,0 +1,3 @@
1
+ export function isRecord(value: unknown): value is Record<string, unknown> {
2
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
3
+ }