@apifuse/provider-sdk 2.2.0-beta.54 → 2.2.0-beta.56

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 (47) hide show
  1. package/CHANGELOG.md +8 -0
  2. package/bin/apifuse-pack-types.ts +121 -0
  3. package/dist/cli/migrate-operation-declaration.d.ts +5 -1
  4. package/dist/cli/migrate-operation-declaration.js +645 -46
  5. package/dist/index.d.ts +2 -1
  6. package/dist/index.js +1 -0
  7. package/dist/runtime/proxy-telemetry.d.ts +53 -2
  8. package/dist/runtime/proxy-telemetry.js +106 -65
  9. package/dist/runtime/request-telemetry.d.ts +70 -0
  10. package/dist/runtime/request-telemetry.js +228 -0
  11. package/dist/server/index.d.ts +3 -1
  12. package/dist/server/index.js +1 -0
  13. package/dist/server/serve-implementation.js +58 -20
  14. package/package.json +1 -1
  15. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-ambiguous-a.ts.txt +3 -0
  16. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-ambiguous-b.ts.txt +3 -0
  17. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-ambiguous-barrel.ts.txt +2 -0
  18. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-ambiguous-index.ts.txt +3 -0
  19. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-computed.ts.txt +6 -0
  20. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-cycle-a.ts.txt +1 -0
  21. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-cycle-b.ts.txt +1 -0
  22. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-cycle-index.ts.txt +3 -0
  23. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-daiso-barrel.ts.txt +7 -0
  24. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-daiso-catalog.ts.txt +19 -0
  25. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-daiso-docs.ts.txt +6 -0
  26. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-daiso-index.ts.txt +3 -0
  27. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-daiso-stores.ts.txt +8 -0
  28. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-indirect-config.ts.txt +5 -0
  29. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-nonstatic.ts.txt +4 -0
  30. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-package-index.ts.txt +3 -0
  31. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-reexport-index.ts.txt +3 -0
  32. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-reexport-leaf.ts.txt +10 -0
  33. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-reexport-one.ts.txt +1 -0
  34. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-reexport-two.ts.txt +1 -0
  35. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-star-package-barrel.ts.txt +2 -0
  36. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-star-package-index.ts.txt +3 -0
  37. package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-star-package-leaf.ts.txt +3 -0
  38. package/src/cli/__tests__/fixtures/migrate-operation-declaration/factory-duplicate-provider.ts.txt +11 -0
  39. package/src/cli/__tests__/fixtures/migrate-operation-declaration/factory-id-unresolved.ts.txt +7 -0
  40. package/src/cli/__tests__/fixtures/migrate-operation-declaration/factory-one-to-many.ts.txt +18 -0
  41. package/src/cli/__tests__/fixtures/migrate-operation-declaration/factory-one-to-one.ts.txt +13 -0
  42. package/src/cli/migrate-operation-declaration.ts +844 -49
  43. package/src/index.ts +13 -0
  44. package/src/runtime/proxy-telemetry.ts +160 -74
  45. package/src/runtime/request-telemetry.ts +376 -0
  46. package/src/server/index.ts +14 -0
  47. package/src/server/serve-implementation.ts +93 -21
@@ -26,6 +26,7 @@ import { getProviderBaseUrl } from "../runtime/provider.js";
26
26
  import { createOcrClientFromEnv } from "../runtime/ocr.js";
27
27
  import { PROXY_AUTH_IP_DENIED_CODE, PROXY_EDGE_AUTH_REJECTED_CODE, PROXY_POOL_EXHAUSTED_CODE, } from "../runtime/proxy-errors.js";
28
28
  import { PROVIDER_TELEMETRY_HEADER, ProxyTelemetryCollector, } from "../runtime/proxy-telemetry.js";
29
+ import { closedEnum, RequestTelemetry, tenantOpaqueKeys, } from "../runtime/request-telemetry.js";
29
30
  import { createUnsupportedResolverClient, } from "../runtime/resolver-shared.js";
30
31
  import { assertRequiredSecretsPresent, listMissingRequiredSecrets, MISSING_SECRET_CODE, } from "../runtime/secrets.js";
31
32
  import { createProviderRuntimeStateFromEnv, createUnsupportedProviderRuntimeState, } from "../runtime/state.js";
@@ -75,6 +76,16 @@ function providerErrorCode(error) {
75
76
  }
76
77
  const AUTH_FLOW_LOCALES = ["en", "ko", "ja"];
77
78
  const retryResponseMeta = new WeakMap();
79
+ const RETRY_LAST_ERROR_CODES = [
80
+ "transport_cancelled",
81
+ "transport_timeout",
82
+ "transport_network_error",
83
+ "upstream_http_error",
84
+ "other",
85
+ ];
86
+ function tenantRetryLastErrorCode(value) {
87
+ return RETRY_LAST_ERROR_CODES.find((candidate) => candidate === value) ?? "other";
88
+ }
78
89
  const STATEFUL_INTERNAL_OPERATIONS_ROUTE = "/__apifuse/stateful/operations";
79
90
  const STATEFUL_FORWARDING_SOURCE_POD_HEADER = "x-apifuse-stateful-source-pod";
80
91
  const DEFAULT_STATEFUL_FORWARDING_MAX_SKEW_MS = 5 * 60_000;
@@ -409,7 +420,7 @@ function createProviderContext(provider, request, operationId, options, state =
409
420
  const proxyClientOptions = {
410
421
  upstream: { proxy: provider.proxy },
411
422
  affinityKey: resolveProviderProxyAffinityKey(provider, request, operationId),
412
- telemetry: scope.proxyTelemetry,
423
+ telemetry: scope.telemetry.proxy,
413
424
  engineCredentials: engineProxyCredentials,
414
425
  };
415
426
  const resolverIdentityScope = resolveProviderResolverIdentityScope(provider, proxyClientOptions.affinityKey, request.requestId);
@@ -417,7 +428,7 @@ function createProviderContext(provider, request, operationId, options, state =
417
428
  const stealthClientOptions = {
418
429
  upstream: proxyClientOptions.upstream,
419
430
  affinityKey: proxyClientOptions.affinityKey,
420
- telemetry: scope.proxyTelemetry,
431
+ telemetry: scope.telemetry.proxy,
421
432
  engineCredentials: engineProxyCredentials,
422
433
  ...(signal ? { signal } : {}),
423
434
  ...(provider.stealth ? { stealth: provider.stealth } : {}),
@@ -564,14 +575,14 @@ function createAuthFlowContext(provider, request, options, state, scope, signal)
564
575
  const proxyClientOptions = {
565
576
  upstream: { proxy: provider.proxy },
566
577
  affinityKey: resolveAuthFlowProxyAffinityKey(provider, request),
567
- telemetry: scope.proxyTelemetry,
578
+ telemetry: scope.telemetry.proxy,
568
579
  engineCredentials: engineProxyCredentials,
569
580
  };
570
581
  const resolverIdentityScope = resolveProviderResolverIdentityScope(provider, proxyClientOptions.affinityKey, request.requestId);
571
582
  const stealthClientOptions = {
572
583
  upstream: proxyClientOptions.upstream,
573
584
  affinityKey: proxyClientOptions.affinityKey,
574
- telemetry: scope.proxyTelemetry,
585
+ telemetry: scope.telemetry.proxy,
575
586
  engineCredentials: engineProxyCredentials,
576
587
  ...(signal ? { signal } : {}),
577
588
  ...(provider.stealth ? { stealth: provider.stealth } : {}),
@@ -1014,7 +1025,7 @@ function providerErrorCauseChain(error) {
1014
1025
  }
1015
1026
  return frames.length > 0 ? frames : undefined;
1016
1027
  }
1017
- function logProviderError(logger, provider, kind, route, requestId, error, status, cost, declaredErrorCode, proxyTelemetry, observabilityDetails, correlation = {}) {
1028
+ function logProviderError(logger, provider, kind, route, requestId, error, status, cost, declaredErrorCode, telemetry, observabilityDetails, correlation = {}) {
1018
1029
  const providerCode = isProviderError(error) ? providerErrorCode(error) : undefined;
1019
1030
  const code = isProviderError(error)
1020
1031
  ? (providerCode ?? "provider_error")
@@ -1033,7 +1044,7 @@ function logProviderError(logger, provider, kind, route, requestId, error, statu
1033
1044
  typeof providerCode === "string" &&
1034
1045
  !SDK_OWNED_PROVIDER_ERROR_CODES.has(providerCode) &&
1035
1046
  declaredErrorCode === undefined;
1036
- const proxy = proxyTelemetry?.toLogPayload();
1047
+ const telemetryPayload = telemetry?.toLogPayload();
1037
1048
  const emit = typeof logger === "function" ? logger : defaultProviderServerLogger;
1038
1049
  // The logger is caller-supplied and may mutate the event synchronously.
1039
1050
  // Give it an independent snapshot so those mutations cannot corrupt the
@@ -1058,7 +1069,7 @@ function logProviderError(logger, provider, kind, route, requestId, error, statu
1058
1069
  : {}),
1059
1070
  status,
1060
1071
  ...cost,
1061
- ...(proxy ? { proxy } : {}),
1072
+ ...(telemetryPayload ?? {}),
1062
1073
  code,
1063
1074
  errorClass,
1064
1075
  message,
@@ -1093,8 +1104,8 @@ function logProviderCleanupError(logger, provider, kind, operationId, requestId,
1093
1104
  message,
1094
1105
  });
1095
1106
  }
1096
- function logProviderSuccess(logger, provider, kind, route, requestId, status, cost, proxyTelemetry, correlation = {}) {
1097
- const proxy = proxyTelemetry?.toLogPayload();
1107
+ function logProviderSuccess(logger, provider, kind, route, requestId, status, cost, telemetry, correlation = {}) {
1108
+ const telemetryPayload = telemetry?.toLogPayload();
1098
1109
  const emit = typeof logger === "function" ? logger : defaultProviderServerLogger;
1099
1110
  emit({
1100
1111
  level: "info",
@@ -1113,7 +1124,7 @@ function logProviderSuccess(logger, provider, kind, route, requestId, status, co
1113
1124
  : {}),
1114
1125
  status,
1115
1126
  ...cost,
1116
- ...(proxy ? { proxy } : {}),
1127
+ ...(telemetryPayload ?? {}),
1117
1128
  });
1118
1129
  }
1119
1130
  const STREAM_CLEANUP_TIMEOUT_MS = 100;
@@ -1165,7 +1176,6 @@ function requestTraceId(headers, requestId) {
1165
1176
  function createRequestScope(input) {
1166
1177
  const requestCost = startRequestCost();
1167
1178
  const traceConfig = resolveTraceConfigFromEnv();
1168
- const proxyTelemetry = new ProxyTelemetryCollector();
1169
1179
  const details = {
1170
1180
  route: input.route,
1171
1181
  requestId: input.requestId,
@@ -1188,6 +1198,9 @@ function createRequestScope(input) {
1188
1198
  ...(initialTraceId ? { traceId: initialTraceId } : {}),
1189
1199
  })
1190
1200
  : createTraceContext();
1201
+ const proxyCollector = new ProxyTelemetryCollector();
1202
+ const telemetry = new RequestTelemetry(trace);
1203
+ telemetry.register(proxyCollector);
1191
1204
  let rootRunner = (fn) => fn();
1192
1205
  let resolveRoot;
1193
1206
  const rootTerminal = new Promise((resolve) => {
@@ -1233,7 +1246,7 @@ function createRequestScope(input) {
1233
1246
  await Promise.all(streamCleanups.map(runCleanupEntry));
1234
1247
  };
1235
1248
  const headerSnapshot = (error) => {
1236
- const providerTelemetryHeader = proxyTelemetry.toHeaderValue();
1249
+ const providerTelemetryHeader = telemetry.toHeaderValue();
1237
1250
  const declaredErrorCode = error === undefined ? undefined : input.declaredErrorCode?.(error);
1238
1251
  const errorObservability = error === undefined ? undefined : errorObservabilityDetails(error, declaredErrorCode);
1239
1252
  return {
@@ -1261,7 +1274,7 @@ function createRequestScope(input) {
1261
1274
  };
1262
1275
  const scope = {
1263
1276
  trace,
1264
- proxyTelemetry,
1277
+ telemetry,
1265
1278
  enrich(enrichment) {
1266
1279
  if (terminalOutcome)
1267
1280
  return;
@@ -1334,10 +1347,10 @@ function createRequestScope(input) {
1334
1347
  const cost = finishRequestCost(requestCost);
1335
1348
  try {
1336
1349
  if (error === undefined) {
1337
- logProviderSuccess(input.logger, input.provider, input.kind, details.route, details.requestId, status, cost, proxyTelemetry, details.correlation);
1350
+ logProviderSuccess(input.logger, input.provider, input.kind, details.route, details.requestId, status, cost, telemetry, details.correlation);
1338
1351
  }
1339
1352
  else {
1340
- logProviderError(input.logger, input.provider, input.kind, details.route, details.requestId, error, status, cost, declaredErrorCode, proxyTelemetry, finishedResult.errorObservability, details.correlation);
1353
+ logProviderError(input.logger, input.provider, input.kind, details.route, details.requestId, error, status, cost, declaredErrorCode, telemetry, finishedResult.errorObservability, details.correlation);
1341
1354
  }
1342
1355
  }
1343
1356
  catch {
@@ -1407,18 +1420,43 @@ function toJsonSuccessResponse(result, ctx) {
1407
1420
  }
1408
1421
  const cacheMeta = ctx && "cache" in ctx ? ctx.cache.responseMeta() : undefined;
1409
1422
  const retryMeta = ctx ? retryResponseMeta.get(ctx) : undefined;
1423
+ const cache = cacheMeta
1424
+ ? {
1425
+ hit: cacheMeta.hit,
1426
+ stale: cacheMeta.stale,
1427
+ // TODO(owner): keep or remove implementation-shaped cache keys from tenant meta.
1428
+ keys: tenantOpaqueKeys(cacheMeta.keys),
1429
+ ...(cacheMeta.source ? { source: closedEnum(cacheMeta.source) } : {}),
1430
+ }
1431
+ : undefined;
1432
+ const retry = retryMeta
1433
+ ? {
1434
+ attempts: retryMeta.attempts,
1435
+ retries: retryMeta.retries,
1436
+ ...(retryMeta.preset ? { preset: closedEnum(retryMeta.preset) } : {}),
1437
+ transport: closedEnum(retryMeta.transport),
1438
+ ...(retryMeta.lastErrorCode
1439
+ ? {
1440
+ lastErrorCode: closedEnum(tenantRetryLastErrorCode(retryMeta.lastErrorCode)),
1441
+ }
1442
+ : {}),
1443
+ ...(retryMeta.lastStatus ? { lastStatus: retryMeta.lastStatus } : {}),
1444
+ }
1445
+ : undefined;
1410
1446
  const meta = cacheMeta || retryMeta
1411
1447
  ? {
1412
- ...(cacheMeta
1448
+ ...(cache
1413
1449
  ? {
1414
- cached: cacheMeta.hit,
1415
- stale: cacheMeta.stale,
1416
- cache: cacheMeta,
1450
+ cached: cache.hit,
1451
+ stale: cache.stale,
1452
+ cache,
1417
1453
  }
1418
1454
  : {}),
1419
- ...(retryMeta ? { retry: retryMeta } : {}),
1455
+ ...(retry ? { retry } : {}),
1420
1456
  }
1421
1457
  : undefined;
1458
+ const _neutralMeta = meta;
1459
+ void _neutralMeta;
1422
1460
  return {
1423
1461
  data: result,
1424
1462
  ...(meta ? { meta } : {}),
package/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "2.2.0-beta.54",
2
+ "version": "2.2.0-beta.56",
3
3
  "name": "@apifuse/provider-sdk",
4
4
  "private": false,
5
5
  "type": "module",
@@ -0,0 +1,3 @@
1
+ export const registry = {
2
+ first: { annotations: { readOnly: true }, input: InputSchema, output: OutputSchema, handler },
3
+ };
@@ -0,0 +1,3 @@
1
+ export const registry = {
2
+ second: { annotations: { readOnly: true }, input: InputSchema, output: OutputSchema, handler },
3
+ };
@@ -0,0 +1,2 @@
1
+ export * from "./ambiguous-a";
2
+ export * from "./ambiguous-b";
@@ -0,0 +1,3 @@
1
+ import { registry } from "./ambiguous-barrel";
2
+
3
+ export default buildProvider({ operations: registry });
@@ -0,0 +1,6 @@
1
+ const operationId = "ping";
2
+ const registry = {
3
+ [operationId]: { annotations: { readOnly: true }, input: InputSchema, output: OutputSchema, handler },
4
+ };
5
+
6
+ export default buildProvider({ operations: registry });
@@ -0,0 +1 @@
1
+ export { registry } from "./cycle-b";
@@ -0,0 +1 @@
1
+ export { registry } from "./cycle-a";
@@ -0,0 +1,3 @@
1
+ import { registry } from "./cycle-a";
2
+
3
+ export default buildProvider({ operations: registry });
@@ -0,0 +1,7 @@
1
+ import { catalogOperations } from "./catalog";
2
+ import { storeOperations } from "./stores";
3
+
4
+ export const daisoOperations = {
5
+ ...catalogOperations,
6
+ ...storeOperations,
7
+ };
@@ -0,0 +1,19 @@
1
+ import { OPERATION_DOCS } from "./docs";
2
+
3
+ export const catalogOperations = {
4
+ "browse-catalog": {
5
+ docs: { ...OPERATION_DOCS.catalog },
6
+ descriptionKey: "operations.browseCatalog.description",
7
+ annotations: { readOnly: true },
8
+ inputExamples: [{ scenario: "Browse the catalog", input: {} }],
9
+ input: InputSchema,
10
+ output: OutputSchema,
11
+ handler,
12
+ },
13
+ "search-products": {
14
+ annotations: { readOnly: true },
15
+ input: InputSchema,
16
+ output: OutputSchema,
17
+ handler,
18
+ },
19
+ };
@@ -0,0 +1,6 @@
1
+ export const OPERATION_DOCS = {
2
+ catalog: {
3
+ titleKey: "operations.groupedRead.title",
4
+ descriptionKey: "operations.groupedRead.description",
5
+ },
6
+ };
@@ -0,0 +1,3 @@
1
+ import { daisoOperations } from "./operations";
2
+
3
+ export default buildProvider({ operations: daisoOperations });
@@ -0,0 +1,8 @@
1
+ export const storeOperations = {
2
+ "search-stores": {
3
+ annotations: { readOnly: true },
4
+ input: InputSchema,
5
+ output: OutputSchema,
6
+ handler,
7
+ },
8
+ };
@@ -0,0 +1,5 @@
1
+ import { packageRegistry } from "provider-operation-package";
2
+
3
+ const config = { operations: packageRegistry };
4
+
5
+ export default buildProvider(config);
@@ -0,0 +1,4 @@
1
+ const first = { ping: pingOperation };
2
+ const second = { pong: pongOperation };
3
+
4
+ export default buildProvider({ operations: enabled ? first : second });
@@ -0,0 +1,3 @@
1
+ import { packageRegistry } from "provider-operation-package";
2
+
3
+ export default buildProvider({ operations: packageRegistry });
@@ -0,0 +1,3 @@
1
+ import { registry } from "./barrel-one.js";
2
+
3
+ export default buildProvider({ operations: registry });
@@ -0,0 +1,10 @@
1
+ const leafRegistry = {
2
+ ping: {
3
+ annotations: { readOnly: true },
4
+ input: InputSchema,
5
+ output: OutputSchema,
6
+ handler,
7
+ },
8
+ };
9
+
10
+ export const registry = leafRegistry;
@@ -0,0 +1 @@
1
+ export { registry } from "./barrel-two.js";
@@ -0,0 +1 @@
1
+ export { registry } from "./leaf";
@@ -0,0 +1,2 @@
1
+ export * from "provider-operation-package";
2
+ export * from "./star-leaf";
@@ -0,0 +1,3 @@
1
+ import { registry } from "./star-barrel";
2
+
3
+ export default buildProvider({ operations: registry });
@@ -0,0 +1,3 @@
1
+ export const registry = {
2
+ ping: { annotations: { readOnly: true }, input: InputSchema, output: OutputSchema, handler },
3
+ };
@@ -0,0 +1,11 @@
1
+ function makePingOperation() {
2
+ return defineOperation<ProviderContext>()({
3
+ annotations: { readOnly: true },
4
+ input: InputSchema,
5
+ output: OutputSchema,
6
+ handler,
7
+ });
8
+ }
9
+
10
+ export const first = buildProvider({ operations: { ping: makePingOperation() } });
11
+ export default buildProvider({ operations: { ping: makePingOperation() } });
@@ -0,0 +1,7 @@
1
+ defineOperation<ProviderContext>()({
2
+ annotations: { readOnly: true },
3
+ inputExamples: [{ scenario: "Anonymous", input: {} }],
4
+ input: InputSchema,
5
+ output: OutputSchema,
6
+ handler,
7
+ });
@@ -0,0 +1,18 @@
1
+ function makeSearchOperation(kind: string) {
2
+ return defineOperation<ProviderContext>()({
3
+ annotations: { readOnly: true },
4
+ inputExamples: [{ scenario: "Search items", input: { kind } }],
5
+ input: InputSchema,
6
+ output: OutputSchema,
7
+ handler,
8
+ });
9
+ }
10
+
11
+ export default buildProvider({
12
+ operations: {
13
+ "used-goods-search": makeSearchOperation("used"),
14
+ "realty-search-listings": makeSearchOperation("realty"),
15
+ "jobs-search": makeSearchOperation("jobs"),
16
+ "cars-search": makeSearchOperation("cars"),
17
+ },
18
+ });
@@ -0,0 +1,13 @@
1
+ function makeSearchOperation(kind: string) {
2
+ return defineOperation<ProviderContext>()({
3
+ annotations: { readOnly: true },
4
+ inputExamples: [{ scenario: "Search items", input: { kind } }],
5
+ input: InputSchema,
6
+ output: OutputSchema,
7
+ handler,
8
+ });
9
+ }
10
+
11
+ export default buildProvider({
12
+ operations: { "search-items": makeSearchOperation("items") },
13
+ });