@apifuse/provider-sdk 2.2.0-beta.55 → 2.2.0-beta.57
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/CHANGELOG.md +8 -0
- package/bin/apifuse-pack-types.ts +121 -0
- package/dist/cli/migrate-operation-declaration.d.ts +7 -0
- package/dist/cli/migrate-operation-declaration.js +52 -12
- package/dist/index.d.ts +2 -1
- package/dist/index.js +1 -0
- package/dist/runtime/proxy-telemetry.d.ts +53 -2
- package/dist/runtime/proxy-telemetry.js +106 -65
- package/dist/runtime/request-telemetry.d.ts +70 -0
- package/dist/runtime/request-telemetry.js +228 -0
- package/dist/server/index.d.ts +3 -1
- package/dist/server/index.js +1 -0
- package/dist/server/serve-implementation.js +58 -20
- package/package.json +1 -1
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-runtime-composed-index.ts.txt +3 -0
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-runtime-composed-one.ts.txt +6 -0
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-runtime-composed-two.ts.txt +6 -0
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-runtime-mixed-index.ts.txt +14 -0
- package/src/cli/__tests__/fixtures/migrate-operation-declaration/discovery-runtime-registry.ts.txt +3 -0
- package/src/cli/migrate-operation-declaration.ts +75 -10
- package/src/index.ts +13 -0
- package/src/runtime/proxy-telemetry.ts +160 -74
- package/src/runtime/request-telemetry.ts +376 -0
- package/src/server/index.ts +14 -0
- package/src/server/serve-implementation.ts +93 -21
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# @apifuse/provider-sdk Changelog
|
|
2
2
|
|
|
3
|
+
## 2.2.0-beta.57
|
|
4
|
+
|
|
5
|
+
- Release candidate for main commit 4087dafdeab771eb91691884ce7315b8c399f863.
|
|
6
|
+
|
|
7
|
+
## 2.2.0-beta.56
|
|
8
|
+
|
|
9
|
+
- Release candidate for main commit 390167b3f51488be0beba25a1ea5eb425d3bf839.
|
|
10
|
+
|
|
3
11
|
## 2.2.0-beta.55
|
|
4
12
|
|
|
5
13
|
- Release candidate for main commit 1c495af089940a4c666e6baf58dc0dd577771a40.
|
|
@@ -292,6 +292,115 @@ const NEGATIVE_CONTROLS = [
|
|
|
292
292
|
"",
|
|
293
293
|
].join("\n"),
|
|
294
294
|
},
|
|
295
|
+
{
|
|
296
|
+
filename: "negative-control-telemetry-header-vendor.ts",
|
|
297
|
+
expectedCode: "TS2322",
|
|
298
|
+
description: "gateway telemetry headers reject an unbranded vendor string",
|
|
299
|
+
source: [
|
|
300
|
+
'import type { TelemetryContributor } from "@apifuse/provider-sdk";',
|
|
301
|
+
"",
|
|
302
|
+
"const bad: TelemetryContributor<{}, { vendor: string }> = {",
|
|
303
|
+
'\tkey: "resolver",',
|
|
304
|
+
"\ttoLogPayload: () => ({}),",
|
|
305
|
+
'\ttoHeaderPayload: () => ({ vendor: "free-text" }),',
|
|
306
|
+
"};",
|
|
307
|
+
"",
|
|
308
|
+
].join("\n"),
|
|
309
|
+
},
|
|
310
|
+
{
|
|
311
|
+
filename: "negative-control-telemetry-header-host.ts",
|
|
312
|
+
expectedCode: "TS2322",
|
|
313
|
+
description: "gateway telemetry headers reject an unbranded host string",
|
|
314
|
+
source: [
|
|
315
|
+
'import type { TelemetryContributor } from "@apifuse/provider-sdk";',
|
|
316
|
+
"",
|
|
317
|
+
"const bad: TelemetryContributor<{}, { host: string }> = {",
|
|
318
|
+
'\tkey: "http",',
|
|
319
|
+
"\ttoLogPayload: () => ({}),",
|
|
320
|
+
'\ttoHeaderPayload: () => ({ host: "x.example" }),',
|
|
321
|
+
"};",
|
|
322
|
+
"",
|
|
323
|
+
].join("\n"),
|
|
324
|
+
},
|
|
325
|
+
{
|
|
326
|
+
filename: "negative-control-telemetry-tenant-vendor.ts",
|
|
327
|
+
expectedCode: "TS2322",
|
|
328
|
+
description: "tenant-neutral projections reject vendor identities",
|
|
329
|
+
source: [
|
|
330
|
+
'import type { TenantNeutral } from "@apifuse/provider-sdk";',
|
|
331
|
+
"",
|
|
332
|
+
'export const bad: TenantNeutral<{ vendorUsed: "smartproxy" }> = { vendorUsed: "smartproxy" };',
|
|
333
|
+
"",
|
|
334
|
+
].join("\n"),
|
|
335
|
+
},
|
|
336
|
+
{
|
|
337
|
+
filename: "negative-control-telemetry-header-any.ts",
|
|
338
|
+
expectedCode: "TS2322",
|
|
339
|
+
description: "gateway telemetry projections reject any-valued properties",
|
|
340
|
+
source: [
|
|
341
|
+
'import type { GatewayIngestible } from "@apifuse/provider-sdk";',
|
|
342
|
+
"",
|
|
343
|
+
'export const bad: GatewayIngestible<{ x: any }> = { x: "free-text" };',
|
|
344
|
+
"",
|
|
345
|
+
].join("\n"),
|
|
346
|
+
},
|
|
347
|
+
{
|
|
348
|
+
filename: "negative-control-telemetry-header-union-array.ts",
|
|
349
|
+
expectedCode: "TS2322",
|
|
350
|
+
description: "gateway telemetry projections reject arrays mixing numbers and strings",
|
|
351
|
+
source: [
|
|
352
|
+
'import type { TelemetryContributor } from "@apifuse/provider-sdk";',
|
|
353
|
+
"",
|
|
354
|
+
"const bad: TelemetryContributor<{}, { values: (number | string)[] }> = {",
|
|
355
|
+
'\tkey: "resolver",',
|
|
356
|
+
"\ttoLogPayload: () => ({}),",
|
|
357
|
+
'\ttoHeaderPayload: () => ({ values: [1, "free prose"] }),',
|
|
358
|
+
"};",
|
|
359
|
+
"",
|
|
360
|
+
].join("\n"),
|
|
361
|
+
},
|
|
362
|
+
{
|
|
363
|
+
filename: "negative-control-telemetry-opaque-identity.ts",
|
|
364
|
+
expectedCode: "TS2322",
|
|
365
|
+
description: "tenant opaque array brands are not accepted outside the cache keys property",
|
|
366
|
+
source: [
|
|
367
|
+
'import type { TenantNeutral } from "@apifuse/provider-sdk";',
|
|
368
|
+
"",
|
|
369
|
+
"type LocalOpaque = string[] & { readonly __tenantOpaqueCacheKeys: true };",
|
|
370
|
+
"declare const opaque: LocalOpaque;",
|
|
371
|
+
"export const bad: TenantNeutral<{ identity: LocalOpaque }> = { identity: opaque };",
|
|
372
|
+
"",
|
|
373
|
+
].join("\n"),
|
|
374
|
+
},
|
|
375
|
+
{
|
|
376
|
+
filename: "negative-control-telemetry-meta-vendor.ts",
|
|
377
|
+
expectedCode: "TS2322",
|
|
378
|
+
description: "success metadata rejects tenant-visible vendor identity keys",
|
|
379
|
+
source: [
|
|
380
|
+
'import { closedEnum, type ClosedEnum, type TenantNeutral } from "@apifuse/provider-sdk";',
|
|
381
|
+
"",
|
|
382
|
+
'type Meta = { cached: boolean; detail: { vendorUsed: ClosedEnum<"smartproxy"> } };',
|
|
383
|
+
'export const bad: TenantNeutral<Meta> = { cached: false, detail: { vendorUsed: closedEnum("smartproxy") } };',
|
|
384
|
+
"",
|
|
385
|
+
].join("\n"),
|
|
386
|
+
},
|
|
387
|
+
{
|
|
388
|
+
filename: "positive-control-proxy-telemetry-contributor.ts",
|
|
389
|
+
expectedCode: "",
|
|
390
|
+
description: "proxy telemetry contributor satisfies the public contributor contract",
|
|
391
|
+
source: [
|
|
392
|
+
'import { closedEnum, type ClosedEnum, type GatewayIngestible, type ProxyTelemetryHeaderPayload, type ProxyTelemetryLogPayload, type TelemetryContributor } from "@apifuse/provider-sdk";',
|
|
393
|
+
"",
|
|
394
|
+
"export const proxy: TelemetryContributor<ProxyTelemetryLogPayload, ProxyTelemetryHeaderPayload> = {",
|
|
395
|
+
'\tkey: "proxy",',
|
|
396
|
+
'\ttoLogPayload: () => ({ kind: "unresolved", vendors: [] }),',
|
|
397
|
+
'\ttoHeaderPayload: () => ({ kind: closedEnum("unresolved"), vendors: [] }),',
|
|
398
|
+
"};",
|
|
399
|
+
'type Values = { values: ClosedEnum<"a" | "b">[] };',
|
|
400
|
+
'export const values: GatewayIngestible<Values> = { values: [closedEnum("a"), closedEnum("b")] };',
|
|
401
|
+
"",
|
|
402
|
+
].join("\n"),
|
|
403
|
+
},
|
|
295
404
|
] as const;
|
|
296
405
|
|
|
297
406
|
const tempRoot = mkdtempSync(join(tmpdir(), "apifuse-provider-sdk-pack-types-"));
|
|
@@ -513,6 +622,15 @@ function assertNegativeControlFails(consumerDir: string): void {
|
|
|
513
622
|
],
|
|
514
623
|
{ cwd: consumerDir, encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] },
|
|
515
624
|
);
|
|
625
|
+
if (negativeControl.expectedCode === "") {
|
|
626
|
+
if (result.status !== 0) {
|
|
627
|
+
throw new Error(
|
|
628
|
+
`Positive control "${negativeControl.description}" failed to compile:\n${result.stdout}\n${result.stderr}`,
|
|
629
|
+
);
|
|
630
|
+
}
|
|
631
|
+
console.log(`Positive control accepted: ${negativeControl.description}`);
|
|
632
|
+
continue;
|
|
633
|
+
}
|
|
516
634
|
if (result.status === 0) {
|
|
517
635
|
throw new Error(
|
|
518
636
|
'Negative control "' +
|
|
@@ -528,6 +646,9 @@ function assertNegativeControlFails(consumerDir: string): void {
|
|
|
528
646
|
`Negative control "${negativeControl.description}" (${negativeControl.filename}) failed for an unexpected reason (wanted ${negativeControl.expectedCode}):\n${output}`,
|
|
529
647
|
);
|
|
530
648
|
}
|
|
649
|
+
console.log(
|
|
650
|
+
`Negative control rejected (${negativeControl.expectedCode}): ${negativeControl.description}`,
|
|
651
|
+
);
|
|
531
652
|
}
|
|
532
653
|
}
|
|
533
654
|
|
|
@@ -45,6 +45,7 @@ export type OperationDeclarationRepositoryResult = {
|
|
|
45
45
|
readonly providerRoot: string;
|
|
46
46
|
readonly changedFiles: readonly string[];
|
|
47
47
|
readonly operationCount: number;
|
|
48
|
+
readonly notes: readonly OperationDeclarationNote[];
|
|
48
49
|
readonly sidecar?: string;
|
|
49
50
|
readonly localeTodoCount: number;
|
|
50
51
|
} | {
|
|
@@ -53,9 +54,15 @@ export type OperationDeclarationRepositoryResult = {
|
|
|
53
54
|
readonly refusals: readonly OperationDeclarationRefusal[];
|
|
54
55
|
/** Operations that were independently migratable before repository-atomic refusal. */
|
|
55
56
|
readonly operationCount: number;
|
|
57
|
+
readonly notes: readonly OperationDeclarationNote[];
|
|
56
58
|
readonly changedFiles: readonly string[];
|
|
57
59
|
readonly localeTodoCount: number;
|
|
58
60
|
};
|
|
61
|
+
export type OperationDeclarationNote = {
|
|
62
|
+
readonly code: "runtime_composed_registry";
|
|
63
|
+
readonly path: string;
|
|
64
|
+
readonly initializer: string;
|
|
65
|
+
};
|
|
59
66
|
/** Run the file transform repository-wide, committing writes only if every file is provable. */
|
|
60
67
|
export declare function migrateOperationDeclarationRepository(providerRootInput: string, options?: {
|
|
61
68
|
readonly check?: boolean;
|
|
@@ -68,7 +68,8 @@ function migrateOperationDeclarationInternal(sourceText, fileName, options, cont
|
|
|
68
68
|
}
|
|
69
69
|
const constObjects = collectModuleConstObjects(source);
|
|
70
70
|
const constArrays = collectModuleConstArrays(source);
|
|
71
|
-
const discovery = discoverOperationSites(source, fileName, constObjects, options.operationIds, context.operationSites, context.excludedBindings);
|
|
71
|
+
const discovery = discoverOperationSites(source, fileName, constObjects, options.operationIds, context.operationSites, context.excludedBindings, context.runtimeComposedInitializers);
|
|
72
|
+
context.recordDiscoveredSites?.(discovery.discoveredCount);
|
|
72
73
|
if (discovery.refusals.length > 0) {
|
|
73
74
|
return { status: "refused", refusals: discovery.refusals };
|
|
74
75
|
}
|
|
@@ -599,14 +600,15 @@ function replaceExampleLocaleMember(member, newName, localeKey, source) {
|
|
|
599
600
|
text: `${newName}: ${JSON.stringify(localeKey)}`,
|
|
600
601
|
};
|
|
601
602
|
}
|
|
602
|
-
function discoverOperationSites(source, fileName, constObjects, operationIds, operationSites, excludedBindings) {
|
|
603
|
+
function discoverOperationSites(source, fileName, constObjects, operationIds, operationSites, excludedBindings, runtimeComposedInitializers) {
|
|
603
604
|
const sitesByStart = new Map();
|
|
605
|
+
const discoveredStarts = new Set();
|
|
604
606
|
const localIds = new Map(operationIds ?? []);
|
|
605
607
|
const localExcludedBindings = new Set(excludedBindings ?? []);
|
|
606
608
|
const refusals = [];
|
|
607
609
|
const operationFactories = collectSimpleOperationFactories(source);
|
|
608
610
|
const factoryCallIds = new Map();
|
|
609
|
-
for (const map of collectOperationsMaps(source, constObjects, fileName, refusals)) {
|
|
611
|
+
for (const map of collectOperationsMaps(source, constObjects, fileName, refusals, runtimeComposedInitializers)) {
|
|
610
612
|
for (const property of map.properties) {
|
|
611
613
|
if (ts.isSpreadAssignment(property))
|
|
612
614
|
continue;
|
|
@@ -680,12 +682,15 @@ function discoverOperationSites(source, fileName, constObjects, operationIds, op
|
|
|
680
682
|
if (ts.isCallExpression(node) && isOperationHelperCall(node)) {
|
|
681
683
|
const argument = operationArgument(node);
|
|
682
684
|
const bindingName = enclosingBindingName(node);
|
|
685
|
+
const unwrappedArgument = unwrapExpression(argument);
|
|
686
|
+
if (unwrappedArgument !== undefined && ts.isObjectLiteralExpression(unwrappedArgument)) {
|
|
687
|
+
discoveredStarts.add(unwrappedArgument.getStart(source));
|
|
688
|
+
}
|
|
683
689
|
if (bindingName !== undefined && localExcludedBindings.has(bindingName))
|
|
684
690
|
return;
|
|
685
691
|
const operationKey = (bindingName === undefined ? undefined : localIds.get(bindingName)) ??
|
|
686
692
|
bindingName ??
|
|
687
693
|
"<anonymous>";
|
|
688
|
-
const unwrappedArgument = unwrapExpression(argument);
|
|
689
694
|
if (unwrappedArgument === undefined || !ts.isObjectLiteralExpression(unwrappedArgument)) {
|
|
690
695
|
refusals.push(refusal(fileName, operationKey, "non_literal", "Operation helper argument must be an object literal."));
|
|
691
696
|
}
|
|
@@ -711,8 +716,11 @@ function discoverOperationSites(source, fileName, constObjects, operationIds, op
|
|
|
711
716
|
ts.forEachChild(node, visit);
|
|
712
717
|
};
|
|
713
718
|
visit(source);
|
|
719
|
+
for (const start of sitesByStart.keys())
|
|
720
|
+
discoveredStarts.add(start);
|
|
714
721
|
return {
|
|
715
722
|
sites: [...sitesByStart.values()].sort((left, right) => left.object.getStart(source) - right.object.getStart(source)),
|
|
723
|
+
discoveredCount: discoveredStarts.size,
|
|
716
724
|
refusals,
|
|
717
725
|
};
|
|
718
726
|
}
|
|
@@ -741,7 +749,7 @@ function collectSimpleOperationFactories(source) {
|
|
|
741
749
|
}
|
|
742
750
|
return factories;
|
|
743
751
|
}
|
|
744
|
-
function collectOperationsMaps(source, constObjects, fileName, refusals) {
|
|
752
|
+
function collectOperationsMaps(source, constObjects, fileName, refusals, runtimeComposedInitializers) {
|
|
745
753
|
const maps = new Map();
|
|
746
754
|
const inspect = (expression, label) => {
|
|
747
755
|
const unwrapped = unwrapExpression(expression);
|
|
@@ -758,6 +766,8 @@ function collectOperationsMaps(source, constObjects, fileName, refusals) {
|
|
|
758
766
|
return;
|
|
759
767
|
}
|
|
760
768
|
if (ts.isCallExpression(unwrapped)) {
|
|
769
|
+
if (runtimeComposedInitializers?.has(expression.getStart(source)) === true)
|
|
770
|
+
return;
|
|
761
771
|
refusals.push(refusal(fileName, label, "factory_composed_operations", "The operations map is produced by a factory call and cannot be statically enumerated."));
|
|
762
772
|
}
|
|
763
773
|
};
|
|
@@ -1274,6 +1284,7 @@ export function migrateOperationDeclarationRepository(providerRootInput, options
|
|
|
1274
1284
|
const todos = [];
|
|
1275
1285
|
const refusals = [...repositoryIndex.refusals];
|
|
1276
1286
|
let operationCount = 0;
|
|
1287
|
+
let repositoryDiscoveredCount = 0;
|
|
1277
1288
|
for (const sourcePath of sourceFiles) {
|
|
1278
1289
|
const relativePath = slash(relative(providerRoot, sourcePath));
|
|
1279
1290
|
const result = migrateOperationDeclarationInternal(readFileSync(sourcePath, "utf8"), relativePath, {
|
|
@@ -1283,6 +1294,10 @@ export function migrateOperationDeclarationRepository(providerRootInput, options
|
|
|
1283
1294
|
operationSites: repositoryIndex.operationSites.get(sourcePath),
|
|
1284
1295
|
excludedBindings: repositoryIndex.excludedBindings.get(sourcePath),
|
|
1285
1296
|
staticObjectResolver: repositoryIndex.staticObjectResolverFor(sourcePath),
|
|
1297
|
+
runtimeComposedInitializers: repositoryIndex.runtimeComposedInitializers.get(sourcePath),
|
|
1298
|
+
recordDiscoveredSites: (count) => {
|
|
1299
|
+
repositoryDiscoveredCount += count;
|
|
1300
|
+
},
|
|
1286
1301
|
});
|
|
1287
1302
|
if (result.status === "refused") {
|
|
1288
1303
|
refusals.push(...result.refusals);
|
|
@@ -1295,7 +1310,18 @@ export function migrateOperationDeclarationRepository(providerRootInput, options
|
|
|
1295
1310
|
todos.push(...result.localeTodos);
|
|
1296
1311
|
}
|
|
1297
1312
|
}
|
|
1298
|
-
|
|
1313
|
+
const notes = repositoryDiscoveredCount === 0
|
|
1314
|
+
? []
|
|
1315
|
+
: repositoryIndex.declarations.flatMap((declaration) => declaration.runtimeInitializer === undefined
|
|
1316
|
+
? []
|
|
1317
|
+
: [
|
|
1318
|
+
{
|
|
1319
|
+
code: "runtime_composed_registry",
|
|
1320
|
+
path: slash(relative(providerRoot, declaration.path)),
|
|
1321
|
+
initializer: declaration.runtimeInitializer,
|
|
1322
|
+
},
|
|
1323
|
+
]);
|
|
1324
|
+
if (repositoryIndex.declarations.length > 0 && repositoryDiscoveredCount === 0) {
|
|
1299
1325
|
for (const declaration of repositoryIndex.declarations) {
|
|
1300
1326
|
refusals.push(refusal(slash(relative(providerRoot, declaration.path)), "<operations>", "no_operations_discovered", `Provider construct ${declaration.construct} declares operations via unresolved initializer ${JSON.stringify(declaration.initializer.getText(declaration.initializer.getSourceFile()))}, but repository-wide discovery found zero operation sites.`));
|
|
1301
1327
|
}
|
|
@@ -1309,6 +1335,7 @@ export function migrateOperationDeclarationRepository(providerRootInput, options
|
|
|
1309
1335
|
providerRoot,
|
|
1310
1336
|
refusals,
|
|
1311
1337
|
operationCount,
|
|
1338
|
+
notes,
|
|
1312
1339
|
changedFiles,
|
|
1313
1340
|
localeTodoCount: todos.length,
|
|
1314
1341
|
};
|
|
@@ -1322,6 +1349,7 @@ export function migrateOperationDeclarationRepository(providerRootInput, options
|
|
|
1322
1349
|
providerRoot,
|
|
1323
1350
|
changedFiles: [],
|
|
1324
1351
|
operationCount,
|
|
1352
|
+
notes,
|
|
1325
1353
|
localeTodoCount: 0,
|
|
1326
1354
|
};
|
|
1327
1355
|
}
|
|
@@ -1338,6 +1366,7 @@ export function migrateOperationDeclarationRepository(providerRootInput, options
|
|
|
1338
1366
|
providerRoot,
|
|
1339
1367
|
changedFiles,
|
|
1340
1368
|
operationCount,
|
|
1369
|
+
notes,
|
|
1341
1370
|
sidecar,
|
|
1342
1371
|
localeTodoCount: todos.length,
|
|
1343
1372
|
};
|
|
@@ -1489,12 +1518,12 @@ function buildRepositoryOperationIndex(sourceFiles, providerRoot) {
|
|
|
1489
1518
|
const operationIds = buildOperationIdIndex(sourceFiles);
|
|
1490
1519
|
const operationSites = new Map();
|
|
1491
1520
|
const excludedBindings = new Map();
|
|
1521
|
+
const runtimeComposedInitializers = new Map();
|
|
1492
1522
|
const refusals = [];
|
|
1493
1523
|
const declarations = [];
|
|
1494
1524
|
const indexedProviderProperties = new Set();
|
|
1495
1525
|
const factoryIds = new Map();
|
|
1496
1526
|
const bindingCandidates = new Map();
|
|
1497
|
-
let discoveredCount = 0;
|
|
1498
1527
|
const relativePath = (path) => slash(relative(providerRoot, path));
|
|
1499
1528
|
const resolutionRefusal = (path, operationKey, detail) => {
|
|
1500
1529
|
refusals.push(refusal(relativePath(path), operationKey, "non_literal", detail));
|
|
@@ -1749,7 +1778,6 @@ function buildRepositoryOperationIndex(sourceFiles, providerRoot) {
|
|
|
1749
1778
|
}
|
|
1750
1779
|
sites.set(start, operationId);
|
|
1751
1780
|
operationSites.set(path, sites);
|
|
1752
|
-
discoveredCount += 1;
|
|
1753
1781
|
};
|
|
1754
1782
|
const classifyEntry = (entry) => {
|
|
1755
1783
|
const resolved = resolveLocatedExpression(entry.value, new Set());
|
|
@@ -1793,7 +1821,6 @@ function buildRepositoryOperationIndex(sourceFiles, providerRoot) {
|
|
|
1793
1821
|
};
|
|
1794
1822
|
factory.ids.add(entry.operationId);
|
|
1795
1823
|
factoryIds.set(key, factory);
|
|
1796
|
-
discoveredCount += 1;
|
|
1797
1824
|
return;
|
|
1798
1825
|
}
|
|
1799
1826
|
resolutionRefusal(resolved.value.path, entry.operationId, `Operation initializer ${JSON.stringify(expression.getText(resolved.value.source))} is not a raw object, operation helper, or simple same-file factory call.`);
|
|
@@ -1803,10 +1830,23 @@ function buildRepositoryOperationIndex(sourceFiles, providerRoot) {
|
|
|
1803
1830
|
if (indexedProviderProperties.has(key))
|
|
1804
1831
|
return;
|
|
1805
1832
|
indexedProviderProperties.add(key);
|
|
1806
|
-
|
|
1833
|
+
const resolvedInitializer = resolveLocatedExpression({ path, source, expression: node.initializer }, new Set());
|
|
1834
|
+
let runtimeInitializer;
|
|
1835
|
+
if (resolvedInitializer.status === "resolved") {
|
|
1836
|
+
const resolvedExpression = unwrapExpression(resolvedInitializer.value.expression);
|
|
1837
|
+
if (resolvedExpression !== undefined && ts.isCallExpression(resolvedExpression)) {
|
|
1838
|
+
runtimeInitializer = resolvedExpression.getText(resolvedInitializer.value.source);
|
|
1839
|
+
const initializers = runtimeComposedInitializers.get(path) ?? new Set();
|
|
1840
|
+
initializers.add(node.initializer.getStart(source));
|
|
1841
|
+
runtimeComposedInitializers.set(path, initializers);
|
|
1842
|
+
}
|
|
1843
|
+
}
|
|
1844
|
+
declarations.push({ path, construct, initializer: node.initializer, runtimeInitializer });
|
|
1807
1845
|
const flattened = flattenRegistry({ path, source, expression: node.initializer }, new Set());
|
|
1808
1846
|
if ("detail" in flattened) {
|
|
1809
|
-
|
|
1847
|
+
if (runtimeInitializer === undefined) {
|
|
1848
|
+
resolutionRefusal(path, "<operations>", flattened.detail);
|
|
1849
|
+
}
|
|
1810
1850
|
}
|
|
1811
1851
|
else {
|
|
1812
1852
|
for (const entry of flattened.entries.values())
|
|
@@ -1919,9 +1959,9 @@ function buildRepositoryOperationIndex(sourceFiles, providerRoot) {
|
|
|
1919
1959
|
operationIds,
|
|
1920
1960
|
operationSites,
|
|
1921
1961
|
excludedBindings,
|
|
1962
|
+
runtimeComposedInitializers,
|
|
1922
1963
|
refusals,
|
|
1923
1964
|
declarations,
|
|
1924
|
-
discoveredCount,
|
|
1925
1965
|
staticObjectResolverFor: (currentPath) => (expression, source) => resolveStaticObject(expression, sources.has(source.fileName) ? source.fileName : currentPath),
|
|
1926
1966
|
};
|
|
1927
1967
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -32,7 +32,8 @@ export { generateInsights } from "./runtime/insights.js";
|
|
|
32
32
|
export { type InstrumentationOptions, type InstrumentedProviderContext, wrapWithInstrumentation, } from "./runtime/instrumentation.js";
|
|
33
33
|
export type { PrevalidateResult } from "./runtime/prevalidate.js";
|
|
34
34
|
export { getProviderBaseUrl } from "./runtime/provider.js";
|
|
35
|
-
export type { ProxyTelemetryLogPayload, ProxyTelemetryResolvedPayload, ProxyTelemetryUnresolvedPayload, } from "./runtime/proxy-telemetry.js";
|
|
35
|
+
export type { ProxyTelemetryHeaderPayload, ProxyHash, ProxyTelemetryLogPayload, ProxyTelemetryResolvedPayload, ProxyTelemetryUnresolvedPayload, } from "./runtime/proxy-telemetry.js";
|
|
36
|
+
export { RequestTelemetry, closedEnum, type ClosedEnum, type GatewayIngestible, type RequestTelemetryLogPayload, type SpanIndex, type TelemetryContributor, type TelemetryKey, type TenantNeutral, } from "./runtime/request-telemetry.js";
|
|
36
37
|
export { APIFUSE__CDP_POOL__URL, APIFUSE__RESOLVER__2CAPTCHA__API_KEY, APIFUSE__RESOLVER__CAPMONSTER__API_KEY, APIFUSE__RESOLVER__CAPSOLVER__API_KEY, APIFUSE__RESOLVER__TIMEOUT_MS, DEFAULT_RESOLVER_TIMEOUT_MS, } from "./runtime/resolver-config.js";
|
|
37
38
|
export { createUnsupportedResolverClient } from "./runtime/resolver-shared.js";
|
|
38
39
|
export type { ResolverRuntimeOptions } from "./runtime/resolver.js";
|
package/dist/index.js
CHANGED
|
@@ -26,6 +26,7 @@ export { NativeEgressGrantExpiredError, NativeEgressNotDeclaredError, NativeIdle
|
|
|
26
26
|
export { generateInsights } from "./runtime/insights.js";
|
|
27
27
|
export { wrapWithInstrumentation, } from "./runtime/instrumentation.js";
|
|
28
28
|
export { getProviderBaseUrl } from "./runtime/provider.js";
|
|
29
|
+
export { RequestTelemetry, closedEnum, } from "./runtime/request-telemetry.js";
|
|
29
30
|
export { APIFUSE__CDP_POOL__URL, APIFUSE__RESOLVER__2CAPTCHA__API_KEY, APIFUSE__RESOLVER__CAPMONSTER__API_KEY, APIFUSE__RESOLVER__CAPSOLVER__API_KEY, APIFUSE__RESOLVER__TIMEOUT_MS, DEFAULT_RESOLVER_TIMEOUT_MS, } from "./runtime/resolver-config.js";
|
|
30
31
|
export { createUnsupportedResolverClient } from "./runtime/resolver-shared.js";
|
|
31
32
|
export { assertRequiredSecretsPresent, listMissingRequiredSecrets, MISSING_SECRET_CODE, } from "./runtime/secrets.js";
|
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import type { ProxyAttemptTelemetryEvent, ProxyCacheStatus, ProxyProtocol, ProxyResolutionTelemetryEvent, ProxyTelemetrySink, ProxyUserAgentSource, ProxyVendorFailoverTelemetryEvent, ProxyVendorName, SmartproxyAllocatorBodyClass } from "../config/loader.js";
|
|
2
|
-
|
|
2
|
+
import { type ClosedEnum, type TelemetryContributor } from "./request-telemetry.js";
|
|
3
|
+
export { PROVIDER_TELEMETRY_HEADER } from "./request-telemetry.js";
|
|
4
|
+
declare const PROXY_HASH: unique symbol;
|
|
5
|
+
/** Bounded hexadecimal host hash used in proxy attempt samples. */
|
|
6
|
+
export type ProxyHash = ClosedEnum<string> & {
|
|
7
|
+
readonly [PROXY_HASH]: true;
|
|
8
|
+
};
|
|
3
9
|
export type ProxyTelemetryResolvedPayload = {
|
|
4
10
|
kind: "resolved";
|
|
5
11
|
provider: ProxyVendorName;
|
|
@@ -77,11 +83,56 @@ export type ProxyTelemetryUnresolvedPayload = {
|
|
|
77
83
|
}[];
|
|
78
84
|
};
|
|
79
85
|
export type ProxyTelemetryLogPayload = ProxyTelemetryResolvedPayload | ProxyTelemetryUnresolvedPayload;
|
|
80
|
-
|
|
86
|
+
/** Concrete gateway-safe projection of the unchanged proxy sibling. */
|
|
87
|
+
export type ProxyTelemetryHeaderPayload = {
|
|
88
|
+
kind: ClosedEnum<"resolved" | "unresolved">;
|
|
89
|
+
provider?: ClosedEnum<ProxyVendorName>;
|
|
90
|
+
userAgentSource?: ClosedEnum<ProxyUserAgentSource>;
|
|
91
|
+
protocol?: ClosedEnum<ProxyProtocol>;
|
|
92
|
+
cacheStatus?: ClosedEnum<ProxyCacheStatus>;
|
|
93
|
+
cacheHit?: boolean;
|
|
94
|
+
resolutionMs?: number;
|
|
95
|
+
allocatorMs?: number;
|
|
96
|
+
allocatorStatus?: number;
|
|
97
|
+
allocatorBodyClass?: ClosedEnum<SmartproxyAllocatorBodyClass>;
|
|
98
|
+
allocatorAttempts?: number;
|
|
99
|
+
lockWaitMs?: number;
|
|
100
|
+
redisReadMs?: number;
|
|
101
|
+
redisWriteMs?: number;
|
|
102
|
+
poolAgeMs?: number;
|
|
103
|
+
poolExpiresInMs?: number;
|
|
104
|
+
attempts?: number;
|
|
105
|
+
refreshes?: number;
|
|
106
|
+
attemptSamples?: {
|
|
107
|
+
n: number;
|
|
108
|
+
a: number;
|
|
109
|
+
i?: number;
|
|
110
|
+
h?: ProxyHash;
|
|
111
|
+
o: ClosedEnum<ProxyAttemptTelemetryEvent["outcome"]>;
|
|
112
|
+
c?: ClosedEnum<string>;
|
|
113
|
+
s?: number;
|
|
114
|
+
d?: number;
|
|
115
|
+
}[];
|
|
116
|
+
vendors?: ClosedEnum<ProxyVendorName>[];
|
|
117
|
+
failovers?: {
|
|
118
|
+
v: ClosedEnum<ProxyVendorName>;
|
|
119
|
+
nx?: ClosedEnum<ProxyVendorName>;
|
|
120
|
+
p: ClosedEnum<ProxyVendorFailoverTelemetryEvent["phase"]>;
|
|
121
|
+
r: ClosedEnum<ProxyVendorFailoverTelemetryEvent["reason"]>;
|
|
122
|
+
a?: number;
|
|
123
|
+
}[];
|
|
124
|
+
};
|
|
125
|
+
export declare class ProxyTelemetryCollector implements ProxyTelemetrySink, TelemetryContributor<ProxyTelemetryLogPayload, ProxyTelemetryHeaderPayload> {
|
|
81
126
|
#private;
|
|
127
|
+
readonly key: "proxy";
|
|
82
128
|
recordProxyResolution(event: ProxyResolutionTelemetryEvent): void;
|
|
129
|
+
/** Number of raw resolution events retained for bounded-history verification. */
|
|
130
|
+
get retainedResolutionEventCount(): number;
|
|
131
|
+
/** Total resolutions included in the incremental aggregate. */
|
|
132
|
+
get resolutionEventCount(): number;
|
|
83
133
|
recordProxyVendorFailover(event: ProxyVendorFailoverTelemetryEvent): void;
|
|
84
134
|
recordProxyAttempt(event: ProxyAttemptTelemetryEvent): void;
|
|
85
135
|
toLogPayload(): ProxyTelemetryLogPayload | undefined;
|
|
136
|
+
toHeaderPayload(log: ProxyTelemetryLogPayload): ProxyTelemetryHeaderPayload;
|
|
86
137
|
toHeaderValue(): string | undefined;
|
|
87
138
|
}
|