@tinycloud/sdk-services 2.3.0-beta.6 → 2.3.0-beta.8

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.
package/dist/index.cjs CHANGED
@@ -62,6 +62,7 @@ __export(index_exports, {
62
62
  ServiceRetryEventSchema: () => ServiceRetryEventSchema,
63
63
  ServiceSessionSchema: () => ServiceSessionSchema,
64
64
  TelemetryEvents: () => TelemetryEvents,
65
+ TelemetrySpanEventSchema: () => TelemetrySpanEventSchema,
65
66
  TinyCloudQuota: () => TinyCloudQuota,
66
67
  VaultHeaders: () => VaultHeaders,
67
68
  VaultPublicSpaceKVActions: () => VaultPublicSpaceKVActions,
@@ -121,6 +122,7 @@ __export(index_exports, {
121
122
  validateServiceRequestEvent: () => validateServiceRequestEvent,
122
123
  validateServiceResponseEvent: () => validateServiceResponseEvent,
123
124
  validateServiceSession: () => validateServiceSession,
125
+ validateTelemetrySpanEvent: () => validateTelemetrySpanEvent,
124
126
  verifyDecryptResponse: () => verifyDecryptResponse,
125
127
  wrapError: () => wrapError
126
128
  });
@@ -171,6 +173,7 @@ var defaultRetryPolicy = {
171
173
  retryableErrors: [ErrorCodes.NETWORK_ERROR, ErrorCodes.TIMEOUT]
172
174
  };
173
175
  var TelemetryEvents = {
176
+ SPAN: "telemetry.span",
174
177
  SERVICE_REQUEST: "service.request",
175
178
  SERVICE_RESPONSE: "service.response",
176
179
  SERVICE_ERROR: "service.error",
@@ -248,18 +251,22 @@ var KVListResultSchema = createResultSchema(KVListResponseSchema);
248
251
  var ServiceRequestEventSchema = import_zod.z.object({
249
252
  service: import_zod.z.string(),
250
253
  action: import_zod.z.string(),
254
+ span: import_zod.z.string().optional(),
251
255
  key: import_zod.z.string().optional(),
252
256
  timestamp: import_zod.z.number()
253
257
  });
254
258
  var ServiceResponseEventSchema = import_zod.z.object({
255
259
  service: import_zod.z.string(),
256
260
  action: import_zod.z.string(),
261
+ span: import_zod.z.string().optional(),
257
262
  ok: import_zod.z.boolean(),
258
263
  duration: import_zod.z.number(),
264
+ durationMs: import_zod.z.number().optional(),
259
265
  status: import_zod.z.number().optional()
260
266
  });
261
267
  var ServiceErrorEventSchema = import_zod.z.object({
262
268
  service: import_zod.z.string(),
269
+ span: import_zod.z.string().optional(),
263
270
  error: ServiceErrorSchema
264
271
  });
265
272
  var ServiceRetryEventSchema = import_zod.z.object({
@@ -268,6 +275,15 @@ var ServiceRetryEventSchema = import_zod.z.object({
268
275
  maxAttempts: import_zod.z.number().int().positive(),
269
276
  error: ServiceErrorSchema
270
277
  });
278
+ var TelemetrySpanEventSchema = import_zod.z.object({
279
+ span: import_zod.z.string(),
280
+ ok: import_zod.z.boolean(),
281
+ durationMs: import_zod.z.number(),
282
+ service: import_zod.z.string().optional(),
283
+ action: import_zod.z.string().optional(),
284
+ status: import_zod.z.number().optional(),
285
+ error: ServiceErrorSchema.optional()
286
+ });
271
287
  var RetryPolicySchema = import_zod.z.object({
272
288
  /** Maximum number of attempts (including initial) */
273
289
  maxAttempts: import_zod.z.number().int().positive(),
@@ -399,6 +415,21 @@ function validateServiceResponseEvent(data) {
399
415
  }
400
416
  return { ok: true, data: result.data };
401
417
  }
418
+ function validateTelemetrySpanEvent(data) {
419
+ const result = TelemetrySpanEventSchema.safeParse(data);
420
+ if (!result.success) {
421
+ return {
422
+ ok: false,
423
+ error: {
424
+ code: "VALIDATION_ERROR",
425
+ message: result.error.message,
426
+ service: "telemetry",
427
+ meta: { issues: result.error.issues }
428
+ }
429
+ };
430
+ }
431
+ return { ok: true, data: result.data };
432
+ }
402
433
 
403
434
  // src/context.ts
404
435
  var ServiceContext = class {
@@ -416,6 +447,8 @@ var ServiceContext = class {
416
447
  ...defaultRetryPolicy,
417
448
  ...config.retryPolicy
418
449
  };
450
+ this._telemetryEnabled = typeof config.telemetry === "boolean" ? config.telemetry : config.telemetry?.enabled === true;
451
+ this._telemetryHandler = typeof config.telemetry === "object" ? config.telemetry.onEvent : void 0;
419
452
  }
420
453
  // ============================================================
421
454
  // Session Management
@@ -510,6 +543,13 @@ var ServiceContext = class {
510
543
  * @param data - Event data
511
544
  */
512
545
  emit(event, data) {
546
+ if (this._telemetryEnabled && this._telemetryHandler) {
547
+ try {
548
+ this._telemetryHandler(event, data);
549
+ } catch (error) {
550
+ console.error(`Error in telemetry handler for "${event}":`, error);
551
+ }
552
+ }
513
553
  const handlers = this._eventHandlers.get(event);
514
554
  if (handlers) {
515
555
  for (const handler of handlers) {
@@ -788,9 +828,11 @@ var BaseService = class {
788
828
  * @param key - Optional key/path being accessed
789
829
  */
790
830
  emitRequest(action, key) {
831
+ const service = this.getServiceName();
791
832
  this.emit(TelemetryEvents.SERVICE_REQUEST, {
792
- service: this.getServiceName(),
833
+ service,
793
834
  action,
835
+ span: this.spanName(action),
794
836
  key,
795
837
  timestamp: Date.now()
796
838
  });
@@ -804,11 +846,24 @@ var BaseService = class {
804
846
  * @param status - Optional HTTP status code
805
847
  */
806
848
  emitResponse(action, ok2, startTime, status) {
849
+ const service = this.getServiceName();
850
+ const durationMs = Date.now() - startTime;
851
+ const span = this.spanName(action);
807
852
  this.emit(TelemetryEvents.SERVICE_RESPONSE, {
808
- service: this.getServiceName(),
853
+ service,
809
854
  action,
855
+ span,
810
856
  ok: ok2,
811
- duration: Date.now() - startTime,
857
+ duration: durationMs,
858
+ durationMs,
859
+ status
860
+ });
861
+ this.emit(TelemetryEvents.SPAN, {
862
+ span,
863
+ service,
864
+ action,
865
+ ok: ok2,
866
+ durationMs,
812
867
  status
813
868
  });
814
869
  }
@@ -817,9 +872,11 @@ var BaseService = class {
817
872
  *
818
873
  * @param error - The service error
819
874
  */
820
- emitError(error) {
875
+ emitError(error, action) {
876
+ const span = action ? this.spanName(action) : void 0;
821
877
  this.emit(TelemetryEvents.SERVICE_ERROR, {
822
878
  service: this.getServiceName(),
879
+ ...span ? { span } : {},
823
880
  error
824
881
  });
825
882
  }
@@ -830,6 +887,12 @@ var BaseService = class {
830
887
  getServiceName() {
831
888
  return this.constructor.serviceName;
832
889
  }
890
+ /**
891
+ * Stable span name used by SDK telemetry sinks.
892
+ */
893
+ spanName(action) {
894
+ return `sdk.${this.getServiceName()}.${action}`;
895
+ }
833
896
  /**
834
897
  * Create a combined abort signal from multiple sources.
835
898
  *
@@ -867,13 +930,13 @@ var BaseService = class {
867
930
  this.emitResponse(action, true, startTime);
868
931
  } else {
869
932
  this.emitResponse(action, false, startTime);
870
- this.emitError(result.error);
933
+ this.emitError(result.error, action);
871
934
  }
872
935
  return result;
873
936
  } catch (error) {
874
937
  const serviceError3 = wrapError(this.getServiceName(), error);
875
938
  this.emitResponse(action, false, startTime);
876
- this.emitError(serviceError3);
939
+ this.emitError(serviceError3, action);
877
940
  return err(serviceError3);
878
941
  }
879
942
  }
@@ -1798,7 +1861,7 @@ var SQLService = class extends BaseService {
1798
1861
  try {
1799
1862
  const response = await this.invokeSQL(
1800
1863
  dbName,
1801
- SQLAction.READ,
1864
+ this.actionForSql(sql, SQLAction.READ),
1802
1865
  { action: "query", sql, params: params ?? [] },
1803
1866
  options?.signal
1804
1867
  );
@@ -1828,7 +1891,7 @@ var SQLService = class extends BaseService {
1828
1891
  }
1829
1892
  const response = await this.invokeSQL(
1830
1893
  dbName,
1831
- SQLAction.WRITE,
1894
+ this.actionForSql(sql, SQLAction.WRITE),
1832
1895
  body,
1833
1896
  options?.signal
1834
1897
  );
@@ -1850,7 +1913,7 @@ var SQLService = class extends BaseService {
1850
1913
  try {
1851
1914
  const response = await this.invokeSQL(
1852
1915
  dbName,
1853
- SQLAction.WRITE,
1916
+ this.actionForSqlBatch(statements),
1854
1917
  { action: "batch", statements },
1855
1918
  options?.signal
1856
1919
  );
@@ -1927,6 +1990,14 @@ var SQLService = class extends BaseService {
1927
1990
  signal: this.combineSignals(signal)
1928
1991
  });
1929
1992
  }
1993
+ actionForSql(sql, fallback) {
1994
+ return firstSqlToken(sql) === "pragma" ? SQLAction.ADMIN : fallback;
1995
+ }
1996
+ actionForSqlBatch(statements) {
1997
+ return statements.some(
1998
+ (statement) => this.actionForSql(statement.sql, SQLAction.WRITE) === SQLAction.ADMIN
1999
+ ) ? SQLAction.ADMIN : SQLAction.WRITE;
2000
+ }
1930
2001
  async handleErrorResponse(response, operation) {
1931
2002
  const errorText = await response.text();
1932
2003
  let errorBody = {};
@@ -1972,6 +2043,33 @@ var SQLService = class extends BaseService {
1972
2043
  }
1973
2044
  };
1974
2045
  SQLService.serviceName = "sql";
2046
+ function firstSqlToken(sql) {
2047
+ let index = 0;
2048
+ while (index < sql.length) {
2049
+ while (index < sql.length && /\s/.test(sql[index])) {
2050
+ index++;
2051
+ }
2052
+ if (sql.startsWith("--", index)) {
2053
+ const newline = sql.indexOf("\n", index + 2);
2054
+ if (newline === -1) {
2055
+ return void 0;
2056
+ }
2057
+ index = newline + 1;
2058
+ continue;
2059
+ }
2060
+ if (sql.startsWith("/*", index)) {
2061
+ const end = sql.indexOf("*/", index + 2);
2062
+ if (end === -1) {
2063
+ return void 0;
2064
+ }
2065
+ index = end + 2;
2066
+ continue;
2067
+ }
2068
+ break;
2069
+ }
2070
+ const match = /^[A-Za-z_]+/.exec(sql.slice(index));
2071
+ return match?.[0].toLowerCase();
2072
+ }
1975
2073
 
1976
2074
  // src/duckdb/DuckDbDatabaseHandle.ts
1977
2075
  var DuckDbDatabaseHandle = class {
@@ -5650,6 +5748,7 @@ EncryptionService.serviceName = "encryption";
5650
5748
  ServiceRetryEventSchema,
5651
5749
  ServiceSessionSchema,
5652
5750
  TelemetryEvents,
5751
+ TelemetrySpanEventSchema,
5653
5752
  TinyCloudQuota,
5654
5753
  VaultHeaders,
5655
5754
  VaultPublicSpaceKVActions,
@@ -5709,6 +5808,7 @@ EncryptionService.serviceName = "encryption";
5709
5808
  validateServiceRequestEvent,
5710
5809
  validateServiceResponseEvent,
5711
5810
  validateServiceSession,
5811
+ validateTelemetrySpanEvent,
5712
5812
  verifyDecryptResponse,
5713
5813
  wrapError
5714
5814
  });