@cosmicdrift/kumiko-framework 0.307.0 → 0.309.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (174) hide show
  1. package/package.json +5 -5
  2. package/src/__tests__/anonymous-access.integration.test.ts +1 -1
  3. package/src/__tests__/error-contract.integration.test.ts +1 -1
  4. package/src/__tests__/field-access.integration.test.ts +1 -1
  5. package/src/__tests__/fixtures/migrate-open-to-all/manual-only.expected.ts +1 -1
  6. package/src/__tests__/fixtures/migrate-open-to-all/manual-only.input.ts +1 -1
  7. package/src/__tests__/fixtures/migrate-open-to-all/test-simple.expected.ts +1 -1
  8. package/src/__tests__/fixtures/migrate-open-to-all/test-simple.input.ts +1 -1
  9. package/src/__tests__/full-stack.integration.test.ts +1 -1
  10. package/src/__tests__/transition-guard.integration.test.ts +1 -1
  11. package/src/api/__tests__/api.test.ts +1 -1
  12. package/src/api/__tests__/batch.integration.test.ts +1 -1
  13. package/src/api/__tests__/body-limit.test.ts +1 -1
  14. package/src/api/__tests__/dispatcher-live.integration.test.ts +1 -1
  15. package/src/api/__tests__/extra-routes.integration.test.ts +1 -1
  16. package/src/api/__tests__/http-route-rate-limit.integration.test.ts +1 -1
  17. package/src/api/__tests__/nested-write-ownership.integration.test.ts +1 -1
  18. package/src/api/__tests__/nested-write.integration.test.ts +1 -1
  19. package/src/api/__tests__/pii-leak-guard.integration.test.ts +1 -1
  20. package/src/api/__tests__/redis-sse-broker.integration.test.ts +73 -6
  21. package/src/api/__tests__/request-locale.integration.test.ts +1 -1
  22. package/src/api/__tests__/server-boot-guards.test.ts +1 -1
  23. package/src/api/__tests__/server-error-logging.test.ts +1 -1
  24. package/src/api/__tests__/sse-broker.test.ts +107 -13
  25. package/src/api/__tests__/stream-escape-hatch.integration.test.ts +1 -1
  26. package/src/api/auth-routes.ts +1 -1
  27. package/src/api/redis-sse-broker.ts +51 -11
  28. package/src/api/server.ts +11 -0
  29. package/src/api/sse-broker.ts +61 -22
  30. package/src/bun-db/__tests__/postgres-closed-socket-write.integration.test.ts +87 -0
  31. package/src/bun-db/connection.ts +2 -0
  32. package/src/changes.json +61 -0
  33. package/src/compliance/override-schema.ts +1 -1
  34. package/src/crypto/__tests__/event-pii.test.ts +1 -1
  35. package/src/crypto/kms-adapter.ts +1 -1
  36. package/src/db/__tests__/feature-table-sources.test.ts +2 -2
  37. package/src/db/__tests__/tenant-db-where-merge.integration.test.ts +1 -1
  38. package/src/db/collect-table-metas.ts +3 -6
  39. package/src/db/feature-table-sources.ts +3 -4
  40. package/src/db/queries/event-consumer.ts +98 -6
  41. package/src/derivatives/__tests__/derivatives-context.integration.test.ts +1 -1
  42. package/src/derivatives/delete-stored-file.ts +86 -0
  43. package/src/derivatives/index.ts +2 -0
  44. package/src/engine/__tests__/boot-validator-action-wiring.test.ts +1 -1
  45. package/src/engine/__tests__/boot-validator-dashboard.test.ts +1 -1
  46. package/src/engine/__tests__/boot-validator-nav-area.test.ts +1 -1
  47. package/src/engine/__tests__/boot-validator-pii-retention.test.ts +1 -1
  48. package/src/engine/__tests__/boot-validator-projection-list.test.ts +1 -1
  49. package/src/engine/__tests__/boot-validator-query-output-schema.test.ts +1 -1
  50. package/src/engine/__tests__/boot-validator-query-refs.test.ts +1 -1
  51. package/src/engine/__tests__/boot-validator-ref-entity.test.ts +1 -1
  52. package/src/engine/__tests__/boot-validator-s0-integration.test.ts +1 -1
  53. package/src/engine/__tests__/boot-validator.test.ts +35 -1
  54. package/src/engine/__tests__/build-app-schema.test.ts +1 -1
  55. package/src/engine/__tests__/build-config-feature-schema-secrets.test.ts +1 -1
  56. package/src/engine/__tests__/define-feature-entity-mapping.test.ts +1 -1
  57. package/src/engine/__tests__/engine.test.ts +1 -1
  58. package/src/engine/__tests__/event-helpers.test.ts +1 -1
  59. package/src/engine/__tests__/event-migration-declarative.test.ts +1 -1
  60. package/src/engine/__tests__/extend-entity-projection.test.ts +1 -1
  61. package/src/engine/__tests__/extends-registrar.test.ts +1 -1
  62. package/src/engine/__tests__/hook-phases.test.ts +1 -1
  63. package/src/engine/__tests__/lifecycle-hooks.test.ts +1 -1
  64. package/src/engine/__tests__/pipeline-engine.test.ts +1 -1
  65. package/src/engine/__tests__/pipeline-handler.integration.test.ts +1 -1
  66. package/src/engine/__tests__/pipeline-observability.integration.test.ts +1 -1
  67. package/src/engine/__tests__/pipeline-performance.integration.test.ts +1 -1
  68. package/src/engine/__tests__/pipeline-sub-pipelines.test.ts +1 -1
  69. package/src/engine/__tests__/post-query-hook.test.ts +1 -1
  70. package/src/engine/__tests__/projection-detail-actions.test.ts +1 -1
  71. package/src/engine/__tests__/projection-detail-singleton.test.ts +1 -1
  72. package/src/engine/__tests__/projection-detail-tabs.test.ts +1 -1
  73. package/src/engine/__tests__/read-steps-tenant-filter.integration.test.ts +1 -1
  74. package/src/engine/__tests__/registry.test.ts +1 -1
  75. package/src/engine/__tests__/screen.test.ts +1 -1
  76. package/src/engine/__tests__/validate-projection-allowlist.test.ts +1 -1
  77. package/src/engine/boot-validator/__tests__/access-declarations.test.ts +1 -1
  78. package/src/engine/boot-validator/__tests__/action-icon.test.ts +81 -0
  79. package/src/engine/boot-validator/__tests__/anonymous-rate-limit-required.test.ts +1 -1
  80. package/src/engine/boot-validator/__tests__/no-all-role-in-handler-access.test.ts +1 -1
  81. package/src/engine/boot-validator/__tests__/parent-ref.test.ts +1 -1
  82. package/src/engine/boot-validator/__tests__/rate-limit-disabled-reason.test.ts +1 -1
  83. package/src/engine/boot-validator/screens.ts +83 -0
  84. package/src/engine/define-handler.ts +2 -1
  85. package/src/engine/entity-handlers.ts +2 -1
  86. package/src/engine/feature-ast/__tests__/pattern-change-schema.test.ts +1 -1
  87. package/src/engine/feature-ast/pattern-change-schema.ts +1 -1
  88. package/src/engine/feature-builder-state.ts +2 -1
  89. package/src/engine/feature-config-events-jobs.ts +2 -1
  90. package/src/engine/feature-entity-handlers.ts +2 -1
  91. package/src/engine/schema-builder.ts +1 -1
  92. package/src/entrypoint/__tests__/entrypoint-attach-dispatcher.integration.test.ts +1 -1
  93. package/src/entrypoint/__tests__/entrypoint-job-wiring.integration.test.ts +1 -1
  94. package/src/entrypoint/__tests__/split-deploy.integration.test.ts +1 -1
  95. package/src/env/__tests__/compose-env-schema.test.ts +1 -1
  96. package/src/env/__tests__/dry-run.test.ts +1 -1
  97. package/src/env/__tests__/parse-env-dry-run.test.ts +1 -1
  98. package/src/env/_zod-introspect.ts +1 -1
  99. package/src/env/dry-run.ts +1 -1
  100. package/src/env/index.ts +1 -1
  101. package/src/errors/__tests__/classes.test.ts +1 -1
  102. package/src/es-ops/__tests__/runner.integration.test.ts +1 -1
  103. package/src/event-store/__tests__/backfill-pii.integration.test.ts +1 -1
  104. package/src/event-store/__tests__/event-attribution.integration.test.ts +1 -1
  105. package/src/event-store/__tests__/upcaster.integration.test.ts +1 -1
  106. package/src/jobs/__tests__/job-backoff.integration.test.ts +1 -1
  107. package/src/jobs/__tests__/job-event-trigger.integration.test.ts +1 -1
  108. package/src/jobs/__tests__/job-multi-trigger.integration.test.ts +1 -1
  109. package/src/jobs/__tests__/job-public-intake-origin.integration.test.ts +1 -1
  110. package/src/jobs/__tests__/job-retention.integration.test.ts +1 -1
  111. package/src/jobs/__tests__/job-write-as.integration.test.ts +1 -1
  112. package/src/jobs/__tests__/jobs.integration.test.ts +1 -1
  113. package/src/observability/__tests__/metrics-wiring.test.ts +1 -1
  114. package/src/observability/__tests__/observability.integration.test.ts +1 -1
  115. package/src/observability/metrics-wiring.ts +1 -1
  116. package/src/pipeline/__tests__/archive-stream.integration.test.ts +1 -1
  117. package/src/pipeline/__tests__/causation-chain.integration.test.ts +1 -1
  118. package/src/pipeline/__tests__/ctx-bridge.integration.test.ts +1 -1
  119. package/src/pipeline/__tests__/ctx-systemdb.integration.test.ts +1 -1
  120. package/src/pipeline/__tests__/dispatcher.test.ts +1 -1
  121. package/src/pipeline/__tests__/domain-events-projections.integration.test.ts +1 -1
  122. package/src/pipeline/__tests__/escape-hatch-audit.integration.test.ts +1 -1
  123. package/src/pipeline/__tests__/escape-hatch.integration.test.ts +1 -1
  124. package/src/pipeline/__tests__/event-dedup.integration.test.ts +1 -1
  125. package/src/pipeline/__tests__/event-define-event-strict.integration.test.ts +1 -1
  126. package/src/pipeline/__tests__/event-dispatcher-batch-delivery.test.ts +178 -0
  127. package/src/pipeline/__tests__/event-dispatcher-batch-handler.integration.test.ts +143 -0
  128. package/src/pipeline/__tests__/event-dispatcher-db-outage.integration.test.ts +192 -0
  129. package/src/pipeline/__tests__/event-dispatcher-idle.integration.test.ts +94 -0
  130. package/src/pipeline/__tests__/event-dispatcher-start-from.integration.test.ts +103 -3
  131. package/src/pipeline/__tests__/fetch-for-writing.integration.test.ts +1 -1
  132. package/src/pipeline/__tests__/idempotency-transient-failure.integration.test.ts +1 -1
  133. package/src/pipeline/__tests__/job-trigger-consumer.integration.test.ts +1 -1
  134. package/src/pipeline/__tests__/lifecycle-pipeline.test.ts +1 -1
  135. package/src/pipeline/__tests__/load-aggregate-query.integration.test.ts +1 -1
  136. package/src/pipeline/__tests__/msp-multi-hop.integration.test.ts +1 -1
  137. package/src/pipeline/__tests__/msp-rebuild.integration.test.ts +116 -1
  138. package/src/pipeline/__tests__/multi-stream-projection.integration.test.ts +1 -1
  139. package/src/pipeline/__tests__/pending-gap-ranges.test.ts +94 -0
  140. package/src/pipeline/__tests__/post-query-hook.integration.test.ts +1 -1
  141. package/src/pipeline/__tests__/public-intake-runtime-gate.integration.test.ts +1 -1
  142. package/src/pipeline/__tests__/query-projection.integration.test.ts +1 -1
  143. package/src/pipeline/__tests__/rate-limit-default.integration.test.ts +1 -1
  144. package/src/pipeline/__tests__/rebuild-poison-quarantine.integration.test.ts +1 -1
  145. package/src/pipeline/__tests__/search-consumer-named-events.integration.test.ts +1 -1
  146. package/src/pipeline/__tests__/system-identity-switch.integration.test.ts +1 -1
  147. package/src/pipeline/__tests__/try-append-event.integration.test.ts +1 -1
  148. package/src/pipeline/__tests__/unsafe-raw-escape-hatch.integration.test.ts +1 -1
  149. package/src/pipeline/dispatch-stream.ts +6 -1
  150. package/src/pipeline/event-dispatcher-delivery.ts +132 -37
  151. package/src/pipeline/event-dispatcher.ts +121 -17
  152. package/src/pipeline/index.ts +1 -0
  153. package/src/pipeline/msp-rebuild.ts +39 -1
  154. package/src/pipeline/pending-gap-ranges.ts +48 -0
  155. package/src/pipeline/system-hooks.ts +223 -68
  156. package/src/pipeline/write-origin.ts +1 -1
  157. package/src/rate-limit/__tests__/dispatcher-l3.integration.test.ts +1 -1
  158. package/src/search/__tests__/derive-search-adapter-config.test.ts +65 -0
  159. package/src/search/__tests__/meilisearch-adapter.integration.test.ts +91 -0
  160. package/src/search/__tests__/search-consumer-batch.integration.test.ts +306 -0
  161. package/src/search/derive-search-adapter-config.ts +27 -0
  162. package/src/search/index.ts +1 -0
  163. package/src/search/meilisearch-adapter.ts +96 -14
  164. package/src/secrets/__tests__/contains-secret.test.ts +1 -1
  165. package/src/stack/__tests__/request-helper.integration.test.ts +1 -1
  166. package/src/stack/__tests__/setup-test-stack-entity-tables.integration.test.ts +65 -0
  167. package/src/stack/__tests__/setup-test-stack-jobs.integration.test.ts +1 -1
  168. package/src/stack/__tests__/setup-test-stack-metrics.integration.test.ts +1 -1
  169. package/src/stack/test-stack.ts +61 -54
  170. package/src/testing/__tests__/e2e-generator.test.ts +1 -1
  171. package/src/testing/__tests__/real-providers.test.ts +9 -1
  172. package/src/testing/e2e-generator.ts +1 -1
  173. package/src/testing/index.ts +1 -1
  174. package/src/testing/real-providers.ts +12 -4
@@ -4,7 +4,6 @@ import type { JwtHelper } from "../api/jwt";
4
4
  import { buildServer } from "../api/server";
5
5
  import { createSseBroker, type SseBroker } from "../api/sse-broker";
6
6
  import type { PgClient } from "../db/connection";
7
- import { extractTableInfo } from "../db/query";
8
7
  import { validateOwnershipBoot } from "../engine/boot-validator/ownership";
9
8
  import { createRegistry } from "../engine/registry";
10
9
  import type { AppContext, FeatureDefinition, JobRunIn, Registry, TenantId } from "../engine/types";
@@ -181,6 +180,10 @@ export type TestStackOptions = {
181
180
  * out (e.g. 60_000) so the polling timer can't land inside the
182
181
  * assertion window and mask a dead subscription — see E.4 (#2042). */
183
182
  eventDispatcherPollIntervalMs?: number;
183
+ /** Opt out of auto-created r.entity() tables for callers that create/migrate
184
+ * them themselves. Default true — false pushes only projection/MSP/
185
+ * storeTable sources, same as before fw#3102. */
186
+ entityTables?: boolean;
184
187
  };
185
188
 
186
189
  const DEFAULT_JWT_SECRET = "test-stack-secret-minimum-32-characters!!";
@@ -215,68 +218,72 @@ export async function setupTestStack(options: TestStackOptions): Promise<TestSta
215
218
  createTestRedis(),
216
219
  ]);
217
220
 
218
- // Every ES-entity writes events via createEventStoreExecutor in the
219
- // feature's write handlers. Auto-create the events table so every
220
- // setupTestStack call is ready for writes without needing a manual
221
- // createEventsTable().
222
- await createEventsTable(testDb.db);
223
- // Archive-stream metadata — needed by ctx.appendEvent's archive guard and
224
- // loadAggregate's default-skip. Idempotent, so production boot running
225
- // the same call is fine.
226
- await createArchivedStreamsTable(testDb.db);
221
+ // Any throw in this DDL region must still drop the just-created ephemeral
222
+ // DB/Redis — the caller never gets the stack object back to call
223
+ // cleanup() itself (same "leaked ephemeral DB" failure class as the later
224
+ // try/catch below, but for setup that runs before that try starts).
225
+ try {
226
+ // Every ES-entity writes events via createEventStoreExecutor in the
227
+ // feature's write handlers. Auto-create the events table so every
228
+ // setupTestStack call is ready for writes without needing a manual
229
+ // createEventsTable().
230
+ await createEventsTable(testDb.db);
231
+ // Archive-stream metadata — needed by ctx.appendEvent's archive guard and
232
+ // loadAggregate's default-skip. Idempotent, so production boot running
233
+ // the same call is fine.
234
+ await createArchivedStreamsTable(testDb.db);
227
235
 
228
- // Framework state for projection rebuild/status + event-consumer cursors.
229
- // Idempotent — production boot flows run the same calls.
230
- const { createProjectionStateTable, createEventConsumerStateTable } = await import("../pipeline");
231
- await createProjectionStateTable(testDb.db);
232
- await createEventConsumerStateTable(testDb.db);
236
+ // Framework state for projection rebuild/status + event-consumer cursors.
237
+ // Idempotent — production boot flows run the same calls.
238
+ const { createProjectionStateTable, createEventConsumerStateTable } = await import(
239
+ "../pipeline"
240
+ );
241
+ await createProjectionStateTable(testDb.db);
242
+ await createEventConsumerStateTable(testDb.db);
233
243
 
234
- // Files support: when a provider is registered, the fileRefs table must
235
- // exist before the first upload. Skipped when no provider — the table
236
- // stays off tenant test DBs that never touch files.
237
- if (options.files) {
238
- const { fileRefsTable } = await import("../files");
239
- await unsafePushTables(testDb.db, { fileRefsTable });
240
- }
241
-
242
- // Projection-/MSP-/raw-tables: the executor (or async dispatcher) writes
243
- // into them as soon as the first matching event flows, so the DDL must
244
- // exist before setupTestStack returns. The source list is shared with
245
- // collectTableMetas (`kumiko schema generate`) — divergence between the
246
- // two was exactly the #255 prod-crash. Two registrations backed by the
247
- // same physical table (e.g. an alternative apply-shape for the same
248
- // read-model in a test feature) are deduped by table reference so we
249
- // emit only one CREATE TABLE per physical table.
250
- const { enumerateFeatureTableSources } = await import("../db/feature-table-sources");
251
- const projectionTables: Record<string, unknown> = {};
252
- // Dedup by NAME, matching collectTableMetas — by-reference alone let two
253
- // distinct table objects with the same name slip through as a double
254
- // CREATE TABLE while schema-generate emitted only one meta (silent
255
- // test-vs-schema divergence).
256
- const seenTableNames = new Set<string>();
257
- for (const feature of options.features) {
258
- for (const { table, origin } of enumerateFeatureTableSources(feature)) {
259
- const name = extractTableInfo(table).name;
260
- if (seenTableNames.has(name)) continue;
261
- seenTableNames.add(name);
262
- projectionTables[origin] = table;
244
+ // Files support: when a provider is registered, the fileRefs table must
245
+ // exist before the first upload. Skipped when no provider — the table
246
+ // stays off tenant test DBs that never touch files.
247
+ if (options.files) {
248
+ const { fileRefsTable } = await import("../files");
249
+ await unsafePushTables(testDb.db, { fileRefsTable });
263
250
  }
264
- }
265
- if (Object.keys(projectionTables).length > 0) {
266
- // unsafePushTables emits raw CREATE TABLE — fine for ephemeral test DBs but
267
- // collides on re-boot against a persistent DB whose projection tables
268
- // were created during a previous run. Filter out the ones that already
269
- // exist so the re-boot doesn't fail on duplicate CREATE TABLE.
251
+
252
+ // Same table list as `kumiko schema generate` (collectTableMetas) — divergence
253
+ // was the #255 prod-crash and the #3102 missing r.entity() tables.
254
+ const { collectTableMetas } = await import("../db/collect-table-metas");
270
255
  const { tableExists } = await import("../db/schema-inspection");
256
+ let metas = collectTableMetas(options.features);
257
+ if (options.entityTables === false) {
258
+ const { enumerateFeatureTableSources } = await import("../db/feature-table-sources");
259
+ const { extractTableInfo } = await import("../db/query");
260
+ const nonEntityNames = new Set(
261
+ options.features.flatMap((f) =>
262
+ enumerateFeatureTableSources(f).map((s) => extractTableInfo(s.table).name),
263
+ ),
264
+ );
265
+ metas = metas.filter((m) => nonEntityNames.has(m.tableName));
266
+ }
271
267
  const missing: Record<string, unknown> = {};
272
- for (const [key, tbl] of Object.entries(projectionTables)) {
273
- const physical = extractTableInfo(tbl).name;
274
- if (await tableExists(testDb.db, `public.${physical}`)) continue;
275
- missing[key] = tbl;
268
+ for (const meta of metas) {
269
+ if (await tableExists(testDb.db, `public.${meta.tableName}`)) continue;
270
+ missing[meta.tableName] = meta;
276
271
  }
277
272
  if (Object.keys(missing).length > 0) {
278
273
  await unsafePushTables(testDb.db, missing);
279
274
  }
275
+ } catch (error) {
276
+ try {
277
+ await testDb.cleanup();
278
+ } catch {
279
+ // ignore — `error` is the one that matters
280
+ }
281
+ try {
282
+ await testRedis.cleanup();
283
+ } catch {
284
+ // ignore — `error` is the one that matters
285
+ }
286
+ throw error;
280
287
  }
281
288
 
282
289
  const searchAdapter = createInMemorySearchAdapter();
@@ -1,5 +1,5 @@
1
1
  import { describe, expect, test } from "bun:test";
2
- import { z } from "zod";
2
+ import * as z from "zod";
3
3
  import {
4
4
  createBooleanField,
5
5
  createDateField,
@@ -1,5 +1,5 @@
1
1
  import { afterEach, beforeEach, describe, expect, test } from "bun:test";
2
- import { requireRealProviders } from "../real-providers";
2
+ import { isRealProviderRun, REAL_PROVIDERS_ENV, requireRealProviders } from "../real-providers";
3
3
 
4
4
  const CI = "CI";
5
5
  const FLAG = "KUMIKO_REAL_PROVIDERS";
@@ -52,3 +52,11 @@ describe("requireRealProviders", () => {
52
52
  expect(() => requireRealProviders()).not.toThrow();
53
53
  });
54
54
  });
55
+
56
+ describe("isRealProviderRun", () => {
57
+ test("reads the exported flag name from an explicit env map, defaulting to process.env", () => {
58
+ expect(isRealProviderRun({ [REAL_PROVIDERS_ENV]: "1" })).toBe(true);
59
+ expect(isRealProviderRun({ [REAL_PROVIDERS_ENV]: "true" })).toBe(false);
60
+ expect(isRealProviderRun({})).toBe(false);
61
+ });
62
+ });
@@ -24,7 +24,7 @@
24
24
  // "not supported yet" — wir füllen nach, wenn ein echter Caller es braucht.
25
25
  // Fake-komplexe-Werte würden nur False-Green-Tests produzieren.
26
26
 
27
- import type { z } from "zod";
27
+ import type * as z from "zod";
28
28
  import {
29
29
  type EntityDefinition,
30
30
  type EntityEditScreenDefinition,
@@ -45,7 +45,7 @@ export {
45
45
  createRecordingProvider,
46
46
  type RecordingProvider,
47
47
  } from "./observability-recorder";
48
- export { requireRealProviders } from "./real-providers";
48
+ export { isRealProviderRun, REAL_PROVIDERS_ENV, requireRealProviders } from "./real-providers";
49
49
  export { deleteRows, seedRow, seedRows, updateRows } from "./seed";
50
50
  export {
51
51
  sharedItemEntity,
@@ -1,4 +1,4 @@
1
- const REAL_PROVIDERS_FLAG = "KUMIKO_REAL_PROVIDERS";
1
+ export const REAL_PROVIDERS_ENV = "KUMIKO_REAL_PROVIDERS";
2
2
  const CI_FLAG = "CI";
3
3
 
4
4
  function isCi(): boolean {
@@ -6,15 +6,23 @@ function isCi(): boolean {
6
6
  return ci !== undefined && ci !== "" && ci !== "0" && ci.toLowerCase() !== "false";
7
7
  }
8
8
 
9
+ // Apps and the e2e/bunfig templates gate real-provider runs on this same
10
+ // check instead of re-reading KUMIKO_REAL_PROVIDERS themselves.
11
+ export function isRealProviderRun(
12
+ env: Readonly<Record<string, string | undefined>> = process.env,
13
+ ): boolean {
14
+ return env[REAL_PROVIDERS_ENV] === "1";
15
+ }
16
+
9
17
  export function requireRealProviders(): void {
10
18
  if (isCi()) {
11
19
  throw new Error(
12
- `Real-provider tests never run in CI (CI is set). Run them locally with ${REAL_PROVIDERS_FLAG}=1 via the test:real / e2e:real script.`,
20
+ `Real-provider tests never run in CI (CI is set). Run them locally with ${REAL_PROVIDERS_ENV}=1 via the test:real / e2e:real script.`,
13
21
  );
14
22
  }
15
- if (process.env[REAL_PROVIDERS_FLAG] !== "1") {
23
+ if (!isRealProviderRun()) {
16
24
  throw new Error(
17
- `Real-provider test needs ${REAL_PROVIDERS_FLAG}=1 (an API key alone never enables it). Run it via the test:real / e2e:real script.`,
25
+ `Real-provider test needs ${REAL_PROVIDERS_ENV}=1 (an API key alone never enables it). Run it via the test:real / e2e:real script.`,
18
26
  );
19
27
  }
20
28
  }