@wildo-ai/saas-backend-lib 1.1.8 → 1.1.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/esm/data-seeding/models/data-seed.shared.schemas.d.ts +8 -0
- package/dist/esm/data-seeding/models/data-seed.shared.schemas.d.ts.map +1 -1
- package/dist/esm/data-seeding/models/data-seed.shared.schemas.js +17 -0
- package/dist/esm/data-seeding/models/data-seed.shared.schemas.js.map +1 -1
- package/dist/esm/data-seeding/services/data-seed-application-bootstrap.backend.d.ts.map +1 -1
- package/dist/esm/data-seeding/services/data-seed-application-bootstrap.backend.js +9 -1
- package/dist/esm/data-seeding/services/data-seed-application-bootstrap.backend.js.map +1 -1
- package/dist/esm/data-seeding/services/data-seed-environment-admission.backend.d.ts +23 -0
- package/dist/esm/data-seeding/services/data-seed-environment-admission.backend.d.ts.map +1 -0
- package/dist/esm/data-seeding/services/data-seed-environment-admission.backend.js +25 -0
- package/dist/esm/data-seeding/services/data-seed-environment-admission.backend.js.map +1 -0
- package/dist/esm/databases/services/postgresql-migration-runner.backend.d.ts +6 -0
- package/dist/esm/databases/services/postgresql-migration-runner.backend.d.ts.map +1 -1
- package/dist/esm/databases/services/postgresql-migration-runner.backend.js +8 -1
- package/dist/esm/databases/services/postgresql-migration-runner.backend.js.map +1 -1
- package/dist/esm/external-providers/services/external-providers-registry.backend.service.d.ts +0 -1
- package/dist/esm/external-providers/services/external-providers-registry.backend.service.d.ts.map +1 -1
- package/dist/esm/external-providers/services/external-providers-registry.backend.service.js +4 -39
- package/dist/esm/external-providers/services/external-providers-registry.backend.service.js.map +1 -1
- package/dist/esm/flows-actors/services/agents.backend.service.d.ts +15 -4
- package/dist/esm/flows-actors/services/agents.backend.service.d.ts.map +1 -1
- package/dist/esm/flows-actors/services/agents.backend.service.js +21 -11
- package/dist/esm/flows-actors/services/agents.backend.service.js.map +1 -1
- package/dist/esm/flows-actors/utils/acp-session-runner.backend.utils.d.ts +47 -2
- package/dist/esm/flows-actors/utils/acp-session-runner.backend.utils.d.ts.map +1 -1
- package/dist/esm/flows-actors/utils/acp-session-runner.backend.utils.js +56 -8
- package/dist/esm/flows-actors/utils/acp-session-runner.backend.utils.js.map +1 -1
- package/dist/esm/queue/job-consumer.backend.service.d.ts.map +1 -1
- package/dist/esm/queue/job-consumer.backend.service.js +21 -8
- package/dist/esm/queue/job-consumer.backend.service.js.map +1 -1
- package/dist/esm/queue/job-status-tracking.backend.service.d.ts.map +1 -1
- package/dist/esm/queue/job-status-tracking.backend.service.js +4 -1
- package/dist/esm/queue/job-status-tracking.backend.service.js.map +1 -1
- package/dist/esm/queue/queue.backend.service.d.ts +14 -0
- package/dist/esm/queue/queue.backend.service.d.ts.map +1 -1
- package/dist/esm/queue/queue.backend.service.js +68 -12
- package/dist/esm/queue/queue.backend.service.js.map +1 -1
- package/dist/esm/queue/scheduled-evidence-coverage.backend.d.ts +8 -0
- package/dist/esm/queue/scheduled-evidence-coverage.backend.d.ts.map +1 -1
- package/dist/esm/queue/scheduled-evidence-coverage.backend.js +20 -2
- package/dist/esm/queue/scheduled-evidence-coverage.backend.js.map +1 -1
- package/dist/esm/queue/scheduled-execution.backend.service.d.ts +44 -1
- package/dist/esm/queue/scheduled-execution.backend.service.d.ts.map +1 -1
- package/dist/esm/queue/scheduled-execution.backend.service.js +180 -10
- package/dist/esm/queue/scheduled-execution.backend.service.js.map +1 -1
- package/dist/esm/queue/scheduled-message-verifier.backend.service.d.ts +73 -21
- package/dist/esm/queue/scheduled-message-verifier.backend.service.d.ts.map +1 -1
- package/dist/esm/queue/scheduled-message-verifier.backend.service.js +162 -88
- package/dist/esm/queue/scheduled-message-verifier.backend.service.js.map +1 -1
- package/dist/esm/runtime/services/start-application.backend.service.d.ts.map +1 -1
- package/dist/esm/runtime/services/start-application.backend.service.js +16 -1
- package/dist/esm/runtime/services/start-application.backend.service.js.map +1 -1
- package/package.json +10 -10
- package/dist/esm/__tests__/fixture-deep-partial.d.ts +0 -18
- package/dist/esm/__tests__/fixture-deep-partial.d.ts.map +0 -1
- package/dist/esm/__tests__/fixture-deep-partial.js +0 -1
- package/dist/esm/__tests__/fixture-deep-partial.js.map +0 -1
- package/dist/esm/__tests__/integration/redis-integration-gate.d.ts +0 -26
- package/dist/esm/__tests__/integration/redis-integration-gate.d.ts.map +0 -1
- package/dist/esm/__tests__/integration/redis-integration-gate.js +0 -105
- package/dist/esm/__tests__/integration/redis-integration-gate.js.map +0 -1
- package/dist/esm/__tests__/setup.d.ts +0 -7
- package/dist/esm/__tests__/setup.d.ts.map +0 -1
- package/dist/esm/__tests__/setup.js +0 -14
- package/dist/esm/__tests__/setup.js.map +0 -1
- package/dist/esm/__tests__/test-container.d.ts +0 -41
- package/dist/esm/__tests__/test-container.d.ts.map +0 -1
- package/dist/esm/__tests__/test-container.js +0 -98
- package/dist/esm/__tests__/test-container.js.map +0 -1
- package/dist/esm/base-classes/services/__tests__/mock-resource-operation-helper.service.d.ts +0 -47
- package/dist/esm/base-classes/services/__tests__/mock-resource-operation-helper.service.d.ts.map +0 -1
- package/dist/esm/base-classes/services/__tests__/mock-resource-operation-helper.service.js +0 -82
- package/dist/esm/base-classes/services/__tests__/mock-resource-operation-helper.service.js.map +0 -1
- package/dist/esm/base-classes/services/repositories/__tests__/method-span-scan.guard-helpers.d.ts +0 -57
- package/dist/esm/base-classes/services/repositories/__tests__/method-span-scan.guard-helpers.d.ts.map +0 -1
- package/dist/esm/base-classes/services/repositories/__tests__/method-span-scan.guard-helpers.js +0 -172
- package/dist/esm/base-classes/services/repositories/__tests__/method-span-scan.guard-helpers.js.map +0 -1
- package/dist/esm/base-classes/services/repositories/__tests__/repositories-registry-infrastructure-read.double.d.ts +0 -41
- package/dist/esm/base-classes/services/repositories/__tests__/repositories-registry-infrastructure-read.double.d.ts.map +0 -1
- package/dist/esm/base-classes/services/repositories/__tests__/repositories-registry-infrastructure-read.double.js +0 -54
- package/dist/esm/base-classes/services/repositories/__tests__/repositories-registry-infrastructure-read.double.js.map +0 -1
- package/dist/esm/billing/services/__tests__/subscription-provider-projection.test-double.d.ts +0 -20
- package/dist/esm/billing/services/__tests__/subscription-provider-projection.test-double.d.ts.map +0 -1
- package/dist/esm/billing/services/__tests__/subscription-provider-projection.test-double.js +0 -33
- package/dist/esm/billing/services/__tests__/subscription-provider-projection.test-double.js.map +0 -1
- package/dist/esm/billing/services/custom-impl/__tests__/plan-change-confirmation.fixture.d.ts +0 -22
- package/dist/esm/billing/services/custom-impl/__tests__/plan-change-confirmation.fixture.d.ts.map +0 -1
- package/dist/esm/billing/services/custom-impl/__tests__/plan-change-confirmation.fixture.js +0 -92
- package/dist/esm/billing/services/custom-impl/__tests__/plan-change-confirmation.fixture.js.map +0 -1
- package/dist/esm/billing/services/custom-impl/__tests__/subscription-keyed-mutation.fixture.d.ts +0 -15
- package/dist/esm/billing/services/custom-impl/__tests__/subscription-keyed-mutation.fixture.d.ts.map +0 -1
- package/dist/esm/billing/services/custom-impl/__tests__/subscription-keyed-mutation.fixture.js +0 -61
- package/dist/esm/billing/services/custom-impl/__tests__/subscription-keyed-mutation.fixture.js.map +0 -1
- package/dist/esm/billing/services/webhooks/__tests__/billing-event-authority.test-double.d.ts +0 -21
- package/dist/esm/billing/services/webhooks/__tests__/billing-event-authority.test-double.d.ts.map +0 -1
- package/dist/esm/billing/services/webhooks/__tests__/billing-event-authority.test-double.js +0 -32
- package/dist/esm/billing/services/webhooks/__tests__/billing-event-authority.test-double.js.map +0 -1
- package/dist/esm/billing/services/webhooks/__tests__/settlement-ledger.test-double.d.ts +0 -52
- package/dist/esm/billing/services/webhooks/__tests__/settlement-ledger.test-double.d.ts.map +0 -1
- package/dist/esm/billing/services/webhooks/__tests__/settlement-ledger.test-double.js +0 -74
- package/dist/esm/billing/services/webhooks/__tests__/settlement-ledger.test-double.js.map +0 -1
- package/dist/esm/config/services/__tests__/mock-app-configuration.service.d.ts +0 -60
- package/dist/esm/config/services/__tests__/mock-app-configuration.service.d.ts.map +0 -1
- package/dist/esm/config/services/__tests__/mock-app-configuration.service.js +0 -156
- package/dist/esm/config/services/__tests__/mock-app-configuration.service.js.map +0 -1
- package/dist/esm/config/services/__tests__/startup-validator-resources-registry.double.d.ts +0 -31
- package/dist/esm/config/services/__tests__/startup-validator-resources-registry.double.d.ts.map +0 -1
- package/dist/esm/config/services/__tests__/startup-validator-resources-registry.double.js +0 -47
- package/dist/esm/config/services/__tests__/startup-validator-resources-registry.double.js.map +0 -1
- package/dist/esm/databases/services/__tests__/mock-mongoose-model.service.d.ts +0 -20
- package/dist/esm/databases/services/__tests__/mock-mongoose-model.service.d.ts.map +0 -1
- package/dist/esm/databases/services/__tests__/mock-mongoose-model.service.js +0 -424
- package/dist/esm/databases/services/__tests__/mock-mongoose-model.service.js.map +0 -1
- package/dist/esm/databases/services/__tests__/mock-zod-mongoose-schema-convertor.service.d.ts +0 -19
- package/dist/esm/databases/services/__tests__/mock-zod-mongoose-schema-convertor.service.d.ts.map +0 -1
- package/dist/esm/databases/services/__tests__/mock-zod-mongoose-schema-convertor.service.js +0 -49
- package/dist/esm/databases/services/__tests__/mock-zod-mongoose-schema-convertor.service.js.map +0 -1
- package/dist/esm/flows-actors/services/__tests__/pending-decision.fixtures.d.ts +0 -43
- package/dist/esm/flows-actors/services/__tests__/pending-decision.fixtures.d.ts.map +0 -1
- package/dist/esm/flows-actors/services/__tests__/pending-decision.fixtures.js +0 -61
- package/dist/esm/flows-actors/services/__tests__/pending-decision.fixtures.js.map +0 -1
- package/dist/esm/inbound/services/__tests__/support/in-memory-infrastructure-repositories.d.ts +0 -25
- package/dist/esm/inbound/services/__tests__/support/in-memory-infrastructure-repositories.d.ts.map +0 -1
- package/dist/esm/inbound/services/__tests__/support/in-memory-infrastructure-repositories.js +0 -92
- package/dist/esm/inbound/services/__tests__/support/in-memory-infrastructure-repositories.js.map +0 -1
- package/dist/esm/meta-workflows/services/__tests__/fixtures/meta-workflow-evaluation-treatment.test-fixture.d.ts +0 -7
- package/dist/esm/meta-workflows/services/__tests__/fixtures/meta-workflow-evaluation-treatment.test-fixture.d.ts.map +0 -1
- package/dist/esm/meta-workflows/services/__tests__/fixtures/meta-workflow-evaluation-treatment.test-fixture.js +0 -194
- package/dist/esm/meta-workflows/services/__tests__/fixtures/meta-workflow-evaluation-treatment.test-fixture.js.map +0 -1
- package/dist/esm/monitoring/logger/services/__tests__/mock-logger.service.d.ts +0 -34
- package/dist/esm/monitoring/logger/services/__tests__/mock-logger.service.d.ts.map +0 -1
- package/dist/esm/monitoring/logger/services/__tests__/mock-logger.service.js +0 -59
- package/dist/esm/monitoring/logger/services/__tests__/mock-logger.service.js.map +0 -1
- package/dist/esm/monitoring/observability/services/__tests__/mock-metrics.service.d.ts +0 -48
- package/dist/esm/monitoring/observability/services/__tests__/mock-metrics.service.d.ts.map +0 -1
- package/dist/esm/monitoring/observability/services/__tests__/mock-metrics.service.js +0 -53
- package/dist/esm/monitoring/observability/services/__tests__/mock-metrics.service.js.map +0 -1
- package/dist/esm/notifications/services/__tests__/fixtures/personal-notification-browser/bundle.d.ts +0 -3
- package/dist/esm/notifications/services/__tests__/fixtures/personal-notification-browser/bundle.d.ts.map +0 -1
- package/dist/esm/notifications/services/__tests__/fixtures/personal-notification-browser/bundle.js +0 -19
- package/dist/esm/notifications/services/__tests__/fixtures/personal-notification-browser/bundle.js.map +0 -1
- package/dist/esm/notifications/services/__tests__/fixtures/personal-notification-browser/host.d.ts +0 -51
- package/dist/esm/notifications/services/__tests__/fixtures/personal-notification-browser/host.d.ts.map +0 -1
- package/dist/esm/notifications/services/__tests__/fixtures/personal-notification-browser/host.js +0 -24
- package/dist/esm/notifications/services/__tests__/fixtures/personal-notification-browser/host.js.map +0 -1
- package/dist/esm/notifications/services/__tests__/recipient-language.test-double.d.ts +0 -8
- package/dist/esm/notifications/services/__tests__/recipient-language.test-double.d.ts.map +0 -1
- package/dist/esm/notifications/services/__tests__/recipient-language.test-double.js +0 -9
- package/dist/esm/notifications/services/__tests__/recipient-language.test-double.js.map +0 -1
- package/dist/esm/pdf-templates/__tests__/fixtures/tiny-pdf-template.fixture.d.ts +0 -10
- package/dist/esm/pdf-templates/__tests__/fixtures/tiny-pdf-template.fixture.d.ts.map +0 -1
- package/dist/esm/pdf-templates/__tests__/fixtures/tiny-pdf-template.fixture.js +0 -18
- package/dist/esm/pdf-templates/__tests__/fixtures/tiny-pdf-template.fixture.js.map +0 -1
- package/dist/esm/security/api-keys/__tests__/api-key-usage-redis.double.d.ts +0 -11
- package/dist/esm/security/api-keys/__tests__/api-key-usage-redis.double.d.ts.map +0 -1
- package/dist/esm/security/api-keys/__tests__/api-key-usage-redis.double.js +0 -34
- package/dist/esm/security/api-keys/__tests__/api-key-usage-redis.double.js.map +0 -1
- package/dist/esm/security/authentications/__tests__/registration-onboarding.harness.d.ts +0 -105
- package/dist/esm/security/authentications/__tests__/registration-onboarding.harness.d.ts.map +0 -1
- package/dist/esm/security/authentications/__tests__/registration-onboarding.harness.js +0 -140
- package/dist/esm/security/authentications/__tests__/registration-onboarding.harness.js.map +0 -1
- package/dist/esm/security/authentications/services/__tests__/oauth-provider-audit-spies.mock.d.ts +0 -35
- package/dist/esm/security/authentications/services/__tests__/oauth-provider-audit-spies.mock.d.ts.map +0 -1
- package/dist/esm/security/authentications/services/__tests__/oauth-provider-audit-spies.mock.js +0 -46
- package/dist/esm/security/authentications/services/__tests__/oauth-provider-audit-spies.mock.js.map +0 -1
- package/dist/esm/security/connected-accounts/__tests__/support/real-connection-client-methods.d.ts +0 -2
- package/dist/esm/security/connected-accounts/__tests__/support/real-connection-client-methods.d.ts.map +0 -1
- package/dist/esm/security/connected-accounts/__tests__/support/real-connection-client-methods.js +0 -18
- package/dist/esm/security/connected-accounts/__tests__/support/real-connection-client-methods.js.map +0 -1
- package/dist/esm/systems-of-record/credentials/__tests__/certificate-fixtures.d.ts +0 -20
- package/dist/esm/systems-of-record/credentials/__tests__/certificate-fixtures.d.ts.map +0 -1
- package/dist/esm/systems-of-record/credentials/__tests__/certificate-fixtures.js +0 -56
- package/dist/esm/systems-of-record/credentials/__tests__/certificate-fixtures.js.map +0 -1
- package/dist/tsconfig.build.tsbuildinfo +0 -1
package/dist/esm/base-classes/services/repositories/__tests__/method-span-scan.guard-helpers.d.ts
DELETED
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Method-span source scanner shared by the coverage GUARD tests in this directory:
|
|
3
|
-
*
|
|
4
|
-
* - `repository-retention-chokepoint-coverage.guard.test.ts` — every repository statement is
|
|
5
|
-
* subject to the retention hide;
|
|
6
|
-
* - `rag-chunk-removal-seam-coverage.guard.test.ts` — every destructive seam removes RAG chunks.
|
|
7
|
-
*
|
|
8
|
-
* ONE scanner on purpose (extracted 2026-08-27, when the second guard arrived): the scanner is the
|
|
9
|
-
* part that silently broke twice in the first guard's history — see the defect log in the retention
|
|
10
|
-
* guard's header — so a fix to it must reach every guard, and a per-guard copy is exactly the drift
|
|
11
|
-
* both guards exist to police one layer down. NOT a `.test.ts` file, so importing it registers no
|
|
12
|
-
* duplicate suites in the importer.
|
|
13
|
-
*
|
|
14
|
-
* The per-function reasoning (why spans are delimited by the NEXT signature rather than braces, why
|
|
15
|
-
* `${…}` interpolations stay code, why the waiver window is scoped to a method's OWN comment block)
|
|
16
|
-
* lives on the functions below, moved verbatim from the retention guard.
|
|
17
|
-
*/
|
|
18
|
-
/**
|
|
19
|
-
* Strip comments and blank string CONTENTS, while keeping `${…}` interpolations as code.
|
|
20
|
-
*
|
|
21
|
-
* Blanking string bodies stops a literal like `'insertInto'` in a log message from reading as a
|
|
22
|
-
* database statement. Keeping interpolations is what the retention guard's defect (2) was: a `sql`
|
|
23
|
-
* tagged template is a string whose interpolations carry the real calls. Newlines are preserved
|
|
24
|
-
* throughout so reported line numbers stay truthful.
|
|
25
|
-
*/
|
|
26
|
-
export declare function toScannableCode(source: string): string;
|
|
27
|
-
export interface MethodSpan {
|
|
28
|
-
readonly name: string;
|
|
29
|
-
readonly line: number;
|
|
30
|
-
readonly endLine: number;
|
|
31
|
-
readonly code: string;
|
|
32
|
-
}
|
|
33
|
-
/**
|
|
34
|
-
* Every class member, each spanning from its own signature to the NEXT one.
|
|
35
|
-
*
|
|
36
|
-
* Deliberately NOT brace counting — see defect (1) in the retention guard's header. A span may
|
|
37
|
-
* therefore run a little past a method's closing brace into the next method's JSDoc (already
|
|
38
|
-
* stripped), which cannot mask a violation: the following method's own span still starts at its
|
|
39
|
-
* signature.
|
|
40
|
-
*/
|
|
41
|
-
export declare function collectMethodSpans(source: string): MethodSpan[];
|
|
42
|
-
/**
|
|
43
|
-
* The text a `<rule>: N/A` waiver must appear in to count for THIS method: its own leading comment
|
|
44
|
-
* block, plus its own body.
|
|
45
|
-
*
|
|
46
|
-
* Scoping this precisely matters more than it looks. Because spans run signature → NEXT signature
|
|
47
|
-
* (the F1 fix), a naive window over the span's raw lines swallows the *following* method's JSDoc —
|
|
48
|
-
* so a waiver on M+1 silently exempts M. That was proven live in the retention guard: deleting
|
|
49
|
-
* `_doCreate`'s own waiver still left it unreported, because `_doCreateMany`'s waiver sat inside
|
|
50
|
-
* `_doCreate`'s span. The evasion was CREATED by the fix for the previous evasion, which is why the
|
|
51
|
-
* removal case is pinned in the retention guard rather than trusted.
|
|
52
|
-
*
|
|
53
|
-
* The leading block is found by walking UP from the signature over contiguous comment/decorator/blank
|
|
54
|
-
* lines and stopping at the first line of real code — the end of the previous method.
|
|
55
|
-
*/
|
|
56
|
-
export declare function waiverScopeFor(rawLines: readonly string[], span: MethodSpan): string;
|
|
57
|
-
//# sourceMappingURL=method-span-scan.guard-helpers.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"method-span-scan.guard-helpers.d.ts","sourceRoot":"","sources":["../../../../../../../src/base-classes/services/repositories/__tests__/method-span-scan.guard-helpers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAeH;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAuDtD;AAED,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACvB;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,UAAU,EAAE,CAoB/D;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,EAAE,IAAI,EAAE,UAAU,GAAG,MAAM,CA4BpF"}
|
package/dist/esm/base-classes/services/repositories/__tests__/method-span-scan.guard-helpers.js
DELETED
|
@@ -1,172 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Method-span source scanner shared by the coverage GUARD tests in this directory:
|
|
3
|
-
*
|
|
4
|
-
* - `repository-retention-chokepoint-coverage.guard.test.ts` — every repository statement is
|
|
5
|
-
* subject to the retention hide;
|
|
6
|
-
* - `rag-chunk-removal-seam-coverage.guard.test.ts` — every destructive seam removes RAG chunks.
|
|
7
|
-
*
|
|
8
|
-
* ONE scanner on purpose (extracted 2026-08-27, when the second guard arrived): the scanner is the
|
|
9
|
-
* part that silently broke twice in the first guard's history — see the defect log in the retention
|
|
10
|
-
* guard's header — so a fix to it must reach every guard, and a per-guard copy is exactly the drift
|
|
11
|
-
* both guards exist to police one layer down. NOT a `.test.ts` file, so importing it registers no
|
|
12
|
-
* duplicate suites in the importer.
|
|
13
|
-
*
|
|
14
|
-
* The per-function reasoning (why spans are delimited by the NEXT signature rather than braces, why
|
|
15
|
-
* `${…}` interpolations stay code, why the waiver window is scoped to a method's OWN comment block)
|
|
16
|
-
* lives on the functions below, moved verbatim from the retention guard.
|
|
17
|
-
*/
|
|
18
|
-
/**
|
|
19
|
-
* A class member at its declaration indentation.
|
|
20
|
-
*
|
|
21
|
-
* ASSUMPTION worth naming: a MODULE-scope `function` is not indented two spaces, so it does not start
|
|
22
|
-
* a span and its body falls into a neighbouring one — where it could inherit that neighbour's hide.
|
|
23
|
-
* No such site exists in either repository today (the shared-quoting extraction removed the last
|
|
24
|
-
* one). If one is added and it issues a database statement, extend this matcher.
|
|
25
|
-
*/
|
|
26
|
-
const METHOD_SIGNATURE = /^ {2}(?:(?:public|private|protected)\s+)?(?:static\s+)?(?:async\s+)?(?:get\s+|set\s+)?([A-Za-z_]\w*)\s*(?:<[^>]*>)?\s*\(/gm;
|
|
27
|
-
/** `if (`, `for (`, … also match the signature shape at two-space indentation. */
|
|
28
|
-
const CONTROL_FLOW_KEYWORDS = new Set(['if', 'for', 'while', 'switch', 'catch', 'return', 'do', 'else']);
|
|
29
|
-
/**
|
|
30
|
-
* Strip comments and blank string CONTENTS, while keeping `${…}` interpolations as code.
|
|
31
|
-
*
|
|
32
|
-
* Blanking string bodies stops a literal like `'insertInto'` in a log message from reading as a
|
|
33
|
-
* database statement. Keeping interpolations is what the retention guard's defect (2) was: a `sql`
|
|
34
|
-
* tagged template is a string whose interpolations carry the real calls. Newlines are preserved
|
|
35
|
-
* throughout so reported line numbers stay truthful.
|
|
36
|
-
*/
|
|
37
|
-
export function toScannableCode(source) {
|
|
38
|
-
let output = '';
|
|
39
|
-
let index = 0;
|
|
40
|
-
let quote;
|
|
41
|
-
/** Quote characters suspended while inside a `${…}` interpolation, innermost last. */
|
|
42
|
-
const suspendedQuotes = [];
|
|
43
|
-
while (index < source.length) {
|
|
44
|
-
const character = source[index];
|
|
45
|
-
const next = source[index + 1];
|
|
46
|
-
if (quote) {
|
|
47
|
-
if (character === '\\') {
|
|
48
|
-
output += ' ';
|
|
49
|
-
index += 2;
|
|
50
|
-
continue;
|
|
51
|
-
}
|
|
52
|
-
if (quote === '`' && character === '$' && next === '{') {
|
|
53
|
-
suspendedQuotes.push(quote);
|
|
54
|
-
quote = undefined;
|
|
55
|
-
output += ' {';
|
|
56
|
-
index += 2;
|
|
57
|
-
continue;
|
|
58
|
-
}
|
|
59
|
-
if (character === quote) {
|
|
60
|
-
quote = undefined;
|
|
61
|
-
output += character;
|
|
62
|
-
index += 1;
|
|
63
|
-
continue;
|
|
64
|
-
}
|
|
65
|
-
output += character === '\n' ? '\n' : ' ';
|
|
66
|
-
index += 1;
|
|
67
|
-
continue;
|
|
68
|
-
}
|
|
69
|
-
if (suspendedQuotes.length > 0 && character === '}') {
|
|
70
|
-
quote = suspendedQuotes.pop();
|
|
71
|
-
output += '} ';
|
|
72
|
-
index += 1;
|
|
73
|
-
continue;
|
|
74
|
-
}
|
|
75
|
-
if (character === '\'' || character === '"' || character === '`') {
|
|
76
|
-
quote = character;
|
|
77
|
-
output += character;
|
|
78
|
-
index += 1;
|
|
79
|
-
continue;
|
|
80
|
-
}
|
|
81
|
-
if (character === '/' && next === '/') {
|
|
82
|
-
while (index < source.length && source[index] !== '\n')
|
|
83
|
-
index += 1;
|
|
84
|
-
continue;
|
|
85
|
-
}
|
|
86
|
-
if (character === '/' && next === '*') {
|
|
87
|
-
index += 2;
|
|
88
|
-
while (index < source.length && !(source[index] === '*' && source[index + 1] === '/')) {
|
|
89
|
-
if (source[index] === '\n')
|
|
90
|
-
output += '\n';
|
|
91
|
-
index += 1;
|
|
92
|
-
}
|
|
93
|
-
index += 2;
|
|
94
|
-
continue;
|
|
95
|
-
}
|
|
96
|
-
output += character;
|
|
97
|
-
index += 1;
|
|
98
|
-
}
|
|
99
|
-
return output;
|
|
100
|
-
}
|
|
101
|
-
/**
|
|
102
|
-
* Every class member, each spanning from its own signature to the NEXT one.
|
|
103
|
-
*
|
|
104
|
-
* Deliberately NOT brace counting — see defect (1) in the retention guard's header. A span may
|
|
105
|
-
* therefore run a little past a method's closing brace into the next method's JSDoc (already
|
|
106
|
-
* stripped), which cannot mask a violation: the following method's own span still starts at its
|
|
107
|
-
* signature.
|
|
108
|
-
*/
|
|
109
|
-
export function collectMethodSpans(source) {
|
|
110
|
-
const code = toScannableCode(source);
|
|
111
|
-
const starts = [...code.matchAll(METHOD_SIGNATURE)]
|
|
112
|
-
.filter((match) => !CONTROL_FLOW_KEYWORDS.has(match[1]))
|
|
113
|
-
.map((match) => ({ name: match[1], index: match.index }));
|
|
114
|
-
return starts.map((start, position) => {
|
|
115
|
-
const end = position + 1 < starts.length ? starts[position + 1].index : code.length;
|
|
116
|
-
const line = code.slice(0, start.index).split('\n').length;
|
|
117
|
-
const body = code.slice(start.index, end);
|
|
118
|
-
/**
|
|
119
|
-
* A signature match starts at its LINE start, so `body` ends with the newline that begins the
|
|
120
|
-
* next signature's line. Counting `split('\n')` on it therefore yields one line too many and
|
|
121
|
-
* `endLine` would point AT the next signature — which silently defeated the waiver-window trim
|
|
122
|
-
* (it hit real code and stopped before removing the neighbour's comment). Drop the trailing
|
|
123
|
-
* newline before counting so `endLine` is the last line genuinely inside this span.
|
|
124
|
-
*/
|
|
125
|
-
const lineCount = body.replace(/\n$/, '').split('\n').length;
|
|
126
|
-
return { name: start.name, line, endLine: line + lineCount - 1, code: body };
|
|
127
|
-
});
|
|
128
|
-
}
|
|
129
|
-
/**
|
|
130
|
-
* The text a `<rule>: N/A` waiver must appear in to count for THIS method: its own leading comment
|
|
131
|
-
* block, plus its own body.
|
|
132
|
-
*
|
|
133
|
-
* Scoping this precisely matters more than it looks. Because spans run signature → NEXT signature
|
|
134
|
-
* (the F1 fix), a naive window over the span's raw lines swallows the *following* method's JSDoc —
|
|
135
|
-
* so a waiver on M+1 silently exempts M. That was proven live in the retention guard: deleting
|
|
136
|
-
* `_doCreate`'s own waiver still left it unreported, because `_doCreateMany`'s waiver sat inside
|
|
137
|
-
* `_doCreate`'s span. The evasion was CREATED by the fix for the previous evasion, which is why the
|
|
138
|
-
* removal case is pinned in the retention guard rather than trusted.
|
|
139
|
-
*
|
|
140
|
-
* The leading block is found by walking UP from the signature over contiguous comment/decorator/blank
|
|
141
|
-
* lines and stopping at the first line of real code — the end of the previous method.
|
|
142
|
-
*/
|
|
143
|
-
export function waiverScopeFor(rawLines, span) {
|
|
144
|
-
let firstLine = span.line - 1; // 0-based index of the signature line
|
|
145
|
-
while (firstLine > 0) {
|
|
146
|
-
const previous = rawLines[firstLine - 1].trim();
|
|
147
|
-
const isCommentOrBlank = previous === ''
|
|
148
|
-
|| previous.startsWith('//')
|
|
149
|
-
|| previous.startsWith('/*')
|
|
150
|
-
|| previous.startsWith('*')
|
|
151
|
-
|| previous.startsWith('@');
|
|
152
|
-
if (!isCommentOrBlank)
|
|
153
|
-
break;
|
|
154
|
-
firstLine -= 1;
|
|
155
|
-
}
|
|
156
|
-
/** The body ends where the NEXT method's leading comment block begins, never after it. */
|
|
157
|
-
let lastLine = span.endLine;
|
|
158
|
-
while (lastLine > span.line) {
|
|
159
|
-
const candidate = rawLines[lastLine - 1].trim();
|
|
160
|
-
const isCommentOrBlank = candidate === ''
|
|
161
|
-
|| candidate.startsWith('//')
|
|
162
|
-
|| candidate.startsWith('/*')
|
|
163
|
-
|| candidate.startsWith('*')
|
|
164
|
-
|| candidate.startsWith('*/')
|
|
165
|
-
|| candidate.startsWith('@');
|
|
166
|
-
if (!isCommentOrBlank)
|
|
167
|
-
break;
|
|
168
|
-
lastLine -= 1;
|
|
169
|
-
}
|
|
170
|
-
return rawLines.slice(firstLine, lastLine).join('\n');
|
|
171
|
-
}
|
|
172
|
-
//# sourceMappingURL=method-span-scan.guard-helpers.js.map
|
package/dist/esm/base-classes/services/repositories/__tests__/method-span-scan.guard-helpers.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"method-span-scan.guard-helpers.js","sourceRoot":"","sources":["../../../../../../../src/base-classes/services/repositories/__tests__/method-span-scan.guard-helpers.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Method-span source scanner shared by the coverage GUARD tests in this directory:\n *\n * - `repository-retention-chokepoint-coverage.guard.test.ts` — every repository statement is\n * subject to the retention hide;\n * - `rag-chunk-removal-seam-coverage.guard.test.ts` — every destructive seam removes RAG chunks.\n *\n * ONE scanner on purpose (extracted 2026-08-27, when the second guard arrived): the scanner is the\n * part that silently broke twice in the first guard's history — see the defect log in the retention\n * guard's header — so a fix to it must reach every guard, and a per-guard copy is exactly the drift\n * both guards exist to police one layer down. NOT a `.test.ts` file, so importing it registers no\n * duplicate suites in the importer.\n *\n * The per-function reasoning (why spans are delimited by the NEXT signature rather than braces, why\n * `${…}` interpolations stay code, why the waiver window is scoped to a method's OWN comment block)\n * lives on the functions below, moved verbatim from the retention guard.\n */\n\n/**\n * A class member at its declaration indentation.\n *\n * ASSUMPTION worth naming: a MODULE-scope `function` is not indented two spaces, so it does not start\n * a span and its body falls into a neighbouring one — where it could inherit that neighbour's hide.\n * No such site exists in either repository today (the shared-quoting extraction removed the last\n * one). If one is added and it issues a database statement, extend this matcher.\n */\nconst METHOD_SIGNATURE = /^ {2}(?:(?:public|private|protected)\\s+)?(?:static\\s+)?(?:async\\s+)?(?:get\\s+|set\\s+)?([A-Za-z_]\\w*)\\s*(?:<[^>]*>)?\\s*\\(/gm;\n\n/** `if (`, `for (`, … also match the signature shape at two-space indentation. */\nconst CONTROL_FLOW_KEYWORDS = new Set(['if', 'for', 'while', 'switch', 'catch', 'return', 'do', 'else']);\n\n/**\n * Strip comments and blank string CONTENTS, while keeping `${…}` interpolations as code.\n *\n * Blanking string bodies stops a literal like `'insertInto'` in a log message from reading as a\n * database statement. Keeping interpolations is what the retention guard's defect (2) was: a `sql`\n * tagged template is a string whose interpolations carry the real calls. Newlines are preserved\n * throughout so reported line numbers stay truthful.\n */\nexport function toScannableCode(source: string): string {\n let output = '';\n let index = 0;\n let quote: string | undefined;\n /** Quote characters suspended while inside a `${…}` interpolation, innermost last. */\n const suspendedQuotes: string[] = [];\n\n while (index < source.length) {\n const character = source[index]!;\n const next = source[index + 1];\n\n if (quote) {\n if (character === '\\\\') { output += ' '; index += 2; continue; }\n if (quote === '`' && character === '$' && next === '{') {\n suspendedQuotes.push(quote);\n quote = undefined;\n output += ' {';\n index += 2;\n continue;\n }\n if (character === quote) { quote = undefined; output += character; index += 1; continue; }\n output += character === '\\n' ? '\\n' : ' ';\n index += 1;\n continue;\n }\n\n if (suspendedQuotes.length > 0 && character === '}') {\n quote = suspendedQuotes.pop();\n output += '} ';\n index += 1;\n continue;\n }\n if (character === '\\'' || character === '\"' || character === '`') {\n quote = character;\n output += character;\n index += 1;\n continue;\n }\n if (character === '/' && next === '/') {\n while (index < source.length && source[index] !== '\\n') index += 1;\n continue;\n }\n if (character === '/' && next === '*') {\n index += 2;\n while (index < source.length && !(source[index] === '*' && source[index + 1] === '/')) {\n if (source[index] === '\\n') output += '\\n';\n index += 1;\n }\n index += 2;\n continue;\n }\n output += character;\n index += 1;\n }\n return output;\n}\n\nexport interface MethodSpan {\n readonly name: string;\n readonly line: number;\n readonly endLine: number;\n readonly code: string;\n}\n\n/**\n * Every class member, each spanning from its own signature to the NEXT one.\n *\n * Deliberately NOT brace counting — see defect (1) in the retention guard's header. A span may\n * therefore run a little past a method's closing brace into the next method's JSDoc (already\n * stripped), which cannot mask a violation: the following method's own span still starts at its\n * signature.\n */\nexport function collectMethodSpans(source: string): MethodSpan[] {\n const code = toScannableCode(source);\n const starts = [...code.matchAll(METHOD_SIGNATURE)]\n .filter((match) => !CONTROL_FLOW_KEYWORDS.has(match[1]!))\n .map((match) => ({ name: match[1]!, index: match.index! }));\n\n return starts.map((start, position) => {\n const end = position + 1 < starts.length ? starts[position + 1]!.index : code.length;\n const line = code.slice(0, start.index).split('\\n').length;\n const body = code.slice(start.index, end);\n /**\n * A signature match starts at its LINE start, so `body` ends with the newline that begins the\n * next signature's line. Counting `split('\\n')` on it therefore yields one line too many and\n * `endLine` would point AT the next signature — which silently defeated the waiver-window trim\n * (it hit real code and stopped before removing the neighbour's comment). Drop the trailing\n * newline before counting so `endLine` is the last line genuinely inside this span.\n */\n const lineCount = body.replace(/\\n$/, '').split('\\n').length;\n return { name: start.name, line, endLine: line + lineCount - 1, code: body };\n });\n}\n\n/**\n * The text a `<rule>: N/A` waiver must appear in to count for THIS method: its own leading comment\n * block, plus its own body.\n *\n * Scoping this precisely matters more than it looks. Because spans run signature → NEXT signature\n * (the F1 fix), a naive window over the span's raw lines swallows the *following* method's JSDoc —\n * so a waiver on M+1 silently exempts M. That was proven live in the retention guard: deleting\n * `_doCreate`'s own waiver still left it unreported, because `_doCreateMany`'s waiver sat inside\n * `_doCreate`'s span. The evasion was CREATED by the fix for the previous evasion, which is why the\n * removal case is pinned in the retention guard rather than trusted.\n *\n * The leading block is found by walking UP from the signature over contiguous comment/decorator/blank\n * lines and stopping at the first line of real code — the end of the previous method.\n */\nexport function waiverScopeFor(rawLines: readonly string[], span: MethodSpan): string {\n let firstLine = span.line - 1; // 0-based index of the signature line\n while (firstLine > 0) {\n const previous = rawLines[firstLine - 1]!.trim();\n const isCommentOrBlank = previous === ''\n || previous.startsWith('//')\n || previous.startsWith('/*')\n || previous.startsWith('*')\n || previous.startsWith('@');\n if (!isCommentOrBlank) break;\n firstLine -= 1;\n }\n\n /** The body ends where the NEXT method's leading comment block begins, never after it. */\n let lastLine = span.endLine;\n while (lastLine > span.line) {\n const candidate = rawLines[lastLine - 1]!.trim();\n const isCommentOrBlank = candidate === ''\n || candidate.startsWith('//')\n || candidate.startsWith('/*')\n || candidate.startsWith('*')\n || candidate.startsWith('*/')\n || candidate.startsWith('@');\n if (!isCommentOrBlank) break;\n lastLine -= 1;\n }\n\n return rawLines.slice(firstLine, lastLine).join('\\n');\n}\n"]}
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* A stand-in for the two repositories-registry methods every service-layer destruction of rows now reaches
|
|
3
|
-
* before it writes (#1634, #1896): `hasRepository` and the infrastructure-tier `findManyForInfrastructure`.
|
|
4
|
-
*
|
|
5
|
-
* WHY THIS EXISTS. The legal-hold check reads the ACTIVE holds (`LEGAL_HOLDS`) and, at the scrub chokepoint,
|
|
6
|
-
* the rows being scrubbed — both through `findManyForInfrastructure`. A registry double without that method
|
|
7
|
-
* throws a `TypeError` there, the handler's fail-closed catch turns it into `LEGAL_HOLD_STATE_UNVERIFIABLE`,
|
|
8
|
-
* and every cascade or scrub test refuses before it reaches the seam it pins. Four handler suites went red
|
|
9
|
-
* that way at once. Stubbing the method to `[]` in each would have hidden the next such read the same way; a
|
|
10
|
-
* double that MODELS the real door fails loudly instead when a caller reads something it cannot answer.
|
|
11
|
-
*
|
|
12
|
-
* WHAT IT MODELS, from `RepositoriesRegistryHandlerBackendService`:
|
|
13
|
-
* - `hasRepository(type)` is true exactly for the registered tables. `LEGAL_HOLDS` is always registered,
|
|
14
|
-
* because every application backend registers it (a core resource); a fixture that wants the "no hold
|
|
15
|
-
* table" answer of a platform runtime passes `registerLegalHolds: false`.
|
|
16
|
-
* - `findManyForInfrastructure(type, filter)` throws for an unregistered type (the real door goes through
|
|
17
|
-
* `getRepositoryOrThrow`), and otherwise returns the table's rows matching the raw filter.
|
|
18
|
-
* - The filter matcher answers the shapes the real callers send — field equality, `$in`, `$or` — comparing
|
|
19
|
-
* ids as strings, as the legal-hold predicate does. Any other operator THROWS, and so does a bounded
|
|
20
|
-
* (sorted, limited) read: a double that silently ignored either would answer a question nobody asked.
|
|
21
|
-
*
|
|
22
|
-
* WHAT IT DOES NOT MODEL: the retention hide. The real door hides RETAINED rows; seed a table with the rows
|
|
23
|
-
* as that door would return them.
|
|
24
|
-
*/
|
|
25
|
-
export interface RepositoriesRegistryInfrastructureReadDoubleOptions {
|
|
26
|
-
/** Every registered resource type's rows, as the infrastructure door would return them. */
|
|
27
|
-
readonly tables?: Readonly<Record<string, ReadonlyArray<Record<string, unknown>>>>;
|
|
28
|
-
/** Active and released hold rows. Ignored when `registerLegalHolds` is false. */
|
|
29
|
-
readonly legalHolds?: ReadonlyArray<Record<string, unknown>>;
|
|
30
|
-
/** `false` models a runtime with no hold table (a platform service). Default `true`. */
|
|
31
|
-
readonly registerLegalHolds?: boolean;
|
|
32
|
-
/** When set, every read throws it — the storage outage the fail-closed catch exists for. */
|
|
33
|
-
readonly readFailure?: Error;
|
|
34
|
-
}
|
|
35
|
-
export declare function createRepositoriesRegistryInfrastructureReadDouble(options?: RepositoriesRegistryInfrastructureReadDoubleOptions): {
|
|
36
|
-
hasRepository: import("vitest").Mock<[resourceType: string], boolean>;
|
|
37
|
-
findManyForInfrastructure: import("vitest").Mock<[resourceType: string, filter: Record<string, unknown>, _executionContext?: unknown, _options?: unknown, bounds?: unknown], Promise<{
|
|
38
|
-
[x: string]: unknown;
|
|
39
|
-
}[]>>;
|
|
40
|
-
};
|
|
41
|
-
//# sourceMappingURL=repositories-registry-infrastructure-read.double.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"repositories-registry-infrastructure-read.double.d.ts","sourceRoot":"","sources":["../../../../../../../src/base-classes/services/repositories/__tests__/repositories-registry-infrastructure-read.double.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,WAAW,mDAAmD;IAClE,2FAA2F;IAC3F,QAAQ,CAAC,MAAM,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;IACnF,iFAAiF;IACjF,QAAQ,CAAC,UAAU,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IAC7D,wFAAwF;IACxF,QAAQ,CAAC,kBAAkB,CAAC,EAAE,OAAO,CAAC;IACtC,4FAA4F;IAC5F,QAAQ,CAAC,WAAW,CAAC,EAAE,KAAK,CAAC;CAC9B;AAED,wBAAgB,kDAAkD,CAAC,OAAO,GAAE,mDAAwD;;;;;EAgBnI"}
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
import { vi } from 'vitest';
|
|
2
|
-
import { CoreResourceType } from '@wildo-ai/saas-models';
|
|
3
|
-
export function createRepositoriesRegistryInfrastructureReadDouble(options = {}) {
|
|
4
|
-
const tables = new Map(Object.entries(options.tables ?? {}));
|
|
5
|
-
if (options.registerLegalHolds !== false) {
|
|
6
|
-
tables.set(CoreResourceType.LEGAL_HOLDS, options.legalHolds ?? tables.get(CoreResourceType.LEGAL_HOLDS) ?? []);
|
|
7
|
-
}
|
|
8
|
-
return {
|
|
9
|
-
hasRepository: vi.fn((resourceType) => tables.has(resourceType)),
|
|
10
|
-
findManyForInfrastructure: vi.fn(async (resourceType, filter, _executionContext, _options, bounds) => {
|
|
11
|
-
if (options.readFailure)
|
|
12
|
-
throw options.readFailure;
|
|
13
|
-
// The real door orders and limits a bounded scan; this double does not, so a bounded read is refused rather than answered wrongly.
|
|
14
|
-
if (bounds !== undefined)
|
|
15
|
-
throw new Error('Unmodelled bounded infrastructure read (sort/limit) in the repositories-registry double');
|
|
16
|
-
const rows = tables.get(resourceType);
|
|
17
|
-
if (!rows)
|
|
18
|
-
throw new Error(`No repository registered for resource type '${resourceType}'`);
|
|
19
|
-
return rows.filter((row) => matchesRawFilter(row, filter)).map((row) => ({ ...row }));
|
|
20
|
-
}),
|
|
21
|
-
};
|
|
22
|
-
}
|
|
23
|
-
function matchesRawFilter(row, filter) {
|
|
24
|
-
return Object.entries(filter).every(([key, condition]) => {
|
|
25
|
-
if (key === '$or') {
|
|
26
|
-
if (!Array.isArray(condition))
|
|
27
|
-
throw new Error('$or expects an array of filters');
|
|
28
|
-
return condition.some((clause) => matchesRawFilter(row, clause));
|
|
29
|
-
}
|
|
30
|
-
if (key.startsWith('$'))
|
|
31
|
-
throw new Error(`Unmodelled top-level operator '${key}' in an infrastructure read filter`);
|
|
32
|
-
return matchesCondition(row[key], condition);
|
|
33
|
-
});
|
|
34
|
-
}
|
|
35
|
-
function matchesCondition(value, condition) {
|
|
36
|
-
if (condition !== null && typeof condition === 'object' && !Array.isArray(condition) && Object.keys(condition).some((k) => k.startsWith('$'))) {
|
|
37
|
-
return Object.entries(condition).every(([operator, operand]) => {
|
|
38
|
-
if (operator === '$in') {
|
|
39
|
-
if (!Array.isArray(operand))
|
|
40
|
-
throw new Error('$in expects an array');
|
|
41
|
-
return operand.some((candidate) => sameValue(value, candidate));
|
|
42
|
-
}
|
|
43
|
-
throw new Error(`Unmodelled operator '${operator}' in an infrastructure read filter`);
|
|
44
|
-
});
|
|
45
|
-
}
|
|
46
|
-
return sameValue(value, condition);
|
|
47
|
-
}
|
|
48
|
-
/** Ids compare as strings: an ObjectId on MongoDB and a string on PostgreSQL name the same row. */
|
|
49
|
-
function sameValue(left, right) {
|
|
50
|
-
if (left === undefined || left === null || right === undefined || right === null)
|
|
51
|
-
return left === right;
|
|
52
|
-
return String(left) === String(right);
|
|
53
|
-
}
|
|
54
|
-
//# sourceMappingURL=repositories-registry-infrastructure-read.double.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"repositories-registry-infrastructure-read.double.js","sourceRoot":"","sources":["../../../../../../../src/base-classes/services/repositories/__tests__/repositories-registry-infrastructure-read.double.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAC5B,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAqCzD,MAAM,UAAU,kDAAkD,CAAC,UAA+D,EAAE;IAClI,MAAM,MAAM,GAAG,IAAI,GAAG,CAAiD,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,CAAC;IAC7G,IAAI,OAAO,CAAC,kBAAkB,KAAK,KAAK,EAAE,CAAC;QACzC,MAAM,CAAC,GAAG,CAAC,gBAAgB,CAAC,WAAW,EAAE,OAAO,CAAC,UAAU,IAAI,MAAM,CAAC,GAAG,CAAC,gBAAgB,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC,CAAC;IACjH,CAAC;IACD,OAAO;QACL,aAAa,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC,YAAoB,EAAE,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;QACxE,yBAAyB,EAAE,EAAE,CAAC,EAAE,CAAC,KAAK,EAAE,YAAoB,EAAE,MAA+B,EAAE,iBAA2B,EAAE,QAAkB,EAAE,MAAgB,EAAE,EAAE;YAClK,IAAI,OAAO,CAAC,WAAW;gBAAE,MAAM,OAAO,CAAC,WAAW,CAAC;YACnD,mIAAmI;YACnI,IAAI,MAAM,KAAK,SAAS;gBAAE,MAAM,IAAI,KAAK,CAAC,yFAAyF,CAAC,CAAC;YACrI,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;YACtC,IAAI,CAAC,IAAI;gBAAE,MAAM,IAAI,KAAK,CAAC,+CAA+C,YAAY,GAAG,CAAC,CAAC;YAC3F,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,gBAAgB,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,GAAG,EAAE,CAAC,CAAC,CAAC;QACxF,CAAC,CAAC;KACH,CAAC;AACJ,CAAC;AAED,SAAS,gBAAgB,CAAC,GAA4B,EAAE,MAA+B;IACrF,OAAO,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,EAAE,SAAS,CAAC,EAAE,EAAE;QACvD,IAAI,GAAG,KAAK,KAAK,EAAE,CAAC;YAClB,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC;gBAAE,MAAM,IAAI,KAAK,CAAC,iCAAiC,CAAC,CAAC;YAClF,OAAO,SAAS,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,gBAAgB,CAAC,GAAG,EAAE,MAAiC,CAAC,CAAC,CAAC;QAC9F,CAAC;QACD,IAAI,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC;YAAE,MAAM,IAAI,KAAK,CAAC,kCAAkC,GAAG,oCAAoC,CAAC,CAAC;QACpH,OAAO,gBAAgB,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,SAAS,CAAC,CAAC;IAC/C,CAAC,CAAC,CAAC;AACL,CAAC;AAED,SAAS,gBAAgB,CAAC,KAAc,EAAE,SAAkB;IAC1D,IAAI,SAAS,KAAK,IAAI,IAAI,OAAO,SAAS,KAAK,QAAQ,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,IAAI,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;QAC9I,OAAO,MAAM,CAAC,OAAO,CAAC,SAAoC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,QAAQ,EAAE,OAAO,CAAC,EAAE,EAAE;YACxF,IAAI,QAAQ,KAAK,KAAK,EAAE,CAAC;gBACvB,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC;oBAAE,MAAM,IAAI,KAAK,CAAC,sBAAsB,CAAC,CAAC;gBACrE,OAAO,OAAO,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC,CAAC;YAClE,CAAC;YACD,MAAM,IAAI,KAAK,CAAC,wBAAwB,QAAQ,oCAAoC,CAAC,CAAC;QACxF,CAAC,CAAC,CAAC;IACL,CAAC;IACD,OAAO,SAAS,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC;AACrC,CAAC;AAED,mGAAmG;AACnG,SAAS,SAAS,CAAC,IAAa,EAAE,KAAc;IAC9C,IAAI,IAAI,KAAK,SAAS,IAAI,IAAI,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,IAAI,KAAK,KAAK,CAAC;IACxG,OAAO,MAAM,CAAC,IAAI,CAAC,KAAK,MAAM,CAAC,KAAK,CAAC,CAAC;AACxC,CAAC","sourcesContent":["import { vi } from 'vitest';\nimport { CoreResourceType } from '@wildo-ai/saas-models';\n\n/**\n * A stand-in for the two repositories-registry methods every service-layer destruction of rows now reaches\n * before it writes (#1634, #1896): `hasRepository` and the infrastructure-tier `findManyForInfrastructure`.\n *\n * WHY THIS EXISTS. The legal-hold check reads the ACTIVE holds (`LEGAL_HOLDS`) and, at the scrub chokepoint,\n * the rows being scrubbed — both through `findManyForInfrastructure`. A registry double without that method\n * throws a `TypeError` there, the handler's fail-closed catch turns it into `LEGAL_HOLD_STATE_UNVERIFIABLE`,\n * and every cascade or scrub test refuses before it reaches the seam it pins. Four handler suites went red\n * that way at once. Stubbing the method to `[]` in each would have hidden the next such read the same way; a\n * double that MODELS the real door fails loudly instead when a caller reads something it cannot answer.\n *\n * WHAT IT MODELS, from `RepositoriesRegistryHandlerBackendService`:\n * - `hasRepository(type)` is true exactly for the registered tables. `LEGAL_HOLDS` is always registered,\n * because every application backend registers it (a core resource); a fixture that wants the \"no hold\n * table\" answer of a platform runtime passes `registerLegalHolds: false`.\n * - `findManyForInfrastructure(type, filter)` throws for an unregistered type (the real door goes through\n * `getRepositoryOrThrow`), and otherwise returns the table's rows matching the raw filter.\n * - The filter matcher answers the shapes the real callers send — field equality, `$in`, `$or` — comparing\n * ids as strings, as the legal-hold predicate does. Any other operator THROWS, and so does a bounded\n * (sorted, limited) read: a double that silently ignored either would answer a question nobody asked.\n *\n * WHAT IT DOES NOT MODEL: the retention hide. The real door hides RETAINED rows; seed a table with the rows\n * as that door would return them.\n */\nexport interface RepositoriesRegistryInfrastructureReadDoubleOptions {\n /** Every registered resource type's rows, as the infrastructure door would return them. */\n readonly tables?: Readonly<Record<string, ReadonlyArray<Record<string, unknown>>>>;\n /** Active and released hold rows. Ignored when `registerLegalHolds` is false. */\n readonly legalHolds?: ReadonlyArray<Record<string, unknown>>;\n /** `false` models a runtime with no hold table (a platform service). Default `true`. */\n readonly registerLegalHolds?: boolean;\n /** When set, every read throws it — the storage outage the fail-closed catch exists for. */\n readonly readFailure?: Error;\n}\n\nexport function createRepositoriesRegistryInfrastructureReadDouble(options: RepositoriesRegistryInfrastructureReadDoubleOptions = {}) {\n const tables = new Map<string, ReadonlyArray<Record<string, unknown>>>(Object.entries(options.tables ?? {}));\n if (options.registerLegalHolds !== false) {\n tables.set(CoreResourceType.LEGAL_HOLDS, options.legalHolds ?? tables.get(CoreResourceType.LEGAL_HOLDS) ?? []);\n }\n return {\n hasRepository: vi.fn((resourceType: string) => tables.has(resourceType)),\n findManyForInfrastructure: vi.fn(async (resourceType: string, filter: Record<string, unknown>, _executionContext?: unknown, _options?: unknown, bounds?: unknown) => {\n if (options.readFailure) throw options.readFailure;\n // The real door orders and limits a bounded scan; this double does not, so a bounded read is refused rather than answered wrongly.\n if (bounds !== undefined) throw new Error('Unmodelled bounded infrastructure read (sort/limit) in the repositories-registry double');\n const rows = tables.get(resourceType);\n if (!rows) throw new Error(`No repository registered for resource type '${resourceType}'`);\n return rows.filter((row) => matchesRawFilter(row, filter)).map((row) => ({ ...row }));\n }),\n };\n}\n\nfunction matchesRawFilter(row: Record<string, unknown>, filter: Record<string, unknown>): boolean {\n return Object.entries(filter).every(([key, condition]) => {\n if (key === '$or') {\n if (!Array.isArray(condition)) throw new Error('$or expects an array of filters');\n return condition.some((clause) => matchesRawFilter(row, clause as Record<string, unknown>));\n }\n if (key.startsWith('$')) throw new Error(`Unmodelled top-level operator '${key}' in an infrastructure read filter`);\n return matchesCondition(row[key], condition);\n });\n}\n\nfunction matchesCondition(value: unknown, condition: unknown): boolean {\n if (condition !== null && typeof condition === 'object' && !Array.isArray(condition) && Object.keys(condition).some((k) => k.startsWith('$'))) {\n return Object.entries(condition as Record<string, unknown>).every(([operator, operand]) => {\n if (operator === '$in') {\n if (!Array.isArray(operand)) throw new Error('$in expects an array');\n return operand.some((candidate) => sameValue(value, candidate));\n }\n throw new Error(`Unmodelled operator '${operator}' in an infrastructure read filter`);\n });\n }\n return sameValue(value, condition);\n}\n\n/** Ids compare as strings: an ObjectId on MongoDB and a string on PostgreSQL name the same row. */\nfunction sameValue(left: unknown, right: unknown): boolean {\n if (left === undefined || left === null || right === undefined || right === null) return left === right;\n return String(left) === String(right);\n}\n"]}
|
package/dist/esm/billing/services/__tests__/subscription-provider-projection.test-double.d.ts
DELETED
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import { vi } from 'vitest';
|
|
2
|
-
import { BillingSubscriptionProjectionOutcome, type BillingSubscriptionProviderProjection } from '../billing-subscription-provider-projection.backend';
|
|
3
|
-
/** The answers a real projection gives when the slot, not the provider, decides that nothing is written. */
|
|
4
|
-
export type BillingSubscriptionProjectionSkip = BillingSubscriptionProjectionOutcome.SKIPPED_ADMISSION_PREPARED | BillingSubscriptionProjectionOutcome.SKIPPED_SLOT_IDENTITY_CHANGED | BillingSubscriptionProjectionOutcome.SKIPPED_SLOT_MOVED;
|
|
5
|
-
/**
|
|
6
|
-
* A provider-projection double for tests whose subject is NOT slot coordination (#535 S6): it reads the row, the provider, decides
|
|
7
|
-
* and writes once, with no slot and no transaction. The coordination itself — the version compare-and-swap, the PREPARED skip and
|
|
8
|
-
* release, the retry — is pinned by the projection's own tests, so a caller's test that used this double to assert ordering would be
|
|
9
|
-
* measuring the double.
|
|
10
|
-
*
|
|
11
|
-
* `skip` makes it answer as the slot would: PREPARED and a changed identity before any read, SLOT_MOVED after the reads and the
|
|
12
|
-
* decision but before the write. `releasedAbandonedAdmission` reports a release. That is what lets a caller pin how it REPORTS each.
|
|
13
|
-
*/
|
|
14
|
-
export declare function createPassthroughSubscriptionProjection(options?: {
|
|
15
|
-
skip?: BillingSubscriptionProjectionSkip;
|
|
16
|
-
releasedAbandonedAdmission?: boolean;
|
|
17
|
-
}): BillingSubscriptionProviderProjection & {
|
|
18
|
-
project: ReturnType<typeof vi.fn>;
|
|
19
|
-
};
|
|
20
|
-
//# sourceMappingURL=subscription-provider-projection.test-double.d.ts.map
|
package/dist/esm/billing/services/__tests__/subscription-provider-projection.test-double.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"subscription-provider-projection.test-double.d.ts","sourceRoot":"","sources":["../../../../../../src/billing/services/__tests__/subscription-provider-projection.test-double.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAC5B,OAAO,EACL,oCAAoC,EAEpC,KAAK,qCAAqC,EAC3C,MAAM,qDAAqD,CAAC;AAE7D,4GAA4G;AAC5G,MAAM,MAAM,iCAAiC,GACzC,oCAAoC,CAAC,0BAA0B,GAC/D,oCAAoC,CAAC,6BAA6B,GAClE,oCAAoC,CAAC,kBAAkB,CAAC;AAE5D;;;;;;;;GAQG;AACH,wBAAgB,uCAAuC,CAAC,OAAO,CAAC,EAAE;IAChE,IAAI,CAAC,EAAE,iCAAiC,CAAC;IACzC,0BAA0B,CAAC,EAAE,OAAO,CAAC;CACtC,GAAG,qCAAqC,GAAG;IAAE,OAAO,EAAE,UAAU,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC,CAAA;CAAE,CAiBhF"}
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
import { vi } from 'vitest';
|
|
2
|
-
import { BillingSubscriptionProjectionOutcome, } from '../billing-subscription-provider-projection.backend.js';
|
|
3
|
-
/**
|
|
4
|
-
* A provider-projection double for tests whose subject is NOT slot coordination (#535 S6): it reads the row, the provider, decides
|
|
5
|
-
* and writes once, with no slot and no transaction. The coordination itself — the version compare-and-swap, the PREPARED skip and
|
|
6
|
-
* release, the retry — is pinned by the projection's own tests, so a caller's test that used this double to assert ordering would be
|
|
7
|
-
* measuring the double.
|
|
8
|
-
*
|
|
9
|
-
* `skip` makes it answer as the slot would: PREPARED and a changed identity before any read, SLOT_MOVED after the reads and the
|
|
10
|
-
* decision but before the write. `releasedAbandonedAdmission` reports a release. That is what lets a caller pin how it REPORTS each.
|
|
11
|
-
*/
|
|
12
|
-
export function createPassthroughSubscriptionProjection(options) {
|
|
13
|
-
const releasedAbandonedAdmission = options?.releasedAbandonedAdmission ?? false;
|
|
14
|
-
const project = vi.fn(async (request) => {
|
|
15
|
-
const answer = (outcome, attempts) => ({ outcome, attempts, releasedAbandonedAdmission });
|
|
16
|
-
if (options?.skip === BillingSubscriptionProjectionOutcome.SKIPPED_ADMISSION_PREPARED
|
|
17
|
-
|| options?.skip === BillingSubscriptionProjectionOutcome.SKIPPED_SLOT_IDENTITY_CHANGED) {
|
|
18
|
-
return answer(options.skip, 0);
|
|
19
|
-
}
|
|
20
|
-
const current = await request.readSubscription();
|
|
21
|
-
if (!current)
|
|
22
|
-
return answer(BillingSubscriptionProjectionOutcome.SKIPPED_SUBSCRIPTION_ABSENT, 0);
|
|
23
|
-
const update = request.decide(await request.readProvider(), current);
|
|
24
|
-
if (update === null)
|
|
25
|
-
return answer(BillingSubscriptionProjectionOutcome.UNCHANGED, 1);
|
|
26
|
-
if (options?.skip === BillingSubscriptionProjectionOutcome.SKIPPED_SLOT_MOVED)
|
|
27
|
-
return answer(BillingSubscriptionProjectionOutcome.SKIPPED_SLOT_MOVED, 2);
|
|
28
|
-
await request.write(update, { ...request.options });
|
|
29
|
-
return answer(BillingSubscriptionProjectionOutcome.WRITTEN, 1);
|
|
30
|
-
});
|
|
31
|
-
return { project };
|
|
32
|
-
}
|
|
33
|
-
//# sourceMappingURL=subscription-provider-projection.test-double.js.map
|
package/dist/esm/billing/services/__tests__/subscription-provider-projection.test-double.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"subscription-provider-projection.test-double.js","sourceRoot":"","sources":["../../../../../../src/billing/services/__tests__/subscription-provider-projection.test-double.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAC;AAC5B,OAAO,EACL,oCAAoC,GAGrC,MAAM,qDAAqD,CAAC;AAQ7D;;;;;;;;GAQG;AACH,MAAM,UAAU,uCAAuC,CAAC,OAGvD;IACC,MAAM,0BAA0B,GAAG,OAAO,EAAE,0BAA0B,IAAI,KAAK,CAAC;IAChF,MAAM,OAAO,GAAG,EAAE,CAAC,EAAE,CAAC,KAAK,EAAE,OAAsD,EAAE,EAAE;QACrF,MAAM,MAAM,GAAG,CAAC,OAA6C,EAAE,QAAgB,EAAE,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,QAAQ,EAAE,0BAA0B,EAAE,CAAC,CAAC;QACxI,IAAI,OAAO,EAAE,IAAI,KAAK,oCAAoC,CAAC,0BAA0B;eAChF,OAAO,EAAE,IAAI,KAAK,oCAAoC,CAAC,6BAA6B,EAAE,CAAC;YAC1F,OAAO,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;QACjC,CAAC;QACD,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,gBAAgB,EAAE,CAAC;QACjD,IAAI,CAAC,OAAO;YAAE,OAAO,MAAM,CAAC,oCAAoC,CAAC,2BAA2B,EAAE,CAAC,CAAC,CAAC;QACjG,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,OAAO,CAAC,YAAY,EAAE,EAAE,OAAO,CAAC,CAAC;QACrE,IAAI,MAAM,KAAK,IAAI;YAAE,OAAO,MAAM,CAAC,oCAAoC,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC;QACtF,IAAI,OAAO,EAAE,IAAI,KAAK,oCAAoC,CAAC,kBAAkB;YAAE,OAAO,MAAM,CAAC,oCAAoC,CAAC,kBAAkB,EAAE,CAAC,CAAC,CAAC;QACzJ,MAAM,OAAO,CAAC,KAAK,CAAC,MAAM,EAAE,EAAE,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC;QACpD,OAAO,MAAM,CAAC,oCAAoC,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;IACjE,CAAC,CAAC,CAAC;IACH,OAAO,EAAE,OAAO,EAA8F,CAAC;AACjH,CAAC","sourcesContent":["import { vi } from 'vitest';\nimport {\n BillingSubscriptionProjectionOutcome,\n type BillingSubscriptionProjectionRequest,\n type BillingSubscriptionProviderProjection,\n} from '../billing-subscription-provider-projection.backend';\n\n/** The answers a real projection gives when the slot, not the provider, decides that nothing is written. */\nexport type BillingSubscriptionProjectionSkip =\n | BillingSubscriptionProjectionOutcome.SKIPPED_ADMISSION_PREPARED\n | BillingSubscriptionProjectionOutcome.SKIPPED_SLOT_IDENTITY_CHANGED\n | BillingSubscriptionProjectionOutcome.SKIPPED_SLOT_MOVED;\n\n/**\n * A provider-projection double for tests whose subject is NOT slot coordination (#535 S6): it reads the row, the provider, decides\n * and writes once, with no slot and no transaction. The coordination itself — the version compare-and-swap, the PREPARED skip and\n * release, the retry — is pinned by the projection's own tests, so a caller's test that used this double to assert ordering would be\n * measuring the double.\n *\n * `skip` makes it answer as the slot would: PREPARED and a changed identity before any read, SLOT_MOVED after the reads and the\n * decision but before the write. `releasedAbandonedAdmission` reports a release. That is what lets a caller pin how it REPORTS each.\n */\nexport function createPassthroughSubscriptionProjection(options?: {\n skip?: BillingSubscriptionProjectionSkip;\n releasedAbandonedAdmission?: boolean;\n}): BillingSubscriptionProviderProjection & { project: ReturnType<typeof vi.fn> } {\n const releasedAbandonedAdmission = options?.releasedAbandonedAdmission ?? false;\n const project = vi.fn(async (request: BillingSubscriptionProjectionRequest<unknown>) => {\n const answer = (outcome: BillingSubscriptionProjectionOutcome, attempts: number) => ({ outcome, attempts, releasedAbandonedAdmission });\n if (options?.skip === BillingSubscriptionProjectionOutcome.SKIPPED_ADMISSION_PREPARED\n || options?.skip === BillingSubscriptionProjectionOutcome.SKIPPED_SLOT_IDENTITY_CHANGED) {\n return answer(options.skip, 0);\n }\n const current = await request.readSubscription();\n if (!current) return answer(BillingSubscriptionProjectionOutcome.SKIPPED_SUBSCRIPTION_ABSENT, 0);\n const update = request.decide(await request.readProvider(), current);\n if (update === null) return answer(BillingSubscriptionProjectionOutcome.UNCHANGED, 1);\n if (options?.skip === BillingSubscriptionProjectionOutcome.SKIPPED_SLOT_MOVED) return answer(BillingSubscriptionProjectionOutcome.SKIPPED_SLOT_MOVED, 2);\n await request.write(update, { ...request.options });\n return answer(BillingSubscriptionProjectionOutcome.WRITTEN, 1);\n });\n return { project } as unknown as BillingSubscriptionProviderProjection & { project: ReturnType<typeof vi.fn> };\n}\n"]}
|
package/dist/esm/billing/services/custom-impl/__tests__/plan-change-confirmation.fixture.d.ts
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
export declare function slotRegistryFor(registry: object): {
|
|
2
|
-
readUnscopedByKey: (resource: unknown, filter: {
|
|
3
|
-
_id: string;
|
|
4
|
-
}) => Promise<Record<string, any>>;
|
|
5
|
-
insertDocumentForInfrastructure: (resource: unknown, document: Record<string, any>) => Promise<Record<string, any>>;
|
|
6
|
-
atomicGuardedMutation: (resource: unknown, filter: Record<string, any>, mutation: {
|
|
7
|
-
set?: Record<string, unknown>;
|
|
8
|
-
increment?: Record<string, number>;
|
|
9
|
-
}) => Promise<Record<string, any>>;
|
|
10
|
-
};
|
|
11
|
-
/**
|
|
12
|
-
* Commercial-policy fixtures execute the real handlers end to end, with an actual sealed quote: the preview, then the
|
|
13
|
-
* confirmation's BEFORE_TRANSACTIONAL_CORE phase (verify, admit, dispatch), its PREFIX (lock the applied generation) and its
|
|
14
|
-
* POSTFIX (complete, record, enqueue) inside a transaction context. Answers the items the core would write.
|
|
15
|
-
*/
|
|
16
|
-
export declare function confirmPlanFixture(variant: number, id: string, target: {
|
|
17
|
-
newProductKey: string;
|
|
18
|
-
newPriceId: string;
|
|
19
|
-
}, context: any, utils: any): Promise<{
|
|
20
|
-
items: any[];
|
|
21
|
-
}>;
|
|
22
|
-
//# sourceMappingURL=plan-change-confirmation.fixture.d.ts.map
|
package/dist/esm/billing/services/custom-impl/__tests__/plan-change-confirmation.fixture.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"plan-change-confirmation.fixture.d.ts","sourceRoot":"","sources":["../../../../../../../src/billing/services/custom-impl/__tests__/plan-change-confirmation.fixture.ts"],"names":[],"mappings":"AAaA,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM;kCAOR,OAAO,UAAU;QAAE,GAAG,EAAE,MAAM,CAAA;KAAE;gDAClB,OAAO,YAAY,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC;sCAKhD,OAAO,UAAU,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,YAAY;QAAE,GAAG,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QAAC,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;KAAE;EAShK;AAED;;;;GAIG;AACH,wBAAsB,kBAAkB,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE;IAAE,aAAa,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,EAAE,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG;WAkDxG,GAAG,EAAE;GACjD"}
|
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
import { CoreResourceType, PersistenceAdapter } from "@wildo-ai/saas-models";
|
|
2
|
-
import { createTransactionContext } from "../../../../databases/models/transaction.shared.schemas.js";
|
|
3
|
-
import { ResourceOperationBeforeTransactionalCoreDisposition } from "../../../../base-classes/services/services/resource-operation-custom-implementation-base.backend.service.js";
|
|
4
|
-
import { classifiedPrimaryKeyConflict } from "../../webhooks/__tests__/settlement-ledger.test-double.js";
|
|
5
|
-
import { createSubscriptionChangePlanFactories, createSubscriptionPreviewPlanChangeFactories } from "../subscription.mutations.custom-impl.backend.service.js";
|
|
6
|
-
/**
|
|
7
|
-
* The mutation slot, completion and outbox rows a confirmation writes (#535 S4b), kept per harness registry so consecutive
|
|
8
|
-
* plan changes in one test see the generation the previous one completed. A durable-store double, not proof of native
|
|
9
|
-
* atomicity — the native PostgreSQL pipeline test is that.
|
|
10
|
-
*/
|
|
11
|
-
const slotRowsByRegistry = new WeakMap();
|
|
12
|
-
export function slotRegistryFor(registry) {
|
|
13
|
-
let rows = slotRowsByRegistry.get(registry);
|
|
14
|
-
if (!rows)
|
|
15
|
-
slotRowsByRegistry.set(registry, (rows = new Map()));
|
|
16
|
-
const key = (resource, id) => `${String(resource)}:${String(id)}`;
|
|
17
|
-
const matches = (row, filter) => Object.entries(filter).every(([field, value]) => value && typeof value === "object" && "$in" in value ? value.$in.includes(row[field]) : row[field] === value);
|
|
18
|
-
return {
|
|
19
|
-
readUnscopedByKey: async (resource, filter) => structuredClone(rows.get(key(resource, filter._id)) ?? null),
|
|
20
|
-
insertDocumentForInfrastructure: async (resource, document) => {
|
|
21
|
-
if (rows.has(key(resource, document._id)))
|
|
22
|
-
throw classifiedPrimaryKeyConflict();
|
|
23
|
-
rows.set(key(resource, document._id), structuredClone(document));
|
|
24
|
-
return structuredClone(document);
|
|
25
|
-
},
|
|
26
|
-
atomicGuardedMutation: async (resource, filter, mutation) => {
|
|
27
|
-
const row = rows.get(key(resource, filter._id));
|
|
28
|
-
if (!row || !matches(row, filter))
|
|
29
|
-
return null;
|
|
30
|
-
const updated = { ...row, ...structuredClone(mutation.set ?? {}) };
|
|
31
|
-
for (const [path, delta] of Object.entries(mutation.increment ?? {}))
|
|
32
|
-
updated[path] = (updated[path] ?? 0) + delta;
|
|
33
|
-
rows.set(key(resource, filter._id), updated);
|
|
34
|
-
return structuredClone(updated);
|
|
35
|
-
},
|
|
36
|
-
};
|
|
37
|
-
}
|
|
38
|
-
/**
|
|
39
|
-
* Commercial-policy fixtures execute the real handlers end to end, with an actual sealed quote: the preview, then the
|
|
40
|
-
* confirmation's BEFORE_TRANSACTIONAL_CORE phase (verify, admit, dispatch), its PREFIX (lock the applied generation) and its
|
|
41
|
-
* POSTFIX (complete, record, enqueue) inside a transaction context. Answers the items the core would write.
|
|
42
|
-
*/
|
|
43
|
-
export async function confirmPlanFixture(variant, id, target, context, utils) {
|
|
44
|
-
const scopeField = variant === 0 ? "organizationId" : "userId";
|
|
45
|
-
const accountField = variant === 0 ? "organizationBillingAccountId" : "userBillingAccountId";
|
|
46
|
-
const subscriptionField = variant === 0 ? "organizationSubscriptionId" : "userSubscriptionId";
|
|
47
|
-
const scope = context[scopeField] ?? "fixture-scope";
|
|
48
|
-
const account = utils.currentObject[accountField] ?? "fixture-account";
|
|
49
|
-
const scopedContext = {
|
|
50
|
-
...context,
|
|
51
|
-
userId: context.userId ?? "fixture-actor",
|
|
52
|
-
[scopeField]: scope,
|
|
53
|
-
getContextResourceIds: () => new Map([
|
|
54
|
-
[scopeField, scope],
|
|
55
|
-
[accountField, account],
|
|
56
|
-
[subscriptionField, id],
|
|
57
|
-
]),
|
|
58
|
-
};
|
|
59
|
-
const currentObject = { ...utils.currentObject, _id: id, [scopeField]: scope, [accountField]: account };
|
|
60
|
-
const variantResourceType = variant === 0 ? CoreResourceType.ORGANIZATION_SUBSCRIPTIONS : CoreResourceType.USER_SUBSCRIPTIONS;
|
|
61
|
-
const slotRegistry = slotRegistryFor(utils.repositoriesRegistry ?? utils);
|
|
62
|
-
const scopedUtils = {
|
|
63
|
-
...utils,
|
|
64
|
-
currentObject,
|
|
65
|
-
repositoriesRegistry: {
|
|
66
|
-
...utils.repositoriesRegistry,
|
|
67
|
-
...slotRegistry,
|
|
68
|
-
// The confirmation judges freshness on the subscription read after the slot; in this double it is the row the test holds.
|
|
69
|
-
readUnscopedByKey: async (resource, filter) => resource === variantResourceType && filter._id === id ? structuredClone(currentObject) : slotRegistry.readUnscopedByKey(resource, filter),
|
|
70
|
-
},
|
|
71
|
-
};
|
|
72
|
-
// The configured application, never the request context, names the mutation identity; an HTTP context carries none.
|
|
73
|
-
const container = { get: () => ({ applicationId: "fixture-app", secrets: { security: { session: { secret: "fixture-preview-secret" } } } }) };
|
|
74
|
-
const preview = createSubscriptionPreviewPlanChangeFactories()[variant]({}, container).implementation;
|
|
75
|
-
const confirm = createSubscriptionChangePlanFactories()[variant]({}, container).implementation;
|
|
76
|
-
const quote = await preview.replaceCoreOperations(id, target, scopedContext, scopedUtils);
|
|
77
|
-
const request = { ...target, previewToken: quote.previewToken };
|
|
78
|
-
const decision = await confirm.beforeTransactionalCore(id, request, scopedContext, scopedUtils);
|
|
79
|
-
if (decision.disposition !== ResourceOperationBeforeTransactionalCoreDisposition.CONTINUE) {
|
|
80
|
-
throw new Error(`The confirmation fixture expects a fresh confirmation, got ${decision.disposition}`);
|
|
81
|
-
}
|
|
82
|
-
const coreUtils = {
|
|
83
|
-
...scopedUtils,
|
|
84
|
-
serviceOptions: { transaction: createTransactionContext({}, PersistenceAdapter.POSTGRESQL) },
|
|
85
|
-
beforeTransactionalCore: Object.freeze({ continuation: decision.continuation }),
|
|
86
|
-
postCommitEffectCapture: { prepareAudit: () => null, deferredContext: () => ({}), notificationInputs: () => ({}) },
|
|
87
|
-
};
|
|
88
|
-
await confirm.prefixCoreOperations(id, { ...request, ...decision.coreInputPatch }, scopedContext, coreUtils);
|
|
89
|
-
await confirm.postfixCoreOperations(id, { ...scopedUtils.currentObject, ...decision.coreInputPatch }, scopedContext, coreUtils);
|
|
90
|
-
return decision.coreInputPatch;
|
|
91
|
-
}
|
|
92
|
-
//# sourceMappingURL=plan-change-confirmation.fixture.js.map
|
package/dist/esm/billing/services/custom-impl/__tests__/plan-change-confirmation.fixture.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"plan-change-confirmation.fixture.js","sourceRoot":"","sources":["../../../../../../../src/billing/services/custom-impl/__tests__/plan-change-confirmation.fixture.ts"],"names":[],"mappings":"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","sourcesContent":["import { CoreResourceType, PersistenceAdapter } from \"@wildo-ai/saas-models\";\nimport { createTransactionContext } from \"../../../../databases/models/transaction.shared.schemas\";\nimport { ResourceOperationBeforeTransactionalCoreDisposition } from \"~wildo-backend-lib/base-classes/services/services/resource-operation-custom-implementation-base.backend.service\";\nimport { classifiedPrimaryKeyConflict } from \"../../webhooks/__tests__/settlement-ledger.test-double\";\nimport { createSubscriptionChangePlanFactories, createSubscriptionPreviewPlanChangeFactories } from \"../subscription.mutations.custom-impl.backend.service\";\n\n/**\n * The mutation slot, completion and outbox rows a confirmation writes (#535 S4b), kept per harness registry so consecutive\n * plan changes in one test see the generation the previous one completed. A durable-store double, not proof of native\n * atomicity — the native PostgreSQL pipeline test is that.\n */\nconst slotRowsByRegistry = new WeakMap<object, Map<string, Record<string, any>>>();\n\nexport function slotRegistryFor(registry: object) {\n let rows = slotRowsByRegistry.get(registry);\n if (!rows) slotRowsByRegistry.set(registry, (rows = new Map()));\n const key = (resource: unknown, id: unknown) => `${String(resource)}:${String(id)}`;\n const matches = (row: Record<string, any>, filter: Record<string, any>) => Object.entries(filter).every(([field, value]) =>\n value && typeof value === \"object\" && \"$in\" in value ? (value.$in as unknown[]).includes(row[field]) : row[field] === value);\n return {\n readUnscopedByKey: async (resource: unknown, filter: { _id: string }) => structuredClone(rows!.get(key(resource, filter._id)) ?? null),\n insertDocumentForInfrastructure: async (resource: unknown, document: Record<string, any>) => {\n if (rows!.has(key(resource, document._id))) throw classifiedPrimaryKeyConflict();\n rows!.set(key(resource, document._id), structuredClone(document));\n return structuredClone(document);\n },\n atomicGuardedMutation: async (resource: unknown, filter: Record<string, any>, mutation: { set?: Record<string, unknown>; increment?: Record<string, number> }) => {\n const row = rows!.get(key(resource, filter._id));\n if (!row || !matches(row, filter)) return null;\n const updated: Record<string, any> = { ...row, ...structuredClone(mutation.set ?? {}) };\n for (const [path, delta] of Object.entries(mutation.increment ?? {})) updated[path] = (updated[path] ?? 0) + delta;\n rows!.set(key(resource, filter._id), updated);\n return structuredClone(updated);\n },\n };\n}\n\n/**\n * Commercial-policy fixtures execute the real handlers end to end, with an actual sealed quote: the preview, then the\n * confirmation's BEFORE_TRANSACTIONAL_CORE phase (verify, admit, dispatch), its PREFIX (lock the applied generation) and its\n * POSTFIX (complete, record, enqueue) inside a transaction context. Answers the items the core would write.\n */\nexport async function confirmPlanFixture(variant: number, id: string, target: { newProductKey: string; newPriceId: string }, context: any, utils: any) {\n const scopeField = variant === 0 ? \"organizationId\" : \"userId\";\n const accountField = variant === 0 ? \"organizationBillingAccountId\" : \"userBillingAccountId\";\n const subscriptionField = variant === 0 ? \"organizationSubscriptionId\" : \"userSubscriptionId\";\n const scope = context[scopeField] ?? \"fixture-scope\";\n const account = utils.currentObject[accountField] ?? \"fixture-account\";\n const scopedContext = {\n ...context,\n userId: context.userId ?? \"fixture-actor\",\n [scopeField]: scope,\n getContextResourceIds: () =>\n new Map([\n [scopeField, scope],\n [accountField, account],\n [subscriptionField, id],\n ]),\n };\n const currentObject = { ...utils.currentObject, _id: id, [scopeField]: scope, [accountField]: account };\n const variantResourceType = variant === 0 ? CoreResourceType.ORGANIZATION_SUBSCRIPTIONS : CoreResourceType.USER_SUBSCRIPTIONS;\n const slotRegistry = slotRegistryFor(utils.repositoriesRegistry ?? utils);\n const scopedUtils = {\n ...utils,\n currentObject,\n repositoriesRegistry: {\n ...utils.repositoriesRegistry,\n ...slotRegistry,\n // The confirmation judges freshness on the subscription read after the slot; in this double it is the row the test holds.\n readUnscopedByKey: async (resource: unknown, filter: { _id: string }) =>\n resource === variantResourceType && filter._id === id ? structuredClone(currentObject) : slotRegistry.readUnscopedByKey(resource, filter),\n },\n };\n // The configured application, never the request context, names the mutation identity; an HTTP context carries none.\n const container = { get: () => ({ applicationId: \"fixture-app\", secrets: { security: { session: { secret: \"fixture-preview-secret\" } } } }) };\n const preview = (createSubscriptionPreviewPlanChangeFactories()[variant] as any)({}, container).implementation;\n const confirm = (createSubscriptionChangePlanFactories()[variant] as any)({}, container).implementation;\n const quote = await preview.replaceCoreOperations(id, target, scopedContext, scopedUtils);\n const request = { ...target, previewToken: quote.previewToken };\n\n const decision = await confirm.beforeTransactionalCore(id, request, scopedContext, scopedUtils);\n if (decision.disposition !== ResourceOperationBeforeTransactionalCoreDisposition.CONTINUE) {\n throw new Error(`The confirmation fixture expects a fresh confirmation, got ${decision.disposition}`);\n }\n const coreUtils = {\n ...scopedUtils,\n serviceOptions: { transaction: createTransactionContext({}, PersistenceAdapter.POSTGRESQL) },\n beforeTransactionalCore: Object.freeze({ continuation: decision.continuation }),\n postCommitEffectCapture: { prepareAudit: () => null, deferredContext: () => ({}), notificationInputs: () => ({}) },\n };\n await confirm.prefixCoreOperations(id, { ...request, ...decision.coreInputPatch }, scopedContext, coreUtils);\n await confirm.postfixCoreOperations(id, { ...scopedUtils.currentObject, ...decision.coreInputPatch }, scopedContext, coreUtils);\n return decision.coreInputPatch as { items: any[] };\n}\n"]}
|
package/dist/esm/billing/services/custom-impl/__tests__/subscription-keyed-mutation.fixture.d.ts
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Runs a subscription mutation admitted under an Idempotency-Key — CANCEL, RESUME, ADD_ADDON or REMOVE_ADDON (#535 S5b) — through its
|
|
3
|
-
* real handlers end to end: the BEFORE_TRANSACTIONAL_CORE phase (read the subscription after the slot, decide, admit, dispatch), then
|
|
4
|
-
* PREFIX (lock the applied generation, re-check the items) and POSTFIX (complete, record, enqueue) inside a transaction context.
|
|
5
|
-
*
|
|
6
|
-
* The slot, completion and outbox rows live in the same per-registry durable-store double the plan-change fixture uses, so consecutive
|
|
7
|
-
* changes in one test see the generation the previous one completed. A double, not proof of native atomicity — the native PostgreSQL
|
|
8
|
-
* add-on pipeline test is that. Answers the patch the core would write.
|
|
9
|
-
*/
|
|
10
|
-
export declare function runKeyedMutationFixture(factories: unknown[], variant: number, id: string, input: Record<string, unknown>, context: any, utils: any, options?: {
|
|
11
|
-
idempotencyKey?: string;
|
|
12
|
-
}): Promise<{
|
|
13
|
-
items: any[];
|
|
14
|
-
}>;
|
|
15
|
-
//# sourceMappingURL=subscription-keyed-mutation.fixture.d.ts.map
|