@syncular/core 0.1.2 → 0.2.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 (231) hide show
  1. package/README.md +5 -19
  2. package/dist/blob-ref.d.ts +21 -0
  3. package/dist/blob-ref.js +97 -0
  4. package/dist/bytes.d.ts +45 -0
  5. package/dist/bytes.js +242 -0
  6. package/dist/canonical-json.d.ts +10 -0
  7. package/dist/canonical-json.js +18 -0
  8. package/dist/constants.d.ts +5 -0
  9. package/dist/constants.js +5 -0
  10. package/dist/crypto.d.ts +60 -0
  11. package/dist/crypto.js +251 -0
  12. package/dist/errors.d.ts +14 -0
  13. package/dist/errors.js +17 -0
  14. package/dist/frames.d.ts +24 -0
  15. package/dist/frames.js +42 -0
  16. package/dist/index.d.ts +18 -26
  17. package/dist/index.js +18 -43
  18. package/dist/message.d.ts +157 -0
  19. package/dist/message.js +786 -0
  20. package/dist/realtime.d.ts +92 -0
  21. package/dist/realtime.js +222 -0
  22. package/dist/render.d.ts +22 -0
  23. package/dist/render.js +273 -0
  24. package/dist/row-codec.d.ts +39 -0
  25. package/dist/row-codec.js +176 -0
  26. package/dist/segment.d.ts +18 -0
  27. package/dist/segment.js +105 -0
  28. package/dist/stream.d.ts +20 -0
  29. package/dist/stream.js +102 -0
  30. package/package.json +7 -59
  31. package/src/blob-ref.ts +135 -0
  32. package/src/bytes.ts +301 -0
  33. package/src/canonical-json.ts +20 -0
  34. package/src/constants.ts +7 -0
  35. package/src/crypto.ts +335 -0
  36. package/src/errors.ts +18 -0
  37. package/src/frames.ts +47 -0
  38. package/src/index.ts +18 -43
  39. package/src/message.ts +1024 -0
  40. package/src/realtime.ts +340 -0
  41. package/src/render.ts +325 -0
  42. package/src/row-codec.ts +265 -0
  43. package/src/segment.ts +150 -0
  44. package/src/stream.ts +128 -0
  45. package/dist/blobs.d.ts +0 -153
  46. package/dist/blobs.d.ts.map +0 -1
  47. package/dist/blobs.js +0 -47
  48. package/dist/blobs.js.map +0 -1
  49. package/dist/column-codecs.d.ts +0 -59
  50. package/dist/column-codecs.d.ts.map +0 -1
  51. package/dist/column-codecs.js +0 -159
  52. package/dist/column-codecs.js.map +0 -1
  53. package/dist/conflict.d.ts +0 -22
  54. package/dist/conflict.d.ts.map +0 -1
  55. package/dist/conflict.js +0 -81
  56. package/dist/conflict.js.map +0 -1
  57. package/dist/database.d.ts +0 -7
  58. package/dist/database.d.ts.map +0 -1
  59. package/dist/database.js +0 -14
  60. package/dist/database.js.map +0 -1
  61. package/dist/dialect.d.ts +0 -7
  62. package/dist/dialect.d.ts.map +0 -1
  63. package/dist/dialect.js +0 -2
  64. package/dist/dialect.js.map +0 -1
  65. package/dist/error-responses.d.ts +0 -393
  66. package/dist/error-responses.d.ts.map +0 -1
  67. package/dist/error-responses.js +0 -393
  68. package/dist/error-responses.js.map +0 -1
  69. package/dist/http/api-client.d.ts +0 -11
  70. package/dist/http/api-client.d.ts.map +0 -1
  71. package/dist/http/api-client.js +0 -30
  72. package/dist/http/api-client.js.map +0 -1
  73. package/dist/http/blob.d.ts +0 -6
  74. package/dist/http/blob.d.ts.map +0 -1
  75. package/dist/http/blob.js +0 -46
  76. package/dist/http/blob.js.map +0 -1
  77. package/dist/http/generated/api.d.ts +0 -5241
  78. package/dist/http/generated/api.d.ts.map +0 -1
  79. package/dist/http/generated/api.js +0 -6
  80. package/dist/http/generated/api.js.map +0 -1
  81. package/dist/http/index.d.ts +0 -18
  82. package/dist/http/index.d.ts.map +0 -1
  83. package/dist/http/index.js +0 -212
  84. package/dist/http/index.js.map +0 -1
  85. package/dist/http/shared.d.ts +0 -45
  86. package/dist/http/shared.d.ts.map +0 -1
  87. package/dist/http/shared.js +0 -122
  88. package/dist/http/shared.js.map +0 -1
  89. package/dist/http/transport-client.d.ts +0 -31
  90. package/dist/http/transport-client.d.ts.map +0 -1
  91. package/dist/http/transport-client.js +0 -193
  92. package/dist/http/transport-client.js.map +0 -1
  93. package/dist/index.d.ts.map +0 -1
  94. package/dist/index.js.map +0 -1
  95. package/dist/kysely-column-codecs.d.ts +0 -31
  96. package/dist/kysely-column-codecs.d.ts.map +0 -1
  97. package/dist/kysely-column-codecs.js +0 -452
  98. package/dist/kysely-column-codecs.js.map +0 -1
  99. package/dist/logger.d.ts +0 -29
  100. package/dist/logger.d.ts.map +0 -1
  101. package/dist/logger.js +0 -26
  102. package/dist/logger.js.map +0 -1
  103. package/dist/proxy/index.d.ts +0 -54
  104. package/dist/proxy/index.d.ts.map +0 -1
  105. package/dist/proxy/index.js +0 -7
  106. package/dist/proxy/index.js.map +0 -1
  107. package/dist/schemas/blobs.d.ts +0 -82
  108. package/dist/schemas/blobs.d.ts.map +0 -1
  109. package/dist/schemas/blobs.js +0 -64
  110. package/dist/schemas/blobs.js.map +0 -1
  111. package/dist/schemas/common.d.ts +0 -100
  112. package/dist/schemas/common.d.ts.map +0 -1
  113. package/dist/schemas/common.js +0 -64
  114. package/dist/schemas/common.js.map +0 -1
  115. package/dist/schemas/index.d.ts +0 -7
  116. package/dist/schemas/index.d.ts.map +0 -1
  117. package/dist/schemas/index.js +0 -7
  118. package/dist/schemas/index.js.map +0 -1
  119. package/dist/schemas/sync.d.ts +0 -1065
  120. package/dist/schemas/sync.d.ts.map +0 -1
  121. package/dist/schemas/sync.js +0 -361
  122. package/dist/schemas/sync.js.map +0 -1
  123. package/dist/scopes/index.d.ts +0 -113
  124. package/dist/scopes/index.d.ts.map +0 -1
  125. package/dist/scopes/index.js +0 -103
  126. package/dist/scopes/index.js.map +0 -1
  127. package/dist/sentry.d.ts +0 -31
  128. package/dist/sentry.d.ts.map +0 -1
  129. package/dist/sentry.js +0 -147
  130. package/dist/sentry.js.map +0 -1
  131. package/dist/snapshot-chunks.d.ts +0 -110
  132. package/dist/snapshot-chunks.d.ts.map +0 -1
  133. package/dist/snapshot-chunks.js +0 -682
  134. package/dist/snapshot-chunks.js.map +0 -1
  135. package/dist/sync-packs.d.ts +0 -33
  136. package/dist/sync-packs.d.ts.map +0 -1
  137. package/dist/sync-packs.js +0 -821
  138. package/dist/sync-packs.js.map +0 -1
  139. package/dist/telemetry.d.ts +0 -114
  140. package/dist/telemetry.d.ts.map +0 -1
  141. package/dist/telemetry.js +0 -114
  142. package/dist/telemetry.js.map +0 -1
  143. package/dist/transforms.d.ts +0 -146
  144. package/dist/transforms.d.ts.map +0 -1
  145. package/dist/transforms.js +0 -155
  146. package/dist/transforms.js.map +0 -1
  147. package/dist/types.d.ts +0 -210
  148. package/dist/types.d.ts.map +0 -1
  149. package/dist/types.js +0 -20
  150. package/dist/types.js.map +0 -1
  151. package/dist/utils/bytes.d.ts +0 -4
  152. package/dist/utils/bytes.d.ts.map +0 -1
  153. package/dist/utils/bytes.js +0 -66
  154. package/dist/utils/bytes.js.map +0 -1
  155. package/dist/utils/compression.d.ts +0 -22
  156. package/dist/utils/compression.d.ts.map +0 -1
  157. package/dist/utils/compression.js +0 -129
  158. package/dist/utils/compression.js.map +0 -1
  159. package/dist/utils/crypto.d.ts +0 -13
  160. package/dist/utils/crypto.d.ts.map +0 -1
  161. package/dist/utils/crypto.js +0 -107
  162. package/dist/utils/crypto.js.map +0 -1
  163. package/dist/utils/id.d.ts +0 -3
  164. package/dist/utils/id.d.ts.map +0 -1
  165. package/dist/utils/id.js +0 -25
  166. package/dist/utils/id.js.map +0 -1
  167. package/dist/utils/index.d.ts +0 -10
  168. package/dist/utils/index.d.ts.map +0 -1
  169. package/dist/utils/index.js +0 -10
  170. package/dist/utils/index.js.map +0 -1
  171. package/dist/utils/internal-runtime.d.ts +0 -16
  172. package/dist/utils/internal-runtime.d.ts.map +0 -1
  173. package/dist/utils/internal-runtime.js +0 -14
  174. package/dist/utils/internal-runtime.js.map +0 -1
  175. package/dist/utils/object.d.ts +0 -2
  176. package/dist/utils/object.d.ts.map +0 -1
  177. package/dist/utils/object.js +0 -4
  178. package/dist/utils/object.js.map +0 -1
  179. package/dist/utils/realtime-change-index.d.ts +0 -9
  180. package/dist/utils/realtime-change-index.d.ts.map +0 -1
  181. package/dist/utils/realtime-change-index.js +0 -36
  182. package/dist/utils/realtime-change-index.js.map +0 -1
  183. package/dist/utils/realtime-connection-registry.d.ts +0 -40
  184. package/dist/utils/realtime-connection-registry.d.ts.map +0 -1
  185. package/dist/utils/realtime-connection-registry.js +0 -237
  186. package/dist/utils/realtime-connection-registry.js.map +0 -1
  187. package/dist/utils/table-registry.d.ts +0 -10
  188. package/dist/utils/table-registry.d.ts.map +0 -1
  189. package/dist/utils/table-registry.js +0 -54
  190. package/dist/utils/table-registry.js.map +0 -1
  191. package/dist/utils/url.d.ts +0 -3
  192. package/dist/utils/url.d.ts.map +0 -1
  193. package/dist/utils/url.js +0 -20
  194. package/dist/utils/url.js.map +0 -1
  195. package/src/blobs.ts +0 -219
  196. package/src/column-codecs.ts +0 -262
  197. package/src/conflict.ts +0 -92
  198. package/src/database.ts +0 -21
  199. package/src/dialect.ts +0 -8
  200. package/src/error-responses.ts +0 -423
  201. package/src/http/api-client.ts +0 -39
  202. package/src/http/blob.ts +0 -95
  203. package/src/http/generated/api.ts +0 -5241
  204. package/src/http/index.ts +0 -365
  205. package/src/http/shared.ts +0 -220
  206. package/src/http/transport-client.ts +0 -314
  207. package/src/kysely-column-codecs.ts +0 -585
  208. package/src/logger.ts +0 -38
  209. package/src/proxy/index.ts +0 -57
  210. package/src/schemas/blobs.ts +0 -110
  211. package/src/schemas/common.ts +0 -89
  212. package/src/schemas/index.ts +0 -7
  213. package/src/schemas/sync.ts +0 -517
  214. package/src/scopes/index.ts +0 -237
  215. package/src/sentry.ts +0 -200
  216. package/src/snapshot-chunks.ts +0 -964
  217. package/src/sync-packs.ts +0 -1096
  218. package/src/telemetry.ts +0 -243
  219. package/src/transforms.ts +0 -256
  220. package/src/types.ts +0 -267
  221. package/src/utils/bytes.ts +0 -75
  222. package/src/utils/compression.ts +0 -187
  223. package/src/utils/crypto.ts +0 -146
  224. package/src/utils/id.ts +0 -34
  225. package/src/utils/index.ts +0 -9
  226. package/src/utils/internal-runtime.ts +0 -39
  227. package/src/utils/object.ts +0 -3
  228. package/src/utils/realtime-change-index.ts +0 -44
  229. package/src/utils/realtime-connection-registry.ts +0 -286
  230. package/src/utils/table-registry.ts +0 -67
  231. package/src/utils/url.ts +0 -29
@@ -1,237 +0,0 @@
1
- /**
2
- * @syncular/core - Scope types, patterns, and utilities
3
- *
4
- * Scope patterns define how data is partitioned for sync.
5
- * Scopes are stored as JSONB on changes for flexible filtering.
6
- * Patterns use `{placeholder}` syntax to extract or inject values.
7
- */
8
-
9
- // ── Types ────────────────────────────────────────────────────────────
10
-
11
- /**
12
- * Scope pattern string, e.g., 'user:{user_id}', 'project:{project_id}'
13
- */
14
- export type ScopePattern = string;
15
-
16
- /**
17
- * Scope value for a single scope key.
18
- */
19
- export type ScopeValue = string | string[];
20
-
21
- /**
22
- * Scope values - the actual values for scope variables.
23
- * Values can be single strings or arrays (for multi-value subscriptions).
24
- *
25
- * @example
26
- * { user_id: 'U1' }
27
- * { project_id: ['P1', 'P2'] }
28
- * { year: '2025', month: '03' }
29
- */
30
- export type ScopeValues = Record<string, ScopeValue>;
31
-
32
- /**
33
- * Stored scopes on a change - always single values (not arrays).
34
- * This is what gets stored in the JSONB column.
35
- *
36
- * @example
37
- * { user_id: 'U1', project_id: 'P1' }
38
- */
39
- export type StoredScopes = Record<string, string>;
40
-
41
- /**
42
- * Extract scope keys from a scope pattern at the type level.
43
- *
44
- * @example
45
- * ScopeKeysFromPattern<'user:{user_id}'> // 'user_id'
46
- * ScopeKeysFromPattern<'event:{year}:{month}'> // 'year' | 'month'
47
- */
48
- export type ScopeKeysFromPattern<Pattern extends ScopePattern> =
49
- string extends Pattern
50
- ? string
51
- : Pattern extends `${string}{${infer Key}}${infer Rest}`
52
- ? Key | ScopeKeysFromPattern<Rest>
53
- : never;
54
-
55
- /**
56
- * Resolve the pattern string from a scope definition.
57
- */
58
- export type ScopePatternFromDefinition<Definition extends ScopeDefinition> =
59
- Definition extends ScopePattern
60
- ? Definition
61
- : Definition extends { pattern: infer Pattern extends ScopePattern }
62
- ? Pattern
63
- : never;
64
-
65
- /**
66
- * Extract scope keys from a list of scope definitions.
67
- */
68
- export type ScopeKeysFromDefinitions<
69
- Definitions extends readonly ScopeDefinition[],
70
- > = ScopeKeysFromPattern<ScopePatternFromDefinition<Definitions[number]>>;
71
-
72
- /**
73
- * Scope values constrained to known scope keys.
74
- *
75
- * Unknown keys are rejected at compile-time when literals are used.
76
- */
77
- export type ScopeValuesForKeys<ScopeKeys extends string> = Partial<
78
- Record<ScopeKeys, ScopeValue>
79
- >;
80
-
81
- /**
82
- * Scope values inferred from scope definitions.
83
- */
84
- export type ScopeValuesFromPatterns<
85
- Definitions extends readonly ScopeDefinition[],
86
- > = ScopeValuesForKeys<ScopeKeysFromDefinitions<Definitions>>;
87
-
88
- /**
89
- * Stored scopes constrained to known scope keys.
90
- */
91
- export type StoredScopesForKeys<ScopeKeys extends string> = Partial<
92
- Record<ScopeKeys, string>
93
- >;
94
-
95
- /**
96
- * Simplified scope definition.
97
- * Can be a simple pattern string or an object with explicit column mapping.
98
- *
99
- * @example
100
- * ```typescript
101
- * // Simple: pattern column is auto-derived
102
- * scopes: ['user:{user_id}', 'org:{org_id}']
103
- *
104
- * // Explicit: when column differs from pattern variable
105
- * scopes: [
106
- * { pattern: 'user:{user_id}', column: 'owner_id' }
107
- * ]
108
- * ```
109
- */
110
- export type ScopeDefinition =
111
- | ScopePattern
112
- | { pattern: ScopePattern; column: string };
113
-
114
- /**
115
- * Keep scope definitions as a typed tuple for downstream inference.
116
- *
117
- * @example
118
- * const scopes = defineScopePatterns(['user:{user_id}'] as const);
119
- */
120
- export function defineScopePatterns<
121
- const Definitions extends readonly ScopeDefinition[],
122
- >(scopes: Definitions): Definitions {
123
- return scopes;
124
- }
125
-
126
- // ── Pattern parsing (internal helpers) ───────────────────────────────
127
-
128
- /**
129
- * Extract the placeholder name from a pattern.
130
- * Returns null if the pattern doesn't contain a valid placeholder.
131
- */
132
- function extractPlaceholder(pattern: string): {
133
- prefix: string;
134
- placeholder: string;
135
- suffix: string;
136
- } | null {
137
- const match = pattern.match(/^(.*?)\{(\w+)\}(.*)$/);
138
- if (!match) return null;
139
-
140
- return {
141
- prefix: match[1]!,
142
- placeholder: match[2]!,
143
- suffix: match[3]!,
144
- };
145
- }
146
-
147
- /**
148
- * Extract the placeholder name from a pattern.
149
- */
150
- function getPlaceholderName(pattern: string): string | null {
151
- const parsed = extractPlaceholder(pattern);
152
- return parsed?.placeholder ?? null;
153
- }
154
-
155
- /**
156
- * Normalize scope definitions to a pattern-to-column map.
157
- *
158
- * @example
159
- * normalizeScopes(['user:{user_id}'])
160
- * // → { 'user:{user_id}': 'user_id' }
161
- */
162
- export function normalizeScopes(
163
- scopes: readonly ScopeDefinition[]
164
- ): Record<ScopePattern, string> {
165
- const result: Record<ScopePattern, string> = {};
166
- for (const scope of scopes) {
167
- if (typeof scope === 'string') {
168
- const placeholder = getPlaceholderName(scope);
169
- if (!placeholder) {
170
- throw new Error(
171
- `Scope pattern "${scope}" must contain a placeholder like {column_name}`
172
- );
173
- }
174
- result[scope] = placeholder;
175
- } else {
176
- result[scope.pattern] = scope.column;
177
- }
178
- }
179
- return result;
180
- }
181
-
182
- export function createSingleVariableScopeMetadata(
183
- scopes: readonly ScopeDefinition[]
184
- ): {
185
- scopePatterns: ScopePattern[];
186
- scopeColumnsByVariable: Record<string, string>;
187
- } {
188
- const scopeColumnMap = normalizeScopes(scopes);
189
- const scopePatterns = Object.keys(scopeColumnMap) as ScopePattern[];
190
- const scopeColumnsByVariable: Record<string, string> = {};
191
-
192
- for (const [pattern, columnName] of Object.entries(scopeColumnMap)) {
193
- const vars = extractScopeVars(pattern);
194
- if (vars.length !== 1) {
195
- throw new Error(
196
- `Scope pattern "${pattern}" must contain exactly one placeholder (got ${vars.length}).`
197
- );
198
- }
199
- const variableName = vars[0]!;
200
- const existingColumn = scopeColumnsByVariable[variableName];
201
- if (existingColumn && existingColumn !== columnName) {
202
- throw new Error(
203
- `Scope variable "${variableName}" is mapped to multiple columns: "${existingColumn}" and "${columnName}".`
204
- );
205
- }
206
- scopeColumnsByVariable[variableName] = columnName;
207
- }
208
-
209
- return { scopePatterns, scopeColumnsByVariable };
210
- }
211
-
212
- // ── Value operations (public) ────────────────────────────────────────
213
-
214
- /**
215
- * Extract variable names from a scope pattern.
216
- *
217
- * @example
218
- * extractScopeVars('project:{project_id}') // ['project_id']
219
- * extractScopeVars('event_date:{year}:{month}') // ['year', 'month']
220
- */
221
- export function extractScopeVars(pattern: ScopePattern): string[] {
222
- const matches = pattern.match(/\{([^}]+)\}/g);
223
- if (!matches) return [];
224
- return matches.map((m) => m.slice(1, -1));
225
- }
226
-
227
- export function collectScopeVars(
228
- scopePatterns: readonly ScopePattern[]
229
- ): Set<string> {
230
- const keys = new Set<string>();
231
- for (const pattern of scopePatterns) {
232
- for (const key of extractScopeVars(pattern)) {
233
- keys.add(key);
234
- }
235
- }
236
- return keys;
237
- }
package/src/sentry.ts DELETED
@@ -1,200 +0,0 @@
1
- import type {
2
- SyncMetricOptions,
3
- SyncMetrics,
4
- SyncSpan,
5
- SyncSpanOptions,
6
- SyncTelemetry,
7
- SyncTelemetryAttributes,
8
- SyncTelemetryAttributeValue,
9
- SyncTelemetryEvent,
10
- SyncTelemetryLevel,
11
- SyncTracer,
12
- } from '@syncular/core';
13
-
14
- interface SentryLoggerAdapter {
15
- trace?(message: string, attributes?: Record<string, unknown>): void;
16
- debug?(message: string, attributes?: Record<string, unknown>): void;
17
- info?(message: string, attributes?: Record<string, unknown>): void;
18
- warn?(message: string, attributes?: Record<string, unknown>): void;
19
- error?(message: string, attributes?: Record<string, unknown>): void;
20
- fatal?(message: string, attributes?: Record<string, unknown>): void;
21
- }
22
-
23
- interface SentrySpanAdapter {
24
- setAttribute?(name: string, value: SyncTelemetryAttributeValue): void;
25
- setAttributes?(attributes: SyncTelemetryAttributes): void;
26
- setStatus?(status: 'ok' | 'error' | string): void;
27
- }
28
-
29
- interface SentryMetricsAdapter {
30
- count?(name: string, value: number, options?: SyncMetricOptions): void;
31
- gauge?(name: string, value: number, options?: SyncMetricOptions): void;
32
- distribution?(name: string, value: number, options?: SyncMetricOptions): void;
33
- }
34
-
35
- export interface SentryTelemetryAdapter {
36
- logger?: SentryLoggerAdapter;
37
- startSpan?<T>(
38
- options: SyncSpanOptions,
39
- callback: (span: SentrySpanAdapter) => T
40
- ): T;
41
- metrics?: SentryMetricsAdapter;
42
- captureException?(error: unknown): void;
43
- }
44
-
45
- const noopSpan: SyncSpan = {
46
- setAttribute() {},
47
- setAttributes() {},
48
- setStatus() {},
49
- };
50
-
51
- function resolveLogLevel(event: SyncTelemetryEvent): SyncTelemetryLevel {
52
- if (event.level) return event.level;
53
- return event.error ? 'error' : 'info';
54
- }
55
-
56
- function resolveLogMethod(
57
- logger: SentryLoggerAdapter | undefined,
58
- level: SyncTelemetryLevel
59
- ): ((message: string, attributes?: Record<string, unknown>) => void) | null {
60
- if (!logger) return null;
61
- switch (level) {
62
- case 'trace':
63
- return logger.trace ?? logger.debug ?? logger.info ?? null;
64
- case 'debug':
65
- return logger.debug ?? logger.info ?? null;
66
- case 'info':
67
- return logger.info ?? null;
68
- case 'warn':
69
- return logger.warn ?? logger.info ?? null;
70
- case 'error':
71
- return logger.error ?? logger.warn ?? logger.info ?? null;
72
- case 'fatal':
73
- return logger.fatal ?? logger.error ?? logger.warn ?? logger.info ?? null;
74
- default:
75
- return logger.info ?? null;
76
- }
77
- }
78
-
79
- function toSpan(span: SentrySpanAdapter): SyncSpan {
80
- return {
81
- setAttribute(name, value) {
82
- span.setAttribute?.(name, value);
83
- },
84
- setAttributes(attributes) {
85
- if (span.setAttributes) {
86
- span.setAttributes(attributes);
87
- return;
88
- }
89
-
90
- for (const [key, value] of Object.entries(attributes)) {
91
- span.setAttribute?.(key, value);
92
- }
93
- },
94
- setStatus(status) {
95
- span.setStatus?.(status);
96
- },
97
- };
98
- }
99
-
100
- function toLogAttributeValue(
101
- value: unknown
102
- ): SyncTelemetryAttributeValue | undefined {
103
- if (typeof value === 'string') return value;
104
- if (typeof value === 'number') {
105
- return Number.isFinite(value) ? value : undefined;
106
- }
107
- if (typeof value === 'boolean') return value;
108
- if (typeof value === 'bigint') {
109
- const asNumber = Number(value);
110
- if (Number.isFinite(asNumber)) return asNumber;
111
- return value.toString();
112
- }
113
- if (value instanceof Date) return value.toISOString();
114
- if (value === null || value === undefined) return undefined;
115
-
116
- if (Array.isArray(value) || typeof value === 'object') {
117
- try {
118
- return JSON.stringify(value);
119
- } catch {
120
- return String(value);
121
- }
122
- }
123
-
124
- return String(value);
125
- }
126
-
127
- function sanitizeLogAttributes(
128
- attributes: Record<string, unknown>
129
- ): Record<string, SyncTelemetryAttributeValue> | null {
130
- const sanitized: Record<string, SyncTelemetryAttributeValue> = {};
131
- for (const [name, value] of Object.entries(attributes)) {
132
- const normalized = toLogAttributeValue(value);
133
- if (normalized !== undefined) {
134
- sanitized[name] = normalized;
135
- }
136
- }
137
-
138
- return Object.keys(sanitized).length > 0 ? sanitized : null;
139
- }
140
-
141
- function createTracer(adapter: SentryTelemetryAdapter): SyncTracer {
142
- return {
143
- startSpan<T>(options: SyncSpanOptions, callback: (span: SyncSpan) => T): T {
144
- if (!adapter.startSpan) return callback(noopSpan);
145
- return adapter.startSpan(options, (span) => callback(toSpan(span)));
146
- },
147
- };
148
- }
149
-
150
- function createMetrics(adapter: SentryTelemetryAdapter): SyncMetrics {
151
- return {
152
- count(name, value, options) {
153
- adapter.metrics?.count?.(name, value ?? 1, options);
154
- },
155
- gauge(name, value, options) {
156
- adapter.metrics?.gauge?.(name, value, options);
157
- },
158
- distribution(name, value, options) {
159
- adapter.metrics?.distribution?.(name, value, options);
160
- },
161
- };
162
- }
163
-
164
- /**
165
- * Create a Syncular telemetry adapter backed by Sentry primitives.
166
- */
167
- export function createSentrySyncTelemetry(
168
- adapter: SentryTelemetryAdapter
169
- ): SyncTelemetry {
170
- return {
171
- log(event) {
172
- const level = resolveLogLevel(event);
173
- const { event: message, ...attributes } = event;
174
- const logMethod = resolveLogMethod(adapter.logger, level);
175
- if (!logMethod) return;
176
- const sanitizedAttributes = sanitizeLogAttributes(attributes);
177
- if (!sanitizedAttributes) {
178
- logMethod(message);
179
- return;
180
- }
181
- logMethod(message, sanitizedAttributes);
182
- },
183
- tracer: createTracer(adapter),
184
- metrics: createMetrics(adapter),
185
- captureException(error, context) {
186
- adapter.captureException?.(error);
187
- if (!context) return;
188
- const logMethod =
189
- resolveLogMethod(adapter.logger, 'error') ??
190
- resolveLogMethod(adapter.logger, 'info');
191
- if (!logMethod) return;
192
- const sanitizedContext = sanitizeLogAttributes(context);
193
- if (!sanitizedContext) {
194
- logMethod('sync.exception.context');
195
- return;
196
- }
197
- logMethod('sync.exception.context', sanitizedContext);
198
- },
199
- };
200
- }