@workglow/test-contract 0.6.1

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 (149) hide show
  1. package/README.md +232 -0
  2. package/dist/ai-provider/assertions/capabilityHonesty.d.ts +7 -0
  3. package/dist/ai-provider/assertions/dispose.d.ts +7 -0
  4. package/dist/ai-provider/assertions/inferAdvertisesRegistered.d.ts +15 -0
  5. package/dist/ai-provider/assertions/inferServesInferred.d.ts +20 -0
  6. package/dist/ai-provider/assertions/pricingMatchesModality.d.ts +36 -0
  7. package/dist/ai-provider/assertions/registryCoverage.d.ts +13 -0
  8. package/dist/ai-provider/assertions/sessionReuse.d.ts +7 -0
  9. package/dist/ai-provider/assertions/signalHonoring.d.ts +7 -0
  10. package/dist/ai-provider/assertions/structuredGeneration.d.ts +7 -0
  11. package/dist/ai-provider/assertions/textGenerationSmoke.d.ts +7 -0
  12. package/dist/ai-provider/assertions/toolCallAccumulator.d.ts +7 -0
  13. package/dist/ai-provider/assertions/toolCallMultiTurn.d.ts +7 -0
  14. package/dist/ai-provider/assertions/usageNormalization.d.ts +61 -0
  15. package/dist/ai-provider/fixtures.d.ts +8 -0
  16. package/dist/ai-provider/runAiProviderConformance.d.ts +7 -0
  17. package/dist/ai-provider/types.d.ts +75 -0
  18. package/dist/ai-provider.d.ts +25 -0
  19. package/dist/ai-provider.js +876 -0
  20. package/dist/ai-provider.js.map +26 -0
  21. package/dist/browser-context/ConformanceMockContext.d.ts +54 -0
  22. package/dist/browser-context/assertions/ariaRoundTrip.d.ts +7 -0
  23. package/dist/browser-context/assertions/capabilityHonesty.d.ts +7 -0
  24. package/dist/browser-context/assertions/itExpectFail.d.ts +8 -0
  25. package/dist/browser-context/assertions/networkIntrospection.d.ts +7 -0
  26. package/dist/browser-context/assertions/tabsLifecycle.d.ts +7 -0
  27. package/dist/browser-context/fixtures.d.ts +8 -0
  28. package/dist/browser-context/runIBrowserContextConformance.d.ts +7 -0
  29. package/dist/browser-context/types.d.ts +48 -0
  30. package/dist/browser-context.d.ts +18 -0
  31. package/dist/browser-context.js +567 -0
  32. package/dist/browser-context.js.map +17 -0
  33. package/dist/creditExhaustedSkip.d.ts +39 -0
  34. package/dist/creditExhaustedSkip.js +163 -0
  35. package/dist/creditExhaustedSkip.js.map +10 -0
  36. package/dist/entitlement-profile/assertions/denialShape.d.ts +13 -0
  37. package/dist/entitlement-profile/assertions/dispose.d.ts +16 -0
  38. package/dist/entitlement-profile/assertions/hierarchyHonoring.d.ts +11 -0
  39. package/dist/entitlement-profile/assertions/optionalNeverDenied.d.ts +11 -0
  40. package/dist/entitlement-profile/assertions/requestEntitlementShape.d.ts +12 -0
  41. package/dist/entitlement-profile/assertions/resourceScoping.d.ts +16 -0
  42. package/dist/entitlement-profile/assertions/subscribeGrant.d.ts +11 -0
  43. package/dist/entitlement-profile/assertions/subscribeReload.d.ts +11 -0
  44. package/dist/entitlement-profile/assertions/subscribeRevocation.d.ts +12 -0
  45. package/dist/entitlement-profile/assertions/surfaceCoverage.d.ts +7 -0
  46. package/dist/entitlement-profile/assertions/unsubscribeIdempotent.d.ts +7 -0
  47. package/dist/entitlement-profile/fixtures.d.ts +21 -0
  48. package/dist/entitlement-profile/runEntitlementProfileConformance.d.ts +7 -0
  49. package/dist/entitlement-profile/types.d.ts +45 -0
  50. package/dist/entitlement-profile.d.ts +23 -0
  51. package/dist/entitlement-profile.js +436 -0
  52. package/dist/entitlement-profile.js.map +22 -0
  53. package/dist/human-connector/MockHumanConnector.d.ts +29 -0
  54. package/dist/human-connector/assertions/abort.d.ts +7 -0
  55. package/dist/human-connector/assertions/capabilityHonesty.d.ts +7 -0
  56. package/dist/human-connector/assertions/concurrentIsolation.d.ts +7 -0
  57. package/dist/human-connector/assertions/multiTurnFollowUp.d.ts +7 -0
  58. package/dist/human-connector/assertions/notifyDisplayFastResolve.d.ts +7 -0
  59. package/dist/human-connector/assertions/roundtrip.d.ts +7 -0
  60. package/dist/human-connector/fixtures.d.ts +8 -0
  61. package/dist/human-connector/runHumanConnectorConformance.d.ts +9 -0
  62. package/dist/human-connector/types.d.ts +91 -0
  63. package/dist/human-connector.d.ts +19 -0
  64. package/dist/human-connector.js +843 -0
  65. package/dist/human-connector.js.map +20 -0
  66. package/dist/itExpectFail.d.ts +15 -0
  67. package/dist/itExpectFail.js +180 -0
  68. package/dist/itExpectFail.js.map +11 -0
  69. package/dist/storage-migrations/assertions/appliesAndRecords.d.ts +7 -0
  70. package/dist/storage-migrations/assertions/concurrentRunsSerialize.d.ts +18 -0
  71. package/dist/storage-migrations/assertions/ensureBookkeepingIdempotent.d.ts +7 -0
  72. package/dist/storage-migrations/assertions/failedMigrationLeavesNoPartialSchema.d.ts +14 -0
  73. package/dist/storage-migrations/assertions/failedMigrationNotRecorded.d.ts +7 -0
  74. package/dist/storage-migrations/assertions/idempotentRun.d.ts +7 -0
  75. package/dist/storage-migrations/assertions/incrementalApplication.d.ts +7 -0
  76. package/dist/storage-migrations/runMigrationRunnerContract.d.ts +9 -0
  77. package/dist/storage-migrations/types.d.ts +78 -0
  78. package/dist/storage-migrations.d.ts +18 -0
  79. package/dist/storage-migrations.js +473 -0
  80. package/dist/storage-migrations.js.map +20 -0
  81. package/dist/tabular-migrations/assertions/addAndDropIndex.d.ts +7 -0
  82. package/dist/tabular-migrations/assertions/addColumn.d.ts +7 -0
  83. package/dist/tabular-migrations/assertions/backfill.d.ts +7 -0
  84. package/dist/tabular-migrations/assertions/dropColumn.d.ts +7 -0
  85. package/dist/tabular-migrations/assertions/failedMigrationNotRecorded.d.ts +7 -0
  86. package/dist/tabular-migrations/assertions/freshDbFastPath.d.ts +7 -0
  87. package/dist/tabular-migrations/assertions/incrementalApplication.d.ts +7 -0
  88. package/dist/tabular-migrations/assertions/renameColumn.d.ts +7 -0
  89. package/dist/tabular-migrations/runTabularMigrationContract.d.ts +8 -0
  90. package/dist/tabular-migrations/types.d.ts +28 -0
  91. package/dist/tabular-migrations.d.ts +19 -0
  92. package/dist/tabular-migrations.js +257 -0
  93. package/dist/tabular-migrations.js.map +18 -0
  94. package/dist/tabular-storage/assertions/countMatchesQuery.d.ts +7 -0
  95. package/dist/tabular-storage/assertions/guardParity.d.ts +25 -0
  96. package/dist/tabular-storage/assertions/inListCriterion.d.ts +14 -0
  97. package/dist/tabular-storage/assertions/joinBoundedLeftRead.d.ts +29 -0
  98. package/dist/tabular-storage/assertions/joinPushdown.d.ts +13 -0
  99. package/dist/tabular-storage/assertions/notInListCriterion.d.ts +15 -0
  100. package/dist/tabular-storage/assertions/strategyParity.d.ts +31 -0
  101. package/dist/tabular-storage/assertions/subscribeToChanges.d.ts +21 -0
  102. package/dist/tabular-storage/assertions/vectorColumnFormat.d.ts +7 -0
  103. package/dist/tabular-storage/assertions/withConnectionTransaction.d.ts +7 -0
  104. package/dist/tabular-storage/assertions/withTransactionRollback.d.ts +7 -0
  105. package/dist/tabular-storage/fixtures.d.ts +33 -0
  106. package/dist/tabular-storage/joinFixtures.d.ts +66 -0
  107. package/dist/tabular-storage/runTabularJoinContract.d.ts +24 -0
  108. package/dist/tabular-storage/runTabularStorageContract.d.ts +9 -0
  109. package/dist/tabular-storage/types.d.ts +102 -0
  110. package/dist/tabular-storage.d.ts +25 -0
  111. package/dist/tabular-storage.js +1477 -0
  112. package/dist/tabular-storage.js.map +27 -0
  113. package/dist/vector-storage/assertions/dimensionValidation.d.ts +17 -0
  114. package/dist/vector-storage/assertions/legacyEncoding.d.ts +16 -0
  115. package/dist/vector-storage/assertions/metadataFilter.d.ts +16 -0
  116. package/dist/vector-storage/assertions/searchOptions.d.ts +16 -0
  117. package/dist/vector-storage/assertions/shared.d.ts +23 -0
  118. package/dist/vector-storage/assertions/similarityRanking.d.ts +18 -0
  119. package/dist/vector-storage/assertions/similaritySearchEvent.d.ts +16 -0
  120. package/dist/vector-storage/assertions/vectorRoundTrip.d.ts +17 -0
  121. package/dist/vector-storage/runVectorStorageContract.d.ts +20 -0
  122. package/dist/vector-storage/types.d.ts +87 -0
  123. package/dist/vector-storage.d.ts +19 -0
  124. package/dist/vector-storage.js +412 -0
  125. package/dist/vector-storage.js.map +21 -0
  126. package/dist/web-search/assertions/capabilityAgreement.d.ts +23 -0
  127. package/dist/web-search/assertions/credentialNaming.d.ts +21 -0
  128. package/dist/web-search/assertions/dateFiltering.d.ts +22 -0
  129. package/dist/web-search/assertions/domainNormalization.d.ts +24 -0
  130. package/dist/web-search/assertions/maxResultsCeiling.d.ts +22 -0
  131. package/dist/web-search/assertions/reportedQuery.d.ts +18 -0
  132. package/dist/web-search/assertions/signalThreading.d.ts +20 -0
  133. package/dist/web-search/fixtures.d.ts +40 -0
  134. package/dist/web-search/runWebSearchProviderConformance.d.ts +24 -0
  135. package/dist/web-search/types.d.ts +79 -0
  136. package/dist/web-search.d.ts +19 -0
  137. package/dist/web-search.js +560 -0
  138. package/dist/web-search.js.map +18 -0
  139. package/dist/worker-proxy/assertions/backlogOrdering.d.ts +7 -0
  140. package/dist/worker-proxy/assertions/disposeTerminatesWorker.d.ts +8 -0
  141. package/dist/worker-proxy/assertions/errorPropagation.d.ts +7 -0
  142. package/dist/worker-proxy/assertions/providerCallHelpers.d.ts +23 -0
  143. package/dist/worker-proxy/browserOnlyStub.d.ts +7 -0
  144. package/dist/worker-proxy/runWorkerProxyBoundary.d.ts +7 -0
  145. package/dist/worker-proxy/types.d.ts +47 -0
  146. package/dist/worker-proxy.d.ts +16 -0
  147. package/dist/worker-proxy.js +389 -0
  148. package/dist/worker-proxy.js.map +17 -0
  149. package/package.json +120 -0
@@ -0,0 +1,87 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Steven Roussey <sroussey@gmail.com>
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+ import type { AnyVectorStorage } from "@workglow/storage";
7
+ /**
8
+ * The schema every backend's own vector suite already declares, hoisted here so
9
+ * they stop declaring it separately. An `id`, the embedding, and a `metadata`
10
+ * object — the shape a filter is applied to.
11
+ */
12
+ export declare const VectorItemSchema: {
13
+ readonly type: "object";
14
+ readonly properties: {
15
+ readonly id: {
16
+ readonly type: "string";
17
+ };
18
+ readonly vector: {
19
+ readonly type: "array";
20
+ readonly format: "TypedArray";
21
+ readonly title: "Typed Array";
22
+ readonly description: "A typed array (Float32Array, Int8Array, etc.)";
23
+ };
24
+ readonly metadata: {
25
+ readonly type: "object";
26
+ readonly format: "metadata";
27
+ readonly additionalProperties: true;
28
+ };
29
+ };
30
+ readonly required: readonly ["id", "vector", "metadata"];
31
+ readonly additionalProperties: false;
32
+ };
33
+ export declare const VectorItemPrimaryKeyNames: readonly ["id"];
34
+ /** Four, so a unit vector's cosine score is exactly the coordinate it points along. */
35
+ export declare const VECTOR_DIMENSIONS = 4;
36
+ export declare const EAST: Float32Array<ArrayBuffer>;
37
+ export declare const NORTH: Float32Array<ArrayBuffer>;
38
+ export declare const NORTHEAST: Float32Array<ArrayBuffer>;
39
+ /**
40
+ * Closed union of vector-storage contract assertions an adapter can mark as
41
+ * known failing. A typo in `expectedFailures` becomes a TS error rather than a
42
+ * silently-ignored entry.
43
+ */
44
+ export type VectorStorageContractAssertion = "similarityRanking" | "vectorRoundTrip" | "searchOptions" | "metadataFilter" | "similaritySearchEvent" | "legacyEncoding" | "dimensionValidation";
45
+ export interface VectorStorageCapabilities {
46
+ /** Whether `options.filter` narrows by metadata. */
47
+ readonly supportsMetadataFilter: boolean;
48
+ /** Whether `options.scoreThreshold` drops rows below the score. */
49
+ readonly supportsScoreThreshold: boolean;
50
+ /**
51
+ * Whether a write whose vector is the wrong length is refused.
52
+ *
53
+ * A backend that stores the vector opaquely cannot check it, and one that
54
+ * declares this must refuse BEFORE writing — a row of the wrong width that
55
+ * lands is worse than one that never validates, because the search that
56
+ * trips over it is arbitrarily far away.
57
+ */
58
+ readonly supportsDimensionValidation: boolean;
59
+ }
60
+ export interface VectorStorageContractOpts {
61
+ readonly name: string;
62
+ readonly skip?: boolean;
63
+ readonly timeout?: number;
64
+ readonly capabilities: VectorStorageCapabilities;
65
+ /** A fresh, empty store of {@link VectorItemSchema} at {@link VECTOR_DIMENSIONS}. */
66
+ readonly createStorage: () => Promise<AnyVectorStorage>;
67
+ /**
68
+ * Releases whatever the factory allocated that the storage does not own —
69
+ * typically a connection handle. Called from every `afterEach`, passed the
70
+ * instance the factory returned.
71
+ */
72
+ readonly releaseStorage?: (storage: AnyVectorStorage) => void | Promise<void>;
73
+ /**
74
+ * Writes a row past this class's writer, in the column's stored form.
75
+ *
76
+ * Only a backend that has a stored form to write can supply it, and it is
77
+ * what the {@link VectorStorageContractAssertion} `legacyEncoding` block
78
+ * needs: the decode has to accept a row that never round-tripped through the
79
+ * current writer, and a test that inserts through the writer cannot ask that.
80
+ */
81
+ readonly writeRawRow?: (storage: AnyVectorStorage, row: {
82
+ readonly id: string;
83
+ readonly vector: readonly number[];
84
+ readonly metadata: unknown;
85
+ }) => Promise<void> | void;
86
+ readonly expectedFailures?: ReadonlyArray<VectorStorageContractAssertion>;
87
+ }
@@ -0,0 +1,19 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Steven Roussey <sroussey@gmail.com>
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+ /**
7
+ * Conformance suite for `IVectorStorage` — vector round trip, similarity
8
+ * ranking, metadata filtering and dimension validation.
9
+ */
10
+ export * from "./vector-storage/runVectorStorageContract";
11
+ export * from "./vector-storage/types";
12
+ export * from "./vector-storage/assertions/dimensionValidation";
13
+ export * from "./vector-storage/assertions/legacyEncoding";
14
+ export * from "./vector-storage/assertions/metadataFilter";
15
+ export * from "./vector-storage/assertions/searchOptions";
16
+ export * from "./vector-storage/assertions/shared";
17
+ export * from "./vector-storage/assertions/similarityRanking";
18
+ export * from "./vector-storage/assertions/similaritySearchEvent";
19
+ export * from "./vector-storage/assertions/vectorRoundTrip";
@@ -0,0 +1,412 @@
1
+ // src/vector-storage/runVectorStorageContract.ts
2
+ import { describe as describe8 } from "vitest";
3
+
4
+ // src/vector-storage/assertions/dimensionValidation.ts
5
+ import { StorageValidationError } from "@workglow/storage";
6
+ import { afterEach as afterEach2, beforeEach as beforeEach2, describe, expect } from "vitest";
7
+
8
+ // src/vector-storage/types.ts
9
+ import { TypedArraySchema } from "@workglow/util/schema";
10
+ var VectorItemSchema = {
11
+ type: "object",
12
+ properties: {
13
+ id: { type: "string" },
14
+ vector: TypedArraySchema(),
15
+ metadata: { type: "object", format: "metadata", additionalProperties: true }
16
+ },
17
+ required: ["id", "vector", "metadata"],
18
+ additionalProperties: false
19
+ };
20
+ var VectorItemPrimaryKeyNames = ["id"];
21
+ var VECTOR_DIMENSIONS = 4;
22
+ var EAST = new Float32Array([1, 0, 0, 0]);
23
+ var NORTH = new Float32Array([0, 1, 0, 0]);
24
+ var NORTHEAST = new Float32Array([Math.SQRT1_2, Math.SQRT1_2, 0, 0]);
25
+
26
+ // src/vector-storage/assertions/shared.ts
27
+ import { afterEach, beforeEach, it as it2 } from "vitest";
28
+
29
+ // src/creditExhaustedSkip.ts
30
+ import { it as vitestIt } from "vitest";
31
+ var BILLING_CODES = new Set([
32
+ "insufficient_quota",
33
+ "billing_hard_limit_reached",
34
+ "insufficient_credits",
35
+ "insufficient_balance"
36
+ ]);
37
+ var CREDIT_MESSAGE = /insufficient credits|not enough credits|no credits remaining|out of credits|insufficient[\s_-]balance|balance is (?:too low|insufficient)|exceeded[\s\S]{0,80}credits|monthly included credits|payment required|exceeded your current quota|check your plan and billing|billing_hard_limit/i;
38
+ var HTTP_402_MESSAGE = /\bHTTP\/?\d?(?:\.\d)?\s*402\b|\bstatus(?:\s*code)?\s*[:=]\s*402\b|\berror code:?\s*402\b|(?:^|:\s+)402\s+[a-z]/im;
39
+ function isCreditExhaustedError(err) {
40
+ const seen = new Set;
41
+ let current = err;
42
+ while (current !== undefined && current !== null && !seen.has(current)) {
43
+ seen.add(current);
44
+ if (matchesCreditExhaustion(current))
45
+ return true;
46
+ current = causeOf(current);
47
+ }
48
+ return false;
49
+ }
50
+ function matchesCreditExhaustion(err) {
51
+ if (typeof err === "string")
52
+ return messageLooksLikeCreditExhaustion(err);
53
+ if (!err || typeof err !== "object")
54
+ return false;
55
+ const rec = err;
56
+ const status = numericStatus(rec);
57
+ if (status === 402)
58
+ return true;
59
+ const code = stringish(rec.code) ?? stringish(nested(rec.error, "code"));
60
+ const type = stringish(rec.type) ?? stringish(nested(rec.error, "type"));
61
+ if (code && BILLING_CODES.has(code))
62
+ return true;
63
+ if (type && BILLING_CODES.has(type))
64
+ return true;
65
+ const message = [
66
+ stringish(rec.message),
67
+ stringish(nested(rec.error, "message")),
68
+ stringifyUnknown(err)
69
+ ].filter((part) => typeof part === "string").join(`
70
+ `);
71
+ return messageLooksLikeCreditExhaustion(message);
72
+ }
73
+ function messageLooksLikeCreditExhaustion(message) {
74
+ return CREDIT_MESSAGE.test(message) || HTTP_402_MESSAGE.test(message);
75
+ }
76
+ function numericStatus(rec) {
77
+ for (const key of ["status", "statusCode"]) {
78
+ const value = rec[key];
79
+ if (typeof value === "number" && Number.isFinite(value))
80
+ return value;
81
+ }
82
+ const nestedStatus = nested(rec.error, "status");
83
+ if (typeof nestedStatus === "number" && Number.isFinite(nestedStatus))
84
+ return nestedStatus;
85
+ return;
86
+ }
87
+ function nested(value, key) {
88
+ if (!value || typeof value !== "object")
89
+ return;
90
+ return value[key];
91
+ }
92
+ function stringish(value) {
93
+ return typeof value === "string" && value.length > 0 ? value : undefined;
94
+ }
95
+ function stringifyUnknown(err) {
96
+ if (err instanceof Error)
97
+ return err.message;
98
+ return String(err);
99
+ }
100
+ function causeOf(err) {
101
+ if (!err || typeof err !== "object")
102
+ return;
103
+ return err.cause;
104
+ }
105
+ function creditSkipNote(err) {
106
+ const message = err instanceof Error ? err.message : String(err);
107
+ return `skipped: provider out of credits (${message})`;
108
+ }
109
+ var SKIP_OVERRIDE_ENV = "WORKGLOW_CREDIT_EXHAUSTED_SKIP";
110
+ function isTruthyFlag(value) {
111
+ return !/^(?:0|false|off|no)$/i.test(value.trim());
112
+ }
113
+ function creditExhaustionSkipsTest(env = process.env) {
114
+ const override = env[SKIP_OVERRIDE_ENV];
115
+ if (override !== undefined && override.trim() !== "")
116
+ return isTruthyFlag(override);
117
+ for (const key of ["CI", "GITHUB_ACTIONS"]) {
118
+ const value = env[key];
119
+ if (value !== undefined && value.trim() !== "" && isTruthyFlag(value))
120
+ return true;
121
+ }
122
+ return false;
123
+ }
124
+ function reportLocalCreditFailure(err) {
125
+ const message = err instanceof Error ? err.message : String(err);
126
+ console.error(`provider out of credits (${message})
127
+ ` + ` Failing because this is not CI. Top the account up, or set ${SKIP_OVERRIDE_ENV}=1 to skip instead.`);
128
+ }
129
+ async function runWithCreditSkip(ctx, fn) {
130
+ try {
131
+ return await fn();
132
+ } catch (err) {
133
+ if (isCreditExhaustedError(err)) {
134
+ if (!creditExhaustionSkipsTest()) {
135
+ reportLocalCreditFailure(err);
136
+ throw err;
137
+ }
138
+ ctx.skip(creditSkipNote(err));
139
+ return;
140
+ }
141
+ throw err;
142
+ }
143
+ }
144
+ function wrapTestBodyForCreditSkip(fn) {
145
+ return async function wrapped(...args) {
146
+ try {
147
+ return await fn.apply(this, args);
148
+ } catch (err) {
149
+ if (!isCreditExhaustedError(err))
150
+ throw err;
151
+ if (!creditExhaustionSkipsTest()) {
152
+ reportLocalCreditFailure(err);
153
+ throw err;
154
+ }
155
+ const ctx = args[0];
156
+ if (ctx && typeof ctx.skip === "function") {
157
+ ctx.skip(creditSkipNote(err));
158
+ return;
159
+ }
160
+ console.warn(creditSkipNote(err));
161
+ return;
162
+ }
163
+ };
164
+ }
165
+ function wrapCallable(fn) {
166
+ return new Proxy(fn, {
167
+ apply(target, thisArg, argArray) {
168
+ const wrapped = argArray.map((arg) => typeof arg === "function" ? wrapTestBodyForCreditSkip(arg) : arg);
169
+ const result = Reflect.apply(target, thisArg, wrapped);
170
+ return typeof result === "function" ? wrapCallable(result) : result;
171
+ },
172
+ get(target, prop, receiver) {
173
+ const value = Reflect.get(target, prop, receiver);
174
+ if (typeof value === "function") {
175
+ return wrapCallable(value.bind(target));
176
+ }
177
+ return value;
178
+ }
179
+ });
180
+ }
181
+ var it = wrapCallable(vitestIt);
182
+
183
+ // src/itExpectFail.ts
184
+ var itExpectFail = (name, fn, timeout) => {
185
+ const native = it.fails;
186
+ if (typeof native === "function") {
187
+ native(name, fn, timeout);
188
+ return;
189
+ }
190
+ it(`${name} [expected-fail]`, async () => {
191
+ let passed = false;
192
+ try {
193
+ await fn();
194
+ passed = true;
195
+ } catch (err) {
196
+ if (isCreditExhaustedError(err))
197
+ throw err;
198
+ }
199
+ if (passed) {
200
+ throw new Error(`Test "${name}" was marked as expected-fail but passed. Remove its name from opts.expectedFailures.`);
201
+ }
202
+ }, timeout);
203
+ };
204
+
205
+ // src/vector-storage/assertions/shared.ts
206
+ function itFor(opts, assertion) {
207
+ return new Set(opts.expectedFailures ?? []).has(assertion) ? itExpectFail : it2;
208
+ }
209
+ function seededStore(opts) {
210
+ let storage;
211
+ beforeEach(async () => {
212
+ storage = await opts.createStorage();
213
+ await storage.setupDatabase?.();
214
+ await storage.putBulk([
215
+ { id: "east", vector: EAST, metadata: { region: "e" } },
216
+ { id: "north", vector: NORTH, metadata: { region: "n" } },
217
+ { id: "northeast", vector: NORTHEAST, metadata: { region: "ne" } }
218
+ ]);
219
+ });
220
+ afterEach(async () => {
221
+ await storage.deleteAll();
222
+ storage.destroy?.();
223
+ await opts.releaseStorage?.(storage);
224
+ });
225
+ return () => storage;
226
+ }
227
+
228
+ // src/vector-storage/assertions/dimensionValidation.ts
229
+ var SHORT = new Float32Array([1, 2, 3]);
230
+ var LONG = new Float32Array([1, 2, 3, 4, 5]);
231
+ function dimensionValidationBlock(opts) {
232
+ describe.skipIf(!opts.capabilities.supportsDimensionValidation)("dimensionValidation", () => {
233
+ const itImpl = itFor(opts, "dimensionValidation");
234
+ let storage;
235
+ beforeEach2(async () => {
236
+ storage = await opts.createStorage();
237
+ await storage.setupDatabase?.();
238
+ });
239
+ afterEach2(async () => {
240
+ await storage.deleteAll();
241
+ storage.destroy?.();
242
+ await opts.releaseStorage?.(storage);
243
+ });
244
+ itImpl("declares the width it was built with", async () => {
245
+ expect(storage.getVectorDimensions()).toBe(VECTOR_DIMENSIONS);
246
+ }, opts.timeout);
247
+ for (const [label, vector] of [
248
+ ["one entry short", SHORT],
249
+ ["one entry long", LONG]
250
+ ]) {
251
+ itImpl(`refuses a put whose vector is ${label}, and writes nothing`, async () => {
252
+ await expect(storage.put({ id: "x", vector, metadata: {} })).rejects.toBeInstanceOf(StorageValidationError);
253
+ expect(await storage.getAll() ?? []).toHaveLength(0);
254
+ }, opts.timeout);
255
+ itImpl(`refuses a search whose query vector is ${label}`, async () => {
256
+ await expect(storage.similaritySearch(vector)).rejects.toBeInstanceOf(StorageValidationError);
257
+ }, opts.timeout);
258
+ itImpl(`refuses a putBulk containing a vector that is ${label}, and writes nothing`, async () => {
259
+ await expect(storage.putBulk([
260
+ { id: "good", vector: EAST, metadata: {} },
261
+ { id: "bad", vector, metadata: {} }
262
+ ])).rejects.toBeInstanceOf(StorageValidationError);
263
+ expect(await storage.getAll() ?? []).toHaveLength(0);
264
+ }, opts.timeout);
265
+ }
266
+ });
267
+ }
268
+
269
+ // src/vector-storage/assertions/legacyEncoding.ts
270
+ import { describe as describe2, expect as expect2 } from "vitest";
271
+ function legacyEncodingBlock(opts) {
272
+ describe2.skipIf(opts.writeRawRow === undefined)("legacyEncoding", () => {
273
+ const store = seededStore(opts);
274
+ itFor(opts, "legacyEncoding")("searches a row that never round-tripped through this writer", async () => {
275
+ await opts.writeRawRow?.(store(), {
276
+ id: "legacy",
277
+ vector: [...NORTH],
278
+ metadata: { region: "legacy" }
279
+ });
280
+ const hits = await store().similaritySearch(NORTH, { topK: 2 });
281
+ expect2(hits.map((hit) => hit.id)).toContain("legacy");
282
+ const legacy = hits.find((hit) => hit.id === "legacy");
283
+ expect2([...legacy.vector]).toEqual([...NORTH]);
284
+ }, opts.timeout);
285
+ });
286
+ }
287
+
288
+ // src/vector-storage/assertions/metadataFilter.ts
289
+ import { describe as describe3, expect as expect3 } from "vitest";
290
+ function metadataFilterBlock(opts) {
291
+ describe3.skipIf(!opts.capabilities.supportsMetadataFilter)("metadataFilter", () => {
292
+ const store = seededStore(opts);
293
+ const itImpl = itFor(opts, "metadataFilter");
294
+ itImpl("returns only the rows the filter matches", async () => {
295
+ const hits = await store().similaritySearch(EAST, { filter: { region: "n" } });
296
+ expect3(hits.map((hit) => hit.id)).toEqual(["north"]);
297
+ }, opts.timeout);
298
+ itImpl("finds a match the ranking would not have reached", async () => {
299
+ const hits = await store().similaritySearch(EAST, {
300
+ filter: { region: "n" },
301
+ topK: 1
302
+ });
303
+ expect3(hits.map((hit) => hit.id)).toEqual(["north"]);
304
+ }, opts.timeout);
305
+ itImpl("returns nothing when the filter matches nothing", async () => {
306
+ expect3(await store().similaritySearch(EAST, { filter: { region: "nowhere" } })).toHaveLength(0);
307
+ }, opts.timeout);
308
+ });
309
+ }
310
+
311
+ // src/vector-storage/assertions/searchOptions.ts
312
+ import { describe as describe4, expect as expect4 } from "vitest";
313
+ function searchOptionsBlock(opts) {
314
+ describe4("searchOptions", () => {
315
+ const store = seededStore(opts);
316
+ const itImpl = itFor(opts, "searchOptions");
317
+ itImpl("truncates to topK, keeping the most similar", async () => {
318
+ const hits = await store().similaritySearch(EAST, { topK: 2 });
319
+ expect4(hits.map((hit) => hit.id)).toEqual(["east", "northeast"]);
320
+ }, opts.timeout);
321
+ itImpl("returns everything when topK exceeds the row count", async () => {
322
+ expect4(await store().similaritySearch(EAST, { topK: 10 })).toHaveLength(3);
323
+ }, opts.timeout);
324
+ describe4.skipIf(!opts.capabilities.supportsScoreThreshold)("scoreThreshold", () => {
325
+ itImpl("drops rows scoring below the threshold", async () => {
326
+ const hits = await store().similaritySearch(EAST, { scoreThreshold: 0.9 });
327
+ expect4(hits.map((hit) => hit.id)).toEqual(["east"]);
328
+ }, opts.timeout);
329
+ itImpl("keeps a row scoring exactly the threshold", async () => {
330
+ const hits = await store().similaritySearch(EAST, { scoreThreshold: 0 });
331
+ expect4(hits.map((hit) => hit.id)).toEqual(["east", "northeast", "north"]);
332
+ }, opts.timeout);
333
+ });
334
+ });
335
+ }
336
+
337
+ // src/vector-storage/assertions/similarityRanking.ts
338
+ import { describe as describe5, expect as expect5 } from "vitest";
339
+ function similarityRankingBlock(opts) {
340
+ describe5("similarityRanking", () => {
341
+ const store = seededStore(opts);
342
+ itFor(opts, "similarityRanking")("returns the stored rows ranked by similarity, with the scores that ordered them", async () => {
343
+ const hits = await store().similaritySearch(EAST);
344
+ expect5(hits.map((hit) => hit.id)).toEqual(["east", "northeast", "north"]);
345
+ expect5(hits[0].score).toBeCloseTo(1, 5);
346
+ expect5(hits[1].score).toBeCloseTo(Math.SQRT1_2, 5);
347
+ expect5(hits[2].score).toBeCloseTo(0, 5);
348
+ }, opts.timeout);
349
+ });
350
+ }
351
+
352
+ // src/vector-storage/assertions/similaritySearchEvent.ts
353
+ import { describe as describe6, expect as expect6 } from "vitest";
354
+ function similaritySearchEventBlock(opts) {
355
+ describe6("similaritySearchEvent", () => {
356
+ const store = seededStore(opts);
357
+ itFor(opts, "similaritySearchEvent")("fires with the results the caller was handed", async () => {
358
+ let seen;
359
+ store().on("similaritySearch", (_query, results) => {
360
+ seen = results;
361
+ });
362
+ await store().similaritySearch(EAST, { topK: 1 });
363
+ expect6(seen?.map((hit) => hit.id)).toEqual(["east"]);
364
+ }, opts.timeout);
365
+ });
366
+ }
367
+
368
+ // src/vector-storage/assertions/vectorRoundTrip.ts
369
+ import { describe as describe7, expect as expect7 } from "vitest";
370
+ function vectorRoundTripBlock(opts) {
371
+ describe7("vectorRoundTrip", () => {
372
+ const store = seededStore(opts);
373
+ itFor(opts, "vectorRoundTrip")("hands back the vector as a TypedArray with the values that were written", async () => {
374
+ const [hit] = await store().similaritySearch(EAST, { topK: 1 });
375
+ expect7(ArrayBuffer.isView(hit.vector)).toBe(true);
376
+ expect7([...hit.vector]).toEqual([...EAST]);
377
+ }, opts.timeout);
378
+ });
379
+ }
380
+
381
+ // src/vector-storage/runVectorStorageContract.ts
382
+ function runVectorStorageContract(opts) {
383
+ describe8.skipIf(opts.skip)(`Vector storage contract: ${opts.name}`, () => {
384
+ similarityRankingBlock(opts);
385
+ vectorRoundTripBlock(opts);
386
+ searchOptionsBlock(opts);
387
+ metadataFilterBlock(opts);
388
+ similaritySearchEventBlock(opts);
389
+ legacyEncodingBlock(opts);
390
+ dimensionValidationBlock(opts);
391
+ });
392
+ }
393
+ export {
394
+ EAST,
395
+ NORTH,
396
+ NORTHEAST,
397
+ VECTOR_DIMENSIONS,
398
+ VectorItemPrimaryKeyNames,
399
+ VectorItemSchema,
400
+ dimensionValidationBlock,
401
+ itFor,
402
+ legacyEncodingBlock,
403
+ metadataFilterBlock,
404
+ runVectorStorageContract,
405
+ searchOptionsBlock,
406
+ seededStore,
407
+ similarityRankingBlock,
408
+ similaritySearchEventBlock,
409
+ vectorRoundTripBlock
410
+ };
411
+
412
+ //# debugId=DFF5A9885C0171A264756E2164756E21
@@ -0,0 +1,21 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../src/vector-storage/runVectorStorageContract.ts", "../src/vector-storage/assertions/dimensionValidation.ts", "../src/vector-storage/types.ts", "../src/vector-storage/assertions/shared.ts", "../src/creditExhaustedSkip.ts", "../src/itExpectFail.ts", "../src/vector-storage/assertions/legacyEncoding.ts", "../src/vector-storage/assertions/metadataFilter.ts", "../src/vector-storage/assertions/searchOptions.ts", "../src/vector-storage/assertions/similarityRanking.ts", "../src/vector-storage/assertions/similaritySearchEvent.ts", "../src/vector-storage/assertions/vectorRoundTrip.ts"],
4
+ "sourcesContent": [
5
+ "/**\n * @license\n * Copyright 2026 Steven Roussey <sroussey@gmail.com>\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport { describe } from \"vitest\";\nimport { dimensionValidationBlock } from \"./assertions/dimensionValidation\";\nimport { legacyEncodingBlock } from \"./assertions/legacyEncoding\";\nimport { metadataFilterBlock } from \"./assertions/metadataFilter\";\nimport { searchOptionsBlock } from \"./assertions/searchOptions\";\nimport { similarityRankingBlock } from \"./assertions/similarityRanking\";\nimport { similaritySearchEventBlock } from \"./assertions/similaritySearchEvent\";\nimport { vectorRoundTripBlock } from \"./assertions/vectorRoundTrip\";\nimport type { VectorStorageContractOpts } from \"./types\";\n\n/**\n * The conformance suite for {@link IVectorStorage}, in the shape\n * `runTabularStorageContract` already has.\n *\n * Eight classes implement the interface and each had its own hand-written file,\n * no two checking the same things — which is how a release shipped in which\n * every single query failed on one backend: its sibling suite only ever wrote\n * and read back, so nothing there issued a search. The fix added seven cases,\n * all against that one backend, and the other seven implementations gained\n * nothing.\n */\nexport function runVectorStorageContract(opts: VectorStorageContractOpts): void {\n describe.skipIf(opts.skip)(`Vector storage contract: ${opts.name}`, () => {\n similarityRankingBlock(opts);\n vectorRoundTripBlock(opts);\n searchOptionsBlock(opts);\n metadataFilterBlock(opts);\n similaritySearchEventBlock(opts);\n legacyEncodingBlock(opts);\n dimensionValidationBlock(opts);\n });\n}\n\nexport {\n EAST,\n NORTH,\n NORTHEAST,\n VECTOR_DIMENSIONS,\n VectorItemPrimaryKeyNames,\n VectorItemSchema,\n} from \"./types\";\nexport type {\n VectorStorageCapabilities,\n VectorStorageContractAssertion,\n VectorStorageContractOpts,\n} from \"./types\";\n",
6
+ "/**\n * @license\n * Copyright 2026 Steven Roussey <sroussey@gmail.com>\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport type { AnyVectorStorage } from \"@workglow/storage\";\nimport { StorageValidationError } from \"@workglow/storage\";\nimport { afterEach, beforeEach, describe, expect } from \"vitest\";\nimport type { VectorStorageContractOpts } from \"../types\";\nimport { EAST, VECTOR_DIMENSIONS } from \"../types\";\nimport { itFor } from \"./shared\";\n\nconst SHORT = new Float32Array([1, 2, 3]);\nconst LONG = new Float32Array([1, 2, 3, 4, 5]);\n\n/**\n * A vector of the wrong width is refused, and refused BEFORE the row lands.\n *\n * The store's width is fixed at construction and every row has to have it, so a\n * row of another width is not a bad read waiting to happen — it is a bad read\n * arbitrarily far away, surfacing in whichever query first reaches it, with\n * nothing left to say where it came from. Rejecting late is barely better than\n * not rejecting: the assertion below reads the table back, because a throw\n * after the insert satisfies `rejects` and still leaves the row.\n */\nexport function dimensionValidationBlock(opts: VectorStorageContractOpts): void {\n describe.skipIf(!opts.capabilities.supportsDimensionValidation)(\"dimensionValidation\", () => {\n const itImpl = itFor(opts, \"dimensionValidation\");\n let storage: AnyVectorStorage;\n\n beforeEach(async () => {\n storage = await opts.createStorage();\n await storage.setupDatabase?.();\n });\n\n afterEach(async () => {\n await storage.deleteAll();\n storage.destroy?.();\n await opts.releaseStorage?.(storage);\n });\n\n itImpl(\n \"declares the width it was built with\",\n async () => {\n expect(storage.getVectorDimensions()).toBe(VECTOR_DIMENSIONS);\n },\n opts.timeout\n );\n\n for (const [label, vector] of [\n [\"one entry short\", SHORT],\n [\"one entry long\", LONG],\n ] as const) {\n itImpl(\n `refuses a put whose vector is ${label}, and writes nothing`,\n async () => {\n await expect(storage.put({ id: \"x\", vector, metadata: {} })).rejects.toBeInstanceOf(\n StorageValidationError\n );\n expect((await storage.getAll()) ?? []).toHaveLength(0);\n },\n opts.timeout\n );\n\n itImpl(\n `refuses a search whose query vector is ${label}`,\n async () => {\n // The query side is the same rule and a different code path: a\n // wrong-width query either throws or is quietly coerced by the\n // engine into a search that returns confident nonsense.\n await expect(storage.similaritySearch(vector)).rejects.toBeInstanceOf(\n StorageValidationError\n );\n },\n opts.timeout\n );\n\n itImpl(\n `refuses a putBulk containing a vector that is ${label}, and writes nothing`,\n async () => {\n // Not even the good row: a bulk write that half-lands leaves the\n // caller with no way to know which half.\n await expect(\n storage.putBulk([\n { id: \"good\", vector: EAST, metadata: {} },\n { id: \"bad\", vector, metadata: {} },\n ])\n ).rejects.toBeInstanceOf(StorageValidationError);\n expect((await storage.getAll()) ?? []).toHaveLength(0);\n },\n opts.timeout\n );\n }\n });\n}\n",
7
+ "/**\n * @license\n * Copyright 2026 Steven Roussey <sroussey@gmail.com>\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport type { AnyVectorStorage } from \"@workglow/storage\";\nimport type { DataPortSchemaObject } from \"@workglow/util/schema\";\nimport { TypedArraySchema } from \"@workglow/util/schema\";\n\n/**\n * The schema every backend's own vector suite already declares, hoisted here so\n * they stop declaring it separately. An `id`, the embedding, and a `metadata`\n * object — the shape a filter is applied to.\n */\nexport const VectorItemSchema = {\n type: \"object\",\n properties: {\n id: { type: \"string\" },\n vector: TypedArraySchema(),\n metadata: { type: \"object\", format: \"metadata\", additionalProperties: true },\n },\n required: [\"id\", \"vector\", \"metadata\"],\n additionalProperties: false,\n} as const satisfies DataPortSchemaObject;\n\nexport const VectorItemPrimaryKeyNames = [\"id\"] as const;\n\n/** Four, so a unit vector's cosine score is exactly the coordinate it points along. */\nexport const VECTOR_DIMENSIONS = 4;\n\nexport const EAST = new Float32Array([1, 0, 0, 0]);\nexport const NORTH = new Float32Array([0, 1, 0, 0]);\nexport const NORTHEAST = new Float32Array([Math.SQRT1_2, Math.SQRT1_2, 0, 0]);\n\n/**\n * Closed union of vector-storage contract assertions an adapter can mark as\n * known failing. A typo in `expectedFailures` becomes a TS error rather than a\n * silently-ignored entry.\n */\nexport type VectorStorageContractAssertion =\n | \"similarityRanking\"\n | \"vectorRoundTrip\"\n | \"searchOptions\"\n | \"metadataFilter\"\n | \"similaritySearchEvent\"\n | \"legacyEncoding\"\n | \"dimensionValidation\";\n\nexport interface VectorStorageCapabilities {\n /** Whether `options.filter` narrows by metadata. */\n readonly supportsMetadataFilter: boolean;\n /** Whether `options.scoreThreshold` drops rows below the score. */\n readonly supportsScoreThreshold: boolean;\n /**\n * Whether a write whose vector is the wrong length is refused.\n *\n * A backend that stores the vector opaquely cannot check it, and one that\n * declares this must refuse BEFORE writing — a row of the wrong width that\n * lands is worse than one that never validates, because the search that\n * trips over it is arbitrarily far away.\n */\n readonly supportsDimensionValidation: boolean;\n}\n\nexport interface VectorStorageContractOpts {\n readonly name: string;\n readonly skip?: boolean;\n readonly timeout?: number;\n readonly capabilities: VectorStorageCapabilities;\n /** A fresh, empty store of {@link VectorItemSchema} at {@link VECTOR_DIMENSIONS}. */\n readonly createStorage: () => Promise<AnyVectorStorage>;\n /**\n * Releases whatever the factory allocated that the storage does not own —\n * typically a connection handle. Called from every `afterEach`, passed the\n * instance the factory returned.\n */\n readonly releaseStorage?: (storage: AnyVectorStorage) => void | Promise<void>;\n /**\n * Writes a row past this class's writer, in the column's stored form.\n *\n * Only a backend that has a stored form to write can supply it, and it is\n * what the {@link VectorStorageContractAssertion} `legacyEncoding` block\n * needs: the decode has to accept a row that never round-tripped through the\n * current writer, and a test that inserts through the writer cannot ask that.\n */\n readonly writeRawRow?: (\n storage: AnyVectorStorage,\n row: { readonly id: string; readonly vector: readonly number[]; readonly metadata: unknown }\n ) => Promise<void> | void;\n readonly expectedFailures?: ReadonlyArray<VectorStorageContractAssertion>;\n}\n",
8
+ "/**\n * @license\n * Copyright 2026 Steven Roussey <sroussey@gmail.com>\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport type { AnyVectorStorage } from \"@workglow/storage\";\nimport { afterEach, beforeEach, it } from \"vitest\";\nimport { itExpectFail } from \"../../itExpectFail\";\nimport type { VectorStorageContractAssertion, VectorStorageContractOpts } from \"../types\";\nimport { EAST, NORTH, NORTHEAST } from \"../types\";\n\n/** A hit, as much of one as every assertion here needs to read. */\nexport interface Hit {\n readonly id: string;\n readonly score: number;\n readonly vector: unknown;\n}\n\nexport function itFor(\n opts: VectorStorageContractOpts,\n assertion: VectorStorageContractAssertion\n): typeof it {\n return new Set(opts.expectedFailures ?? []).has(assertion) ? (itExpectFail as typeof it) : it;\n}\n\n/**\n * The three-row fixture every block searches over: two axis-aligned unit\n * vectors and the bisector between them, so a cosine search from `EAST` has one\n * exact match, one at `SQRT1_2` and one orthogonal — three distinct scores in a\n * known order, which is what makes \"ranked by similarity\" checkable rather than\n * merely non-empty.\n */\nexport function seededStore(opts: VectorStorageContractOpts): () => AnyVectorStorage {\n let storage: AnyVectorStorage;\n\n beforeEach(async () => {\n storage = await opts.createStorage();\n await storage.setupDatabase?.();\n await storage.putBulk([\n { id: \"east\", vector: EAST, metadata: { region: \"e\" } },\n { id: \"north\", vector: NORTH, metadata: { region: \"n\" } },\n { id: \"northeast\", vector: NORTHEAST, metadata: { region: \"ne\" } },\n ]);\n });\n\n afterEach(async () => {\n await storage.deleteAll();\n storage.destroy?.();\n await opts.releaseStorage?.(storage);\n });\n\n return () => storage;\n}\n",
9
+ "/**\n * @license\n * Copyright 2026 Steven Roussey <sroussey@gmail.com>\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport { it as vitestIt } from \"vitest\";\n\n/**\n * Live provider tests treat \"the account is out of credits\" as a billing\n * condition rather than a product bug. A key being present is not the same as\n * the account being able to pay; HuggingFace / OpenRouter 402s, Anthropic's\n * credit-balance error, DeepSeek's `Insufficient Balance`, and OpenAI's\n * `insufficient_quota` are all billing.\n *\n * Rate limits (429 + `rate_limit_exceeded`) are NOT this: those are transient\n * and the existing retry policy handles them.\n *\n * Where it skips and where it fails is a deliberate split\n * ({@link creditExhaustionSkipsTest}): on CI it skips for every provider,\n * because a red build tells a reviewer nothing they can act on; locally it\n * fails, because the developer running the suite is the one who can top the\n * account up. `WORKGLOW_CREDIT_EXHAUSTED_SKIP=1` forces the skip locally\n * (and `=0` forces the failure on CI).\n */\n\nconst BILLING_CODES = new Set([\n \"insufficient_quota\",\n \"billing_hard_limit_reached\",\n \"insufficient_credits\",\n \"insufficient_balance\",\n]);\n\nconst CREDIT_MESSAGE =\n /insufficient credits|not enough credits|no credits remaining|out of credits|insufficient[\\s_-]balance|balance is (?:too low|insufficient)|exceeded[\\s\\S]{0,80}credits|monthly included credits|payment required|exceeded your current quota|check your plan and billing|billing_hard_limit/i;\n\n/**\n * A 402 that survives only as text. `classifyProviderError` rebuilds the error\n * as a `PermanentJobError` carrying the message and no `cause`, so by the time\n * a live test sees a DeepSeek billing failure the numeric `status` is gone and\n * the OpenAI-SDK message shape (`402 Insufficient Balance`) is all that is\n * left. Anchored to an HTTP-shaped position — start of a line, or after a\n * summary line's colon — so prose that merely contains the number\n * (\"Item 402(c)\") is not scavenged as a status.\n */\nconst HTTP_402_MESSAGE =\n /\\bHTTP\\/?\\d?(?:\\.\\d)?\\s*402\\b|\\bstatus(?:\\s*code)?\\s*[:=]\\s*402\\b|\\berror code:?\\s*402\\b|(?:^|:\\s+)402\\s+[a-z]/im;\n\nexport function isCreditExhaustedError(err: unknown): boolean {\n const seen = new Set<unknown>();\n let current: unknown = err;\n while (current !== undefined && current !== null && !seen.has(current)) {\n seen.add(current);\n if (matchesCreditExhaustion(current)) return true;\n current = causeOf(current);\n }\n return false;\n}\n\nfunction matchesCreditExhaustion(err: unknown): boolean {\n if (typeof err === \"string\") return messageLooksLikeCreditExhaustion(err);\n\n if (!err || typeof err !== \"object\") return false;\n\n const rec = err as Record<string, unknown>;\n const status = numericStatus(rec);\n if (status === 402) return true;\n\n const code = stringish(rec.code) ?? stringish(nested(rec.error, \"code\"));\n const type = stringish(rec.type) ?? stringish(nested(rec.error, \"type\"));\n if (code && BILLING_CODES.has(code)) return true;\n if (type && BILLING_CODES.has(type)) return true;\n\n const message = [\n stringish(rec.message),\n stringish(nested(rec.error, \"message\")),\n stringifyUnknown(err),\n ]\n .filter((part): part is string => typeof part === \"string\")\n .join(\"\\n\");\n return messageLooksLikeCreditExhaustion(message);\n}\n\nfunction messageLooksLikeCreditExhaustion(message: string): boolean {\n return CREDIT_MESSAGE.test(message) || HTTP_402_MESSAGE.test(message);\n}\n\nfunction numericStatus(rec: Record<string, unknown>): number | undefined {\n for (const key of [\"status\", \"statusCode\"] as const) {\n const value = rec[key];\n if (typeof value === \"number\" && Number.isFinite(value)) return value;\n }\n const nestedStatus = nested(rec.error, \"status\");\n if (typeof nestedStatus === \"number\" && Number.isFinite(nestedStatus)) return nestedStatus;\n return undefined;\n}\n\nfunction nested(value: unknown, key: string): unknown {\n if (!value || typeof value !== \"object\") return undefined;\n return (value as Record<string, unknown>)[key];\n}\n\nfunction stringish(value: unknown): string | undefined {\n return typeof value === \"string\" && value.length > 0 ? value : undefined;\n}\n\nfunction stringifyUnknown(err: unknown): string {\n if (err instanceof Error) return err.message;\n return String(err);\n}\n\nfunction causeOf(err: unknown): unknown {\n if (!err || typeof err !== \"object\") return undefined;\n return (err as { cause?: unknown }).cause;\n}\n\nexport function creditSkipNote(err: unknown): string {\n const message = err instanceof Error ? err.message : String(err);\n return `skipped: provider out of credits (${message})`;\n}\n\nconst SKIP_OVERRIDE_ENV = \"WORKGLOW_CREDIT_EXHAUSTED_SKIP\";\n\nfunction isTruthyFlag(value: string): boolean {\n return !/^(?:0|false|off|no)$/i.test(value.trim());\n}\n\n/**\n * Whether a billing failure skips the test or fails it.\n *\n * CI skips: nobody watching a build can top an account up, and one exhausted\n * key would otherwise turn every provider suite red for a change that touched\n * none of them — so the leniency is provider-agnostic by construction, keyed on\n * the environment rather than on which vendor ran out.\n *\n * Locally it fails, loudly, because the developer running the suite IS the\n * person who can act on it — and a silently green local run is how an\n * exhausted account goes unnoticed until CI is the only thing exercising the\n * provider at all.\n *\n * `WORKGLOW_CREDIT_EXHAUSTED_SKIP` overrides in both directions.\n */\nexport function creditExhaustionSkipsTest(\n env: Record<string, string | undefined> = process.env\n): boolean {\n const override = env[SKIP_OVERRIDE_ENV];\n if (override !== undefined && override.trim() !== \"\") return isTruthyFlag(override);\n for (const key of [\"CI\", \"GITHUB_ACTIONS\"] as const) {\n const value = env[key];\n if (value !== undefined && value.trim() !== \"\" && isTruthyFlag(value)) return true;\n }\n return false;\n}\n\n/**\n * The local-failure path. Rethrows the provider's own error unchanged — its\n * identity and stack are what makes the failure diagnosable — after one line\n * saying why it was not skipped and how to skip it.\n */\nfunction reportLocalCreditFailure(err: unknown): void {\n const message = err instanceof Error ? err.message : String(err);\n console.error(\n `provider out of credits (${message})\\n` +\n ` Failing because this is not CI. Top the account up, or set ${SKIP_OVERRIDE_ENV}=1 to skip instead.`\n );\n}\n\nexport async function runWithCreditSkip<T>(\n ctx: { skip: (note?: string) => void },\n fn: () => Promise<T> | T\n): Promise<T> {\n try {\n return await fn();\n } catch (err) {\n if (isCreditExhaustedError(err)) {\n if (!creditExhaustionSkipsTest()) {\n reportLocalCreditFailure(err);\n throw err;\n }\n ctx.skip(creditSkipNote(err));\n // vitest's skip() is `never` (throws). Mocks used in unit tests return.\n return undefined as T;\n }\n throw err;\n }\n}\n\n/**\n * Wraps one test body. This is the seam every conformance assertion actually\n * runs through (the exported {@link it} proxies each callback through it), so\n * the CI-skips / locally-fails policy is applied here rather than at each\n * call site.\n */\nexport function wrapTestBodyForCreditSkip(\n fn: (...args: never[]) => unknown\n): (...args: never[]) => unknown {\n return async function wrapped(this: unknown, ...args: never[]): Promise<unknown> {\n try {\n return await fn.apply(this, args);\n } catch (err) {\n if (!isCreditExhaustedError(err)) throw err;\n if (!creditExhaustionSkipsTest()) {\n reportLocalCreditFailure(err);\n throw err;\n }\n const ctx = args[0] as { skip?: (note?: string) => void } | undefined;\n if (ctx && typeof ctx.skip === \"function\") {\n ctx.skip(creditSkipNote(err));\n return undefined;\n }\n // bun:test has no TestContext.skip; passing is \"don't fail the test\".\n console.warn(creditSkipNote(err));\n return undefined;\n }\n };\n}\n\nfunction wrapCallable(fn: CallableFunction): CallableFunction {\n return new Proxy(fn, {\n apply(target, thisArg, argArray: unknown[]) {\n const wrapped = argArray.map((arg) =>\n typeof arg === \"function\" ? wrapTestBodyForCreditSkip(arg as never) : arg\n );\n const result = Reflect.apply(target, thisArg, wrapped);\n return typeof result === \"function\" ? wrapCallable(result) : result;\n },\n get(target, prop, receiver) {\n const value = Reflect.get(target, prop, receiver);\n if (typeof value === \"function\") {\n return wrapCallable((value as CallableFunction).bind(target));\n }\n return value;\n },\n });\n}\n\n/**\n * Drop-in `it` for live provider tests: a credit/billing error skips the test on\n * CI and fails it locally (see {@link creditExhaustionSkipsTest}).\n */\nexport const it = wrapCallable(vitestIt) as typeof vitestIt;\n",
10
+ "/**\n * @license\n * Copyright 2026 Steven Roussey <sroussey@gmail.com>\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport { isCreditExhaustedError, it } from \"./creditExhaustedSkip\";\n\ntype ItFn = (name: string, fn: () => Promise<void> | void, timeout?: number) => void;\n\n/**\n * Cross-runner polyfill for `it.fails`. Vitest exposes it natively; bun's\n * native `bun test` runner does not. Under bun we wrap the body in a\n * try/catch and assert that it threw — so a passing-when-expected-fail\n * test still surfaces as a CI failure (signalling \"remove from\n * expectedFailures\").\n */\nexport const itExpectFail: ItFn = (name, fn, timeout) => {\n const native = (it as unknown as { fails?: ItFn }).fails;\n if (typeof native === \"function\") {\n native(name, fn, timeout);\n return;\n }\n it(\n `${name} [expected-fail]`,\n async () => {\n let passed = false;\n try {\n await fn();\n passed = true;\n } catch (err) {\n // Billing exhaustion is not the adapter bug this marker records.\n if (isCreditExhaustedError(err)) throw err;\n // expected; the test was supposed to fail\n }\n if (passed) {\n throw new Error(\n `Test \"${name}\" was marked as expected-fail but passed. Remove its name from opts.expectedFailures.`\n );\n }\n },\n timeout\n );\n};\n",
11
+ "/**\n * @license\n * Copyright 2026 Steven Roussey <sroussey@gmail.com>\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport { describe, expect } from \"vitest\";\nimport type { VectorStorageContractOpts } from \"../types\";\nimport { NORTH } from \"../types\";\nimport type { Hit } from \"./shared\";\nimport { itFor, seededStore } from \"./shared\";\n\n/**\n * A row written in the column's stored form — by an older release, or by\n * anything that is not this class's writer — is still searchable.\n *\n * This is the other half of the decode that broke: a search path may not assume\n * every row it reads was encoded by the writer it ships with. Only a backend\n * that has a stored form to write can be asked, so the block is skipped where\n * the adapter supplies no `writeRawRow`.\n */\nexport function legacyEncodingBlock(opts: VectorStorageContractOpts): void {\n describe.skipIf(opts.writeRawRow === undefined)(\"legacyEncoding\", () => {\n const store = seededStore(opts);\n\n itFor(opts, \"legacyEncoding\")(\n \"searches a row that never round-tripped through this writer\",\n async () => {\n await opts.writeRawRow?.(store(), {\n id: \"legacy\",\n vector: [...NORTH],\n metadata: { region: \"legacy\" },\n });\n\n const hits = (await store().similaritySearch(NORTH, { topK: 2 })) as Hit[];\n\n expect(hits.map((hit) => hit.id)).toContain(\"legacy\");\n // And it decodes to the same thing a written row does, rather than\n // merely not throwing.\n const legacy = hits.find((hit) => hit.id === \"legacy\");\n expect([...(legacy!.vector as Float32Array)]).toEqual([...NORTH]);\n },\n opts.timeout\n );\n });\n}\n",
12
+ "/**\n * @license\n * Copyright 2026 Steven Roussey <sroussey@gmail.com>\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport { describe, expect } from \"vitest\";\nimport type { VectorStorageContractOpts } from \"../types\";\nimport { EAST } from \"../types\";\nimport type { Hit } from \"./shared\";\nimport { itFor, seededStore } from \"./shared\";\n\n/**\n * A metadata filter narrows the search, and narrows it to what matches rather\n * than to what happens to be near.\n *\n * The case worth stating is the second one: filtering to a row that is NOT the\n * nearest. A backend that takes the top `k` and then filters returns nothing\n * there, while one that filters and then ranks returns the row — and the first\n * behaviour is a silently empty result for every scoped RAG query.\n */\nexport function metadataFilterBlock(opts: VectorStorageContractOpts): void {\n describe.skipIf(!opts.capabilities.supportsMetadataFilter)(\"metadataFilter\", () => {\n const store = seededStore(opts);\n const itImpl = itFor(opts, \"metadataFilter\");\n\n itImpl(\n \"returns only the rows the filter matches\",\n async () => {\n const hits = (await store().similaritySearch(EAST, { filter: { region: \"n\" } })) as Hit[];\n expect(hits.map((hit) => hit.id)).toEqual([\"north\"]);\n },\n opts.timeout\n );\n\n itImpl(\n \"finds a match the ranking would not have reached\",\n async () => {\n // `north` is the LAST of the three by similarity to EAST. Asking for it\n // with topK 1 separates \"filter, then rank\" from \"rank, then filter\".\n const hits = (await store().similaritySearch(EAST, {\n filter: { region: \"n\" },\n topK: 1,\n })) as Hit[];\n expect(hits.map((hit) => hit.id)).toEqual([\"north\"]);\n },\n opts.timeout\n );\n\n itImpl(\n \"returns nothing when the filter matches nothing\",\n async () => {\n expect(\n await store().similaritySearch(EAST, { filter: { region: \"nowhere\" } })\n ).toHaveLength(0);\n },\n opts.timeout\n );\n });\n}\n",
13
+ "/**\n * @license\n * Copyright 2026 Steven Roussey <sroussey@gmail.com>\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport { describe, expect } from \"vitest\";\nimport type { VectorStorageContractOpts } from \"../types\";\nimport { EAST } from \"../types\";\nimport type { Hit } from \"./shared\";\nimport { itFor, seededStore } from \"./shared\";\n\n/**\n * `topK` truncates and `scoreThreshold` filters — and they mean the same thing\n * on a backend that computes the scores in SQL as on one that scans in memory.\n *\n * That parity is not free: it has already had to be fixed once, when the two\n * paths disagreed about which side of the threshold an exact match fell on.\n * Stating it here is what makes the next backend inherit the answer instead of\n * rediscovering the question.\n */\nexport function searchOptionsBlock(opts: VectorStorageContractOpts): void {\n describe(\"searchOptions\", () => {\n const store = seededStore(opts);\n const itImpl = itFor(opts, \"searchOptions\");\n\n itImpl(\n \"truncates to topK, keeping the most similar\",\n async () => {\n const hits = (await store().similaritySearch(EAST, { topK: 2 })) as Hit[];\n expect(hits.map((hit) => hit.id)).toEqual([\"east\", \"northeast\"]);\n },\n opts.timeout\n );\n\n itImpl(\n \"returns everything when topK exceeds the row count\",\n async () => {\n // A backend that pushes `topK` into a `LIMIT` and one that slices an\n // array agree here; one that treats it as an exact count does not.\n expect(await store().similaritySearch(EAST, { topK: 10 })).toHaveLength(3);\n },\n opts.timeout\n );\n\n describe.skipIf(!opts.capabilities.supportsScoreThreshold)(\"scoreThreshold\", () => {\n itImpl(\n \"drops rows scoring below the threshold\",\n async () => {\n const hits = (await store().similaritySearch(EAST, { scoreThreshold: 0.9 })) as Hit[];\n expect(hits.map((hit) => hit.id)).toEqual([\"east\"]);\n },\n opts.timeout\n );\n\n itImpl(\n \"keeps a row scoring exactly the threshold\",\n async () => {\n // Zero, because it is the one boundary every backend can hit\n // exactly: `north` is orthogonal to `EAST`, so its score is 0 in\n // float32 and in float64 alike. `Math.SQRT1_2` is not — it rounds\n // down through a Float32Array, so a threshold set to it sits just\n // above the score the store can produce, and the test would be\n // measuring the rounding rather than the comparison.\n //\n // The boundary is where the SQL and in-memory paths drifted apart\n // before, and either answer is defensible until one is written down.\n const hits = (await store().similaritySearch(EAST, { scoreThreshold: 0 })) as Hit[];\n expect(hits.map((hit) => hit.id)).toEqual([\"east\", \"northeast\", \"north\"]);\n },\n opts.timeout\n );\n });\n });\n}\n",
14
+ "/**\n * @license\n * Copyright 2026 Steven Roussey <sroussey@gmail.com>\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport { describe, expect } from \"vitest\";\nimport type { VectorStorageContractOpts } from \"../types\";\nimport { EAST } from \"../types\";\nimport type { Hit } from \"./shared\";\nimport { itFor, seededStore } from \"./shared\";\n\n/**\n * A search returns the rows, most similar first.\n *\n * This is the assertion the tree did not have, and the one that cost: a release\n * shipped in which `similaritySearch` re-decoded a vector column the inherited\n * tabular read had already turned into a `Float32Array`, so `JSON.parse` threw\n * on the first row of every search. Writes, dimension validation and `getAll`\n * were all fine — a store that accepts writes and fails every query looks\n * healthy from anywhere except a query, and only one backend had a test that\n * issued one.\n */\nexport function similarityRankingBlock(opts: VectorStorageContractOpts): void {\n describe(\"similarityRanking\", () => {\n const store = seededStore(opts);\n\n itFor(opts, \"similarityRanking\")(\n \"returns the stored rows ranked by similarity, with the scores that ordered them\",\n async () => {\n const hits = (await store().similaritySearch(EAST)) as Hit[];\n\n expect(hits.map((hit) => hit.id)).toEqual([\"east\", \"northeast\", \"north\"]);\n // The order alone would hold for a backend that returned them in\n // insertion order by luck. The scores are what say it measured.\n expect(hits[0]!.score).toBeCloseTo(1, 5);\n expect(hits[1]!.score).toBeCloseTo(Math.SQRT1_2, 5);\n expect(hits[2]!.score).toBeCloseTo(0, 5);\n },\n opts.timeout\n );\n });\n}\n",
15
+ "/**\n * @license\n * Copyright 2026 Steven Roussey <sroussey@gmail.com>\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport { describe, expect } from \"vitest\";\nimport type { VectorStorageContractOpts } from \"../types\";\nimport { EAST } from \"../types\";\nimport type { Hit } from \"./shared\";\nimport { itFor, seededStore } from \"./shared\";\n\n/**\n * The `similaritySearch` event is the one thing `IVectorStorage` adds to the\n * tabular event surface, and it carries the results — not a count, and not the\n * rows before the options were applied.\n *\n * Telemetry and cache layers wrap these stores and read that payload as the\n * answer the caller got. An event that fires with the unfiltered set reports\n * work nobody received.\n */\nexport function similaritySearchEventBlock(opts: VectorStorageContractOpts): void {\n describe(\"similaritySearchEvent\", () => {\n const store = seededStore(opts);\n\n itFor(opts, \"similaritySearchEvent\")(\n \"fires with the results the caller was handed\",\n async () => {\n let seen: Hit[] | undefined;\n // The vector extension of the event surface; the inherited `on` is\n // typed to the tabular names, so the cast is at the call site.\n (\n store() as unknown as {\n on: (name: string, fn: (query: unknown, results: Hit[]) => void) => void;\n }\n ).on(\"similaritySearch\", (_query, results) => {\n seen = results;\n });\n\n await store().similaritySearch(EAST, { topK: 1 });\n\n expect(seen?.map((hit) => hit.id)).toEqual([\"east\"]);\n },\n opts.timeout\n );\n });\n}\n",
16
+ "/**\n * @license\n * Copyright 2026 Steven Roussey <sroussey@gmail.com>\n * SPDX-License-Identifier: Apache-2.0\n */\n\nimport { describe, expect } from \"vitest\";\nimport type { VectorStorageContractOpts } from \"../types\";\nimport { EAST } from \"../types\";\nimport type { Hit } from \"./shared\";\nimport { itFor, seededStore } from \"./shared\";\n\n/**\n * A hit's vector comes back as the declared `TypedArray`, not as whatever the\n * column stores.\n *\n * The stored form is a backend's own business — JSON text, a native vector\n * type, a blob — and every one of them has a decode step on the search path\n * that the tabular read does not exercise. Handing back a string or a plain\n * array is a break for every caller doing arithmetic on the result, and it is\n * invisible to a test that only reads `id` and `score`.\n */\nexport function vectorRoundTripBlock(opts: VectorStorageContractOpts): void {\n describe(\"vectorRoundTrip\", () => {\n const store = seededStore(opts);\n\n itFor(opts, \"vectorRoundTrip\")(\n \"hands back the vector as a TypedArray with the values that were written\",\n async () => {\n const [hit] = (await store().similaritySearch(EAST, { topK: 1 })) as Hit[];\n\n expect(ArrayBuffer.isView(hit!.vector)).toBe(true);\n expect([...(hit!.vector as Float32Array)]).toEqual([...EAST]);\n },\n opts.timeout\n );\n });\n}\n"
17
+ ],
18
+ "mappings": ";AAMA,qBAAS;;;ACCT;AACA,sBAAS,0BAAW;;;ACApB;AAOO,IAAM,mBAAmB;AAAA,EAC9B,MAAM;AAAA,EACN,YAAY;AAAA,IACV,IAAI,EAAE,MAAM,SAAS;AAAA,IACrB,QAAQ,iBAAiB;AAAA,IACzB,UAAU,EAAE,MAAM,UAAU,QAAQ,YAAY,sBAAsB,KAAK;AAAA,EAC7E;AAAA,EACA,UAAU,CAAC,MAAM,UAAU,UAAU;AAAA,EACrC,sBAAsB;AACxB;AAEO,IAAM,4BAA4B,CAAC,IAAI;AAGvC,IAAM,oBAAoB;AAE1B,IAAM,OAAO,IAAI,aAAa,CAAC,GAAG,GAAG,GAAG,CAAC,CAAC;AAC1C,IAAM,QAAQ,IAAI,aAAa,CAAC,GAAG,GAAG,GAAG,CAAC,CAAC;AAC3C,IAAM,YAAY,IAAI,aAAa,CAAC,KAAK,SAAS,KAAK,SAAS,GAAG,CAAC,CAAC;;;AC1B5E,sCAAgC;;;ACDhC,eAAS;AAoBT,IAAM,gBAAgB,IAAI,IAAI;AAAA,EAC5B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAED,IAAM,iBACJ;AAWF,IAAM,mBACJ;AAEK,SAAS,sBAAsB,CAAC,KAAuB;AAAA,EAC5D,MAAM,OAAO,IAAI;AAAA,EACjB,IAAI,UAAmB;AAAA,EACvB,OAAO,YAAY,aAAa,YAAY,QAAQ,CAAC,KAAK,IAAI,OAAO,GAAG;AAAA,IACtE,KAAK,IAAI,OAAO;AAAA,IAChB,IAAI,wBAAwB,OAAO;AAAA,MAAG,OAAO;AAAA,IAC7C,UAAU,QAAQ,OAAO;AAAA,EAC3B;AAAA,EACA,OAAO;AAAA;AAGT,SAAS,uBAAuB,CAAC,KAAuB;AAAA,EACtD,IAAI,OAAO,QAAQ;AAAA,IAAU,OAAO,iCAAiC,GAAG;AAAA,EAExE,IAAI,CAAC,OAAO,OAAO,QAAQ;AAAA,IAAU,OAAO;AAAA,EAE5C,MAAM,MAAM;AAAA,EACZ,MAAM,SAAS,cAAc,GAAG;AAAA,EAChC,IAAI,WAAW;AAAA,IAAK,OAAO;AAAA,EAE3B,MAAM,OAAO,UAAU,IAAI,IAAI,KAAK,UAAU,OAAO,IAAI,OAAO,MAAM,CAAC;AAAA,EACvE,MAAM,OAAO,UAAU,IAAI,IAAI,KAAK,UAAU,OAAO,IAAI,OAAO,MAAM,CAAC;AAAA,EACvE,IAAI,QAAQ,cAAc,IAAI,IAAI;AAAA,IAAG,OAAO;AAAA,EAC5C,IAAI,QAAQ,cAAc,IAAI,IAAI;AAAA,IAAG,OAAO;AAAA,EAE5C,MAAM,UAAU;AAAA,IACd,UAAU,IAAI,OAAO;AAAA,IACrB,UAAU,OAAO,IAAI,OAAO,SAAS,CAAC;AAAA,IACtC,iBAAiB,GAAG;AAAA,EACtB,EACG,OAAO,CAAC,SAAyB,OAAO,SAAS,QAAQ,EACzD,KAAK;AAAA,CAAI;AAAA,EACZ,OAAO,iCAAiC,OAAO;AAAA;AAGjD,SAAS,gCAAgC,CAAC,SAA0B;AAAA,EAClE,OAAO,eAAe,KAAK,OAAO,KAAK,iBAAiB,KAAK,OAAO;AAAA;AAGtE,SAAS,aAAa,CAAC,KAAkD;AAAA,EACvE,WAAW,OAAO,CAAC,UAAU,YAAY,GAAY;AAAA,IACnD,MAAM,QAAQ,IAAI;AAAA,IAClB,IAAI,OAAO,UAAU,YAAY,OAAO,SAAS,KAAK;AAAA,MAAG,OAAO;AAAA,EAClE;AAAA,EACA,MAAM,eAAe,OAAO,IAAI,OAAO,QAAQ;AAAA,EAC/C,IAAI,OAAO,iBAAiB,YAAY,OAAO,SAAS,YAAY;AAAA,IAAG,OAAO;AAAA,EAC9E;AAAA;AAGF,SAAS,MAAM,CAAC,OAAgB,KAAsB;AAAA,EACpD,IAAI,CAAC,SAAS,OAAO,UAAU;AAAA,IAAU;AAAA,EACzC,OAAQ,MAAkC;AAAA;AAG5C,SAAS,SAAS,CAAC,OAAoC;AAAA,EACrD,OAAO,OAAO,UAAU,YAAY,MAAM,SAAS,IAAI,QAAQ;AAAA;AAGjE,SAAS,gBAAgB,CAAC,KAAsB;AAAA,EAC9C,IAAI,eAAe;AAAA,IAAO,OAAO,IAAI;AAAA,EACrC,OAAO,OAAO,GAAG;AAAA;AAGnB,SAAS,OAAO,CAAC,KAAuB;AAAA,EACtC,IAAI,CAAC,OAAO,OAAO,QAAQ;AAAA,IAAU;AAAA,EACrC,OAAQ,IAA4B;AAAA;AAG/B,SAAS,cAAc,CAAC,KAAsB;AAAA,EACnD,MAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAAA,EAC/D,OAAO,qCAAqC;AAAA;AAG9C,IAAM,oBAAoB;AAE1B,SAAS,YAAY,CAAC,OAAwB;AAAA,EAC5C,OAAO,CAAC,wBAAwB,KAAK,MAAM,KAAK,CAAC;AAAA;AAkB5C,SAAS,yBAAyB,CACvC,MAA0C,QAAQ,KACzC;AAAA,EACT,MAAM,WAAW,IAAI;AAAA,EACrB,IAAI,aAAa,aAAa,SAAS,KAAK,MAAM;AAAA,IAAI,OAAO,aAAa,QAAQ;AAAA,EAClF,WAAW,OAAO,CAAC,MAAM,gBAAgB,GAAY;AAAA,IACnD,MAAM,QAAQ,IAAI;AAAA,IAClB,IAAI,UAAU,aAAa,MAAM,KAAK,MAAM,MAAM,aAAa,KAAK;AAAA,MAAG,OAAO;AAAA,EAChF;AAAA,EACA,OAAO;AAAA;AAQT,SAAS,wBAAwB,CAAC,KAAoB;AAAA,EACpD,MAAM,UAAU,eAAe,QAAQ,IAAI,UAAU,OAAO,GAAG;AAAA,EAC/D,QAAQ,MACN,4BAA4B;AAAA,IAC1B,gEAAgE,sCACpE;AAAA;AAGF,eAAsB,iBAAoB,CACxC,KACA,IACY;AAAA,EACZ,IAAI;AAAA,IACF,OAAO,MAAM,GAAG;AAAA,IAChB,OAAO,KAAK;AAAA,IACZ,IAAI,uBAAuB,GAAG,GAAG;AAAA,MAC/B,IAAI,CAAC,0BAA0B,GAAG;AAAA,QAChC,yBAAyB,GAAG;AAAA,QAC5B,MAAM;AAAA,MACR;AAAA,MACA,IAAI,KAAK,eAAe,GAAG,CAAC;AAAA,MAE5B;AAAA,IACF;AAAA,IACA,MAAM;AAAA;AAAA;AAUH,SAAS,yBAAyB,CACvC,IAC+B;AAAA,EAC/B,OAAO,eAAe,OAAO,IAAmB,MAAiC;AAAA,IAC/E,IAAI;AAAA,MACF,OAAO,MAAM,GAAG,MAAM,MAAM,IAAI;AAAA,MAChC,OAAO,KAAK;AAAA,MACZ,IAAI,CAAC,uBAAuB,GAAG;AAAA,QAAG,MAAM;AAAA,MACxC,IAAI,CAAC,0BAA0B,GAAG;AAAA,QAChC,yBAAyB,GAAG;AAAA,QAC5B,MAAM;AAAA,MACR;AAAA,MACA,MAAM,MAAM,KAAK;AAAA,MACjB,IAAI,OAAO,OAAO,IAAI,SAAS,YAAY;AAAA,QACzC,IAAI,KAAK,eAAe,GAAG,CAAC;AAAA,QAC5B;AAAA,MACF;AAAA,MAEA,QAAQ,KAAK,eAAe,GAAG,CAAC;AAAA,MAChC;AAAA;AAAA;AAAA;AAKN,SAAS,YAAY,CAAC,IAAwC;AAAA,EAC5D,OAAO,IAAI,MAAM,IAAI;AAAA,IACnB,KAAK,CAAC,QAAQ,SAAS,UAAqB;AAAA,MAC1C,MAAM,UAAU,SAAS,IAAI,CAAC,QAC5B,OAAO,QAAQ,aAAa,0BAA0B,GAAY,IAAI,GACxE;AAAA,MACA,MAAM,SAAS,QAAQ,MAAM,QAAQ,SAAS,OAAO;AAAA,MACrD,OAAO,OAAO,WAAW,aAAa,aAAa,MAAM,IAAI;AAAA;AAAA,IAE/D,GAAG,CAAC,QAAQ,MAAM,UAAU;AAAA,MAC1B,MAAM,QAAQ,QAAQ,IAAI,QAAQ,MAAM,QAAQ;AAAA,MAChD,IAAI,OAAO,UAAU,YAAY;AAAA,QAC/B,OAAO,aAAc,MAA2B,KAAK,MAAM,CAAC;AAAA,MAC9D;AAAA,MACA,OAAO;AAAA;AAAA,EAEX,CAAC;AAAA;AAOI,IAAM,KAAK,aAAa,QAAQ;;;AC/NhC,IAAM,eAAqB,CAAC,MAAM,IAAI,YAAY;AAAA,EACvD,MAAM,SAAU,GAAmC;AAAA,EACnD,IAAI,OAAO,WAAW,YAAY;AAAA,IAChC,OAAO,MAAM,IAAI,OAAO;AAAA,IACxB;AAAA,EACF;AAAA,EACA,GACE,GAAG,wBACH,YAAY;AAAA,IACV,IAAI,SAAS;AAAA,IACb,IAAI;AAAA,MACF,MAAM,GAAG;AAAA,MACT,SAAS;AAAA,MACT,OAAO,KAAK;AAAA,MAEZ,IAAI,uBAAuB,GAAG;AAAA,QAAG,MAAM;AAAA;AAAA,IAGzC,IAAI,QAAQ;AAAA,MACV,MAAM,IAAI,MACR,SAAS,2FACX;AAAA,IACF;AAAA,KAEF,OACF;AAAA;;;AFvBK,SAAS,KAAK,CACnB,MACA,WACW;AAAA,EACX,OAAO,IAAI,IAAI,KAAK,oBAAoB,CAAC,CAAC,EAAE,IAAI,SAAS,IAAK,eAA6B;AAAA;AAUtF,SAAS,WAAW,CAAC,MAAyD;AAAA,EACnF,IAAI;AAAA,EAEJ,WAAW,YAAY;AAAA,IACrB,UAAU,MAAM,KAAK,cAAc;AAAA,IACnC,MAAM,QAAQ,gBAAgB;AAAA,IAC9B,MAAM,QAAQ,QAAQ;AAAA,MACpB,EAAE,IAAI,QAAQ,QAAQ,MAAM,UAAU,EAAE,QAAQ,IAAI,EAAE;AAAA,MACtD,EAAE,IAAI,SAAS,QAAQ,OAAO,UAAU,EAAE,QAAQ,IAAI,EAAE;AAAA,MACxD,EAAE,IAAI,aAAa,QAAQ,WAAW,UAAU,EAAE,QAAQ,KAAK,EAAE;AAAA,IACnE,CAAC;AAAA,GACF;AAAA,EAED,UAAU,YAAY;AAAA,IACpB,MAAM,QAAQ,UAAU;AAAA,IACxB,QAAQ,UAAU;AAAA,IAClB,MAAM,KAAK,iBAAiB,OAAO;AAAA,GACpC;AAAA,EAED,OAAO,MAAM;AAAA;;;AFvCf,IAAM,QAAQ,IAAI,aAAa,CAAC,GAAG,GAAG,CAAC,CAAC;AACxC,IAAM,OAAO,IAAI,aAAa,CAAC,GAAG,GAAG,GAAG,GAAG,CAAC,CAAC;AAYtC,SAAS,wBAAwB,CAAC,MAAuC;AAAA,EAC9E,SAAS,OAAO,CAAC,KAAK,aAAa,2BAA2B,EAAE,uBAAuB,MAAM;AAAA,IAC3F,MAAM,SAAS,MAAM,MAAM,qBAAqB;AAAA,IAChD,IAAI;AAAA,IAEJ,YAAW,YAAY;AAAA,MACrB,UAAU,MAAM,KAAK,cAAc;AAAA,MACnC,MAAM,QAAQ,gBAAgB;AAAA,KAC/B;AAAA,IAED,WAAU,YAAY;AAAA,MACpB,MAAM,QAAQ,UAAU;AAAA,MACxB,QAAQ,UAAU;AAAA,MAClB,MAAM,KAAK,iBAAiB,OAAO;AAAA,KACpC;AAAA,IAED,OACE,wCACA,YAAY;AAAA,MACV,OAAO,QAAQ,oBAAoB,CAAC,EAAE,KAAK,iBAAiB;AAAA,OAE9D,KAAK,OACP;AAAA,IAEA,YAAY,OAAO,WAAW;AAAA,MAC5B,CAAC,mBAAmB,KAAK;AAAA,MACzB,CAAC,kBAAkB,IAAI;AAAA,IACzB,GAAY;AAAA,MACV,OACE,iCAAiC,6BACjC,YAAY;AAAA,QACV,MAAM,OAAO,QAAQ,IAAI,EAAE,IAAI,KAAK,QAAQ,UAAU,CAAC,EAAE,CAAC,CAAC,EAAE,QAAQ,eACnE,sBACF;AAAA,QACA,OAAQ,MAAM,QAAQ,OAAO,KAAM,CAAC,CAAC,EAAE,aAAa,CAAC;AAAA,SAEvD,KAAK,OACP;AAAA,MAEA,OACE,0CAA0C,SAC1C,YAAY;AAAA,QAIV,MAAM,OAAO,QAAQ,iBAAiB,MAAM,CAAC,EAAE,QAAQ,eACrD,sBACF;AAAA,SAEF,KAAK,OACP;AAAA,MAEA,OACE,iDAAiD,6BACjD,YAAY;AAAA,QAGV,MAAM,OACJ,QAAQ,QAAQ;AAAA,UACd,EAAE,IAAI,QAAQ,QAAQ,MAAM,UAAU,CAAC,EAAE;AAAA,UACzC,EAAE,IAAI,OAAO,QAAQ,UAAU,CAAC,EAAE;AAAA,QACpC,CAAC,CACH,EAAE,QAAQ,eAAe,sBAAsB;AAAA,QAC/C,OAAQ,MAAM,QAAQ,OAAO,KAAM,CAAC,CAAC,EAAE,aAAa,CAAC;AAAA,SAEvD,KAAK,OACP;AAAA,IACF;AAAA,GACD;AAAA;;;AKxFH,qBAAS,qBAAU;AAeZ,SAAS,mBAAmB,CAAC,MAAuC;AAAA,EACzE,UAAS,OAAO,KAAK,gBAAgB,SAAS,EAAE,kBAAkB,MAAM;AAAA,IACtE,MAAM,QAAQ,YAAY,IAAI;AAAA,IAE9B,MAAM,MAAM,gBAAgB,EAC1B,+DACA,YAAY;AAAA,MACV,MAAM,KAAK,cAAc,MAAM,GAAG;AAAA,QAChC,IAAI;AAAA,QACJ,QAAQ,CAAC,GAAG,KAAK;AAAA,QACjB,UAAU,EAAE,QAAQ,SAAS;AAAA,MAC/B,CAAC;AAAA,MAED,MAAM,OAAQ,MAAM,MAAM,EAAE,iBAAiB,OAAO,EAAE,MAAM,EAAE,CAAC;AAAA,MAE/D,QAAO,KAAK,IAAI,CAAC,QAAQ,IAAI,EAAE,CAAC,EAAE,UAAU,QAAQ;AAAA,MAGpD,MAAM,SAAS,KAAK,KAAK,CAAC,QAAQ,IAAI,OAAO,QAAQ;AAAA,MACrD,QAAO,CAAC,GAAI,OAAQ,MAAuB,CAAC,EAAE,QAAQ,CAAC,GAAG,KAAK,CAAC;AAAA,OAElE,KAAK,OACP;AAAA,GACD;AAAA;;;ACtCH,qBAAS,qBAAU;AAeZ,SAAS,mBAAmB,CAAC,MAAuC;AAAA,EACzE,UAAS,OAAO,CAAC,KAAK,aAAa,sBAAsB,EAAE,kBAAkB,MAAM;AAAA,IACjF,MAAM,QAAQ,YAAY,IAAI;AAAA,IAC9B,MAAM,SAAS,MAAM,MAAM,gBAAgB;AAAA,IAE3C,OACE,4CACA,YAAY;AAAA,MACV,MAAM,OAAQ,MAAM,MAAM,EAAE,iBAAiB,MAAM,EAAE,QAAQ,EAAE,QAAQ,IAAI,EAAE,CAAC;AAAA,MAC9E,QAAO,KAAK,IAAI,CAAC,QAAQ,IAAI,EAAE,CAAC,EAAE,QAAQ,CAAC,OAAO,CAAC;AAAA,OAErD,KAAK,OACP;AAAA,IAEA,OACE,oDACA,YAAY;AAAA,MAGV,MAAM,OAAQ,MAAM,MAAM,EAAE,iBAAiB,MAAM;AAAA,QACjD,QAAQ,EAAE,QAAQ,IAAI;AAAA,QACtB,MAAM;AAAA,MACR,CAAC;AAAA,MACD,QAAO,KAAK,IAAI,CAAC,QAAQ,IAAI,EAAE,CAAC,EAAE,QAAQ,CAAC,OAAO,CAAC;AAAA,OAErD,KAAK,OACP;AAAA,IAEA,OACE,mDACA,YAAY;AAAA,MACV,QACE,MAAM,MAAM,EAAE,iBAAiB,MAAM,EAAE,QAAQ,EAAE,QAAQ,UAAU,EAAE,CAAC,CACxE,EAAE,aAAa,CAAC;AAAA,OAElB,KAAK,OACP;AAAA,GACD;AAAA;;;ACpDH,qBAAS,qBAAU;AAeZ,SAAS,kBAAkB,CAAC,MAAuC;AAAA,EACxE,UAAS,iBAAiB,MAAM;AAAA,IAC9B,MAAM,QAAQ,YAAY,IAAI;AAAA,IAC9B,MAAM,SAAS,MAAM,MAAM,eAAe;AAAA,IAE1C,OACE,+CACA,YAAY;AAAA,MACV,MAAM,OAAQ,MAAM,MAAM,EAAE,iBAAiB,MAAM,EAAE,MAAM,EAAE,CAAC;AAAA,MAC9D,QAAO,KAAK,IAAI,CAAC,QAAQ,IAAI,EAAE,CAAC,EAAE,QAAQ,CAAC,QAAQ,WAAW,CAAC;AAAA,OAEjE,KAAK,OACP;AAAA,IAEA,OACE,sDACA,YAAY;AAAA,MAGV,QAAO,MAAM,MAAM,EAAE,iBAAiB,MAAM,EAAE,MAAM,GAAG,CAAC,CAAC,EAAE,aAAa,CAAC;AAAA,OAE3E,KAAK,OACP;AAAA,IAEA,UAAS,OAAO,CAAC,KAAK,aAAa,sBAAsB,EAAE,kBAAkB,MAAM;AAAA,MACjF,OACE,0CACA,YAAY;AAAA,QACV,MAAM,OAAQ,MAAM,MAAM,EAAE,iBAAiB,MAAM,EAAE,gBAAgB,IAAI,CAAC;AAAA,QAC1E,QAAO,KAAK,IAAI,CAAC,QAAQ,IAAI,EAAE,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC;AAAA,SAEpD,KAAK,OACP;AAAA,MAEA,OACE,6CACA,YAAY;AAAA,QAUV,MAAM,OAAQ,MAAM,MAAM,EAAE,iBAAiB,MAAM,EAAE,gBAAgB,EAAE,CAAC;AAAA,QACxE,QAAO,KAAK,IAAI,CAAC,QAAQ,IAAI,EAAE,CAAC,EAAE,QAAQ,CAAC,QAAQ,aAAa,OAAO,CAAC;AAAA,SAE1E,KAAK,OACP;AAAA,KACD;AAAA,GACF;AAAA;;;ACnEH,qBAAS,qBAAU;AAiBZ,SAAS,sBAAsB,CAAC,MAAuC;AAAA,EAC5E,UAAS,qBAAqB,MAAM;AAAA,IAClC,MAAM,QAAQ,YAAY,IAAI;AAAA,IAE9B,MAAM,MAAM,mBAAmB,EAC7B,mFACA,YAAY;AAAA,MACV,MAAM,OAAQ,MAAM,MAAM,EAAE,iBAAiB,IAAI;AAAA,MAEjD,QAAO,KAAK,IAAI,CAAC,QAAQ,IAAI,EAAE,CAAC,EAAE,QAAQ,CAAC,QAAQ,aAAa,OAAO,CAAC;AAAA,MAGxE,QAAO,KAAK,GAAI,KAAK,EAAE,YAAY,GAAG,CAAC;AAAA,MACvC,QAAO,KAAK,GAAI,KAAK,EAAE,YAAY,KAAK,SAAS,CAAC;AAAA,MAClD,QAAO,KAAK,GAAI,KAAK,EAAE,YAAY,GAAG,CAAC;AAAA,OAEzC,KAAK,OACP;AAAA,GACD;AAAA;;;ACnCH,qBAAS,qBAAU;AAeZ,SAAS,0BAA0B,CAAC,MAAuC;AAAA,EAChF,UAAS,yBAAyB,MAAM;AAAA,IACtC,MAAM,QAAQ,YAAY,IAAI;AAAA,IAE9B,MAAM,MAAM,uBAAuB,EACjC,gDACA,YAAY;AAAA,MACV,IAAI;AAAA,MAIF,MAAM,EAGN,GAAG,oBAAoB,CAAC,QAAQ,YAAY;AAAA,QAC5C,OAAO;AAAA,OACR;AAAA,MAED,MAAM,MAAM,EAAE,iBAAiB,MAAM,EAAE,MAAM,EAAE,CAAC;AAAA,MAEhD,QAAO,MAAM,IAAI,CAAC,QAAQ,IAAI,EAAE,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC;AAAA,OAErD,KAAK,OACP;AAAA,GACD;AAAA;;;ACvCH,qBAAS,qBAAU;AAgBZ,SAAS,oBAAoB,CAAC,MAAuC;AAAA,EAC1E,UAAS,mBAAmB,MAAM;AAAA,IAChC,MAAM,QAAQ,YAAY,IAAI;AAAA,IAE9B,MAAM,MAAM,iBAAiB,EAC3B,2EACA,YAAY;AAAA,MACV,OAAO,OAAQ,MAAM,MAAM,EAAE,iBAAiB,MAAM,EAAE,MAAM,EAAE,CAAC;AAAA,MAE/D,QAAO,YAAY,OAAO,IAAK,MAAM,CAAC,EAAE,KAAK,IAAI;AAAA,MACjD,QAAO,CAAC,GAAI,IAAK,MAAuB,CAAC,EAAE,QAAQ,CAAC,GAAG,IAAI,CAAC;AAAA,OAE9D,KAAK,OACP;AAAA,GACD;AAAA;;;AXTI,SAAS,wBAAwB,CAAC,MAAuC;AAAA,EAC9E,UAAS,OAAO,KAAK,IAAI,EAAE,4BAA4B,KAAK,QAAQ,MAAM;AAAA,IACxE,uBAAuB,IAAI;AAAA,IAC3B,qBAAqB,IAAI;AAAA,IACzB,mBAAmB,IAAI;AAAA,IACvB,oBAAoB,IAAI;AAAA,IACxB,2BAA2B,IAAI;AAAA,IAC/B,oBAAoB,IAAI;AAAA,IACxB,yBAAyB,IAAI;AAAA,GAC9B;AAAA;",
19
+ "debugId": "DFF5A9885C0171A264756E2164756E21",
20
+ "names": []
21
+ }
@@ -0,0 +1,23 @@
1
+ /**
2
+ * @license
3
+ * Copyright 2026 Steven Roussey <sroussey@gmail.com>
4
+ * SPDX-License-Identifier: Apache-2.0
5
+ */
6
+ import type { WebSearchProviderConformanceOpts } from "../types";
7
+ /**
8
+ * What a provider declares is what it is asked to do, and nothing else.
9
+ *
10
+ * `WebSearchCapabilities` is a promise made to the router: an option the record
11
+ * says the provider cannot serve must be refused BEFORE the vendor is reached,
12
+ * and an option it says the provider can serve must actually be attempted. Both
13
+ * halves fail quietly on their own. Under-declaring loses working behaviour to
14
+ * `"auto"` routing, which walks past the provider that could have served the
15
+ * request; over-declaring is worse, because the request is routed here and the
16
+ * adapter throws — or, in the failure this whole record exists to prevent,
17
+ * silently drops the option and reports a search that ran without it.
18
+ *
19
+ * Asserted by reading the record rather than by listing what each provider
20
+ * does. That is the only version of this test that stays true when a provider
21
+ * changes what it supports, which is the event it exists to catch.
22
+ */
23
+ export declare function capabilityAgreementBlock(opts: WebSearchProviderConformanceOpts): void;