@classytic/repo-core 0.23.0 → 0.24.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.
- package/dist/cleanup/types.d.mts +34 -1
- package/dist/tenant/index.d.mts +2 -2
- package/dist/tenant/index.mjs +2 -2
- package/dist/tenant/resolve.d.mts +32 -1
- package/dist/tenant/resolve.mjs +40 -1
- package/package.json +1 -1
package/dist/cleanup/types.d.mts
CHANGED
|
@@ -85,10 +85,27 @@ interface CleanupStepExecuteContext extends CleanupStepContext {
|
|
|
85
85
|
* One preview line — maps 1:1 onto a host plan item. A business record class
|
|
86
86
|
* (`'sales facts'`, `'journal entries'`), never a collection name.
|
|
87
87
|
*/
|
|
88
|
+
/**
|
|
89
|
+
* What a step's `estimated` COUNTS. Absent ⇒ `'remove'`.
|
|
90
|
+
*
|
|
91
|
+
* `destructive: false` is NOT a substitute: it is true of both a protective
|
|
92
|
+
* guard (counts records it defends) and a projection rebuild (counts records it
|
|
93
|
+
* recomputes), and those mean opposite things in a "records to remove" headline.
|
|
94
|
+
* A guard reporting 173 protected journal entries once pushed that headline to
|
|
95
|
+
* 540 on a plan that removed 367 — a plausible, internally consistent, wrong
|
|
96
|
+
* number shown at the exact moment an operator authorises destruction.
|
|
97
|
+
*/
|
|
98
|
+
type CleanupStepDisposition = 'remove' | 'protect' | 'rebuild';
|
|
88
99
|
interface CleanupStepEstimate {
|
|
89
100
|
readonly resource: string;
|
|
90
101
|
/** Estimated records this step would affect. */
|
|
91
102
|
readonly estimated: number;
|
|
103
|
+
/**
|
|
104
|
+
* Whether `estimated` counts records REMOVED, PROTECTED, or REBUILT.
|
|
105
|
+
* Defaults to `'remove'`, so every existing purge step is unchanged and only
|
|
106
|
+
* a step that means something else has to say so.
|
|
107
|
+
*/
|
|
108
|
+
readonly disposition?: CleanupStepDisposition | undefined;
|
|
92
109
|
/** What this step RETAINS (e.g. `'measures kept, PII redacted'`). */
|
|
93
110
|
readonly retained?: string | undefined;
|
|
94
111
|
/**
|
|
@@ -147,6 +164,22 @@ interface CleanupStep {
|
|
|
147
164
|
* recipe is destructive iff ANY of its steps is.
|
|
148
165
|
*/
|
|
149
166
|
readonly destructive: boolean;
|
|
167
|
+
/**
|
|
168
|
+
* What this step IS, declared once — not merely what one estimate counted.
|
|
169
|
+
*
|
|
170
|
+
* Needed at the STEP because a caller has to know a step is protective
|
|
171
|
+
* WITHOUT running it. A host resolving an operator's exclusion list must
|
|
172
|
+
* refuse to switch a guard off, and asking `estimate()` to find out would mean
|
|
173
|
+
* running the counting queries for a line that is being taken out — and
|
|
174
|
+
* surfacing that line's blockers, so excluding a domain could still be refused
|
|
175
|
+
* because of it.
|
|
176
|
+
*
|
|
177
|
+
* `destructive: false` is not the same question: it is true of a guard AND of
|
|
178
|
+
* a projection rebuild, and a rebuild is perfectly reasonable to exclude.
|
|
179
|
+
*
|
|
180
|
+
* An estimate may restate it; absent everywhere ⇒ `'remove'`.
|
|
181
|
+
*/
|
|
182
|
+
readonly disposition?: CleanupStepDisposition | undefined;
|
|
150
183
|
/**
|
|
151
184
|
* Projection / scaffolding rebuilds this step performs AFTER its cleanup —
|
|
152
185
|
* surfaced in the preview's `rebuildActions` (e.g. `'rebuild sales rollup'`).
|
|
@@ -164,4 +197,4 @@ interface CleanupStep {
|
|
|
164
197
|
verify?(ctx: CleanupStepContext): Promise<readonly CleanupStepCheck[]>;
|
|
165
198
|
}
|
|
166
199
|
//#endregion
|
|
167
|
-
export { CleanupStep, CleanupStepCheck, CleanupStepContext, CleanupStepEstimate, CleanupStepExecuteContext, CleanupStepLogger, CleanupStepOutcome, CleanupStepProgress };
|
|
200
|
+
export { CleanupStep, CleanupStepCheck, CleanupStepContext, CleanupStepDisposition, CleanupStepEstimate, CleanupStepExecuteContext, CleanupStepLogger, CleanupStepOutcome, CleanupStepProgress };
|
package/dist/tenant/index.d.mts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import { ResolvedTenantConfig, TenantConfig, TenantFieldType, TenantStrategy } from "./types.mjs";
|
|
2
|
-
import { DEFAULT_TENANT_CONFIG, resolveTenantConfig, resolveTenantField } from "./resolve.mjs";
|
|
3
|
-
export { DEFAULT_TENANT_CONFIG, type ResolvedTenantConfig, type TenantConfig, type TenantFieldType, type TenantStrategy, resolveTenantConfig, resolveTenantField };
|
|
2
|
+
import { DEFAULT_TENANT_CONFIG, assertNoLegacyTenantKeys, resolveTenantConfig, resolveTenantField } from "./resolve.mjs";
|
|
3
|
+
export { DEFAULT_TENANT_CONFIG, type ResolvedTenantConfig, type TenantConfig, type TenantFieldType, type TenantStrategy, assertNoLegacyTenantKeys, resolveTenantConfig, resolveTenantField };
|
package/dist/tenant/index.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { DEFAULT_TENANT_CONFIG, resolveTenantConfig, resolveTenantField } from "./resolve.mjs";
|
|
2
|
-
export { DEFAULT_TENANT_CONFIG, resolveTenantConfig, resolveTenantField };
|
|
1
|
+
import { DEFAULT_TENANT_CONFIG, assertNoLegacyTenantKeys, resolveTenantConfig, resolveTenantField } from "./resolve.mjs";
|
|
2
|
+
export { DEFAULT_TENANT_CONFIG, assertNoLegacyTenantKeys, resolveTenantConfig, resolveTenantField };
|
|
@@ -28,5 +28,36 @@ declare function resolveTenantConfig(config?: TenantConfig | boolean): ResolvedT
|
|
|
28
28
|
* scoping" and all return `false` — callers get one thing to branch on.
|
|
29
29
|
*/
|
|
30
30
|
declare function resolveTenantField(config?: TenantConfig | boolean): string | false;
|
|
31
|
+
/**
|
|
32
|
+
* Refuse a config that still carries a pre-consolidation tenant key.
|
|
33
|
+
*
|
|
34
|
+
* ## Why this belongs in repo-core and not in each kernel
|
|
35
|
+
*
|
|
36
|
+
* Kernels are migrating from a per-package tenant shape (`multiTenant`, plus a
|
|
37
|
+
* sibling `tenantFieldType` in some) onto {@link TenantConfig} under the key
|
|
38
|
+
* `tenant`. `@classytic/ledger` has landed; catalog, order, cart, crm, flow,
|
|
39
|
+
* party, review, transfer and yard have adopted the TYPE but still read
|
|
40
|
+
* `multiTenant`. Each of those is a future rename.
|
|
41
|
+
*
|
|
42
|
+
* The rename itself is trivial. What is not trivial is the failure mode when a
|
|
43
|
+
* CALLER misses it, because every one of these resolvers reads
|
|
44
|
+
* `resolveTenantConfig(config.tenant ?? false)`: an absent `tenant` resolves to
|
|
45
|
+
* `strategy: 'none'` — no tenant field, no tenant filter, every read spanning
|
|
46
|
+
* ALL tenants, no error, and figures that look plausible. A host that asked for
|
|
47
|
+
* tenancy gets none, silently.
|
|
48
|
+
*
|
|
49
|
+
* Nine packages each remembering to hand-roll that check is nine chances to
|
|
50
|
+
* forget, and the one that forgets is the one that ships the leak. So the guard
|
|
51
|
+
* lives beside the resolver every one of them already calls.
|
|
52
|
+
*
|
|
53
|
+
* Additive and non-breaking: nothing calls it until a package renames.
|
|
54
|
+
*
|
|
55
|
+
* @param config the raw, unresolved shape as the host supplied it
|
|
56
|
+
* @param pkg package name for the message (e.g. `'defineOrder'`)
|
|
57
|
+
* @param extra additional legacy keys this package is retiring, as
|
|
58
|
+
* `[oldKey, newPath]` — pass `['tenantFieldType', 'tenant.fieldType']` when
|
|
59
|
+
* the package carried a sibling field-type option.
|
|
60
|
+
*/
|
|
61
|
+
declare function assertNoLegacyTenantKeys(config: unknown, pkg: string, extra?: ReadonlyArray<readonly [string, string]>): void;
|
|
31
62
|
//#endregion
|
|
32
|
-
export { DEFAULT_TENANT_CONFIG, resolveTenantConfig, resolveTenantField };
|
|
63
|
+
export { DEFAULT_TENANT_CONFIG, assertNoLegacyTenantKeys, resolveTenantConfig, resolveTenantField };
|
package/dist/tenant/resolve.mjs
CHANGED
|
@@ -71,5 +71,44 @@ function resolveTenantField(config) {
|
|
|
71
71
|
if (!resolved.enabled || resolved.strategy === "none") return false;
|
|
72
72
|
return resolved.tenantField;
|
|
73
73
|
}
|
|
74
|
+
/**
|
|
75
|
+
* Refuse a config that still carries a pre-consolidation tenant key.
|
|
76
|
+
*
|
|
77
|
+
* ## Why this belongs in repo-core and not in each kernel
|
|
78
|
+
*
|
|
79
|
+
* Kernels are migrating from a per-package tenant shape (`multiTenant`, plus a
|
|
80
|
+
* sibling `tenantFieldType` in some) onto {@link TenantConfig} under the key
|
|
81
|
+
* `tenant`. `@classytic/ledger` has landed; catalog, order, cart, crm, flow,
|
|
82
|
+
* party, review, transfer and yard have adopted the TYPE but still read
|
|
83
|
+
* `multiTenant`. Each of those is a future rename.
|
|
84
|
+
*
|
|
85
|
+
* The rename itself is trivial. What is not trivial is the failure mode when a
|
|
86
|
+
* CALLER misses it, because every one of these resolvers reads
|
|
87
|
+
* `resolveTenantConfig(config.tenant ?? false)`: an absent `tenant` resolves to
|
|
88
|
+
* `strategy: 'none'` — no tenant field, no tenant filter, every read spanning
|
|
89
|
+
* ALL tenants, no error, and figures that look plausible. A host that asked for
|
|
90
|
+
* tenancy gets none, silently.
|
|
91
|
+
*
|
|
92
|
+
* Nine packages each remembering to hand-roll that check is nine chances to
|
|
93
|
+
* forget, and the one that forgets is the one that ships the leak. So the guard
|
|
94
|
+
* lives beside the resolver every one of them already calls.
|
|
95
|
+
*
|
|
96
|
+
* Additive and non-breaking: nothing calls it until a package renames.
|
|
97
|
+
*
|
|
98
|
+
* @param config the raw, unresolved shape as the host supplied it
|
|
99
|
+
* @param pkg package name for the message (e.g. `'defineOrder'`)
|
|
100
|
+
* @param extra additional legacy keys this package is retiring, as
|
|
101
|
+
* `[oldKey, newPath]` — pass `['tenantFieldType', 'tenant.fieldType']` when
|
|
102
|
+
* the package carried a sibling field-type option.
|
|
103
|
+
*/
|
|
104
|
+
function assertNoLegacyTenantKeys(config, pkg, extra = []) {
|
|
105
|
+
if (config === null || typeof config !== "object") return;
|
|
106
|
+
const record = config;
|
|
107
|
+
const retired = [["multiTenant", "tenant"], ...extra];
|
|
108
|
+
for (const [key, became] of retired) {
|
|
109
|
+
if (record[key] === void 0) continue;
|
|
110
|
+
throw new Error(`${pkg}: \`${key}\` was renamed to \`${became}\`. It is REFUSED rather than ignored because ignoring it disables tenancy SILENTLY — no tenant field, no tenant filter, and every read spanning all tenants while returning plausible numbers. Move the value to \`tenant\` (\`tenant: false\` for a single-tenant deployment).`);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
74
113
|
//#endregion
|
|
75
|
-
export { DEFAULT_TENANT_CONFIG, resolveTenantConfig, resolveTenantField };
|
|
114
|
+
export { DEFAULT_TENANT_CONFIG, assertNoLegacyTenantKeys, resolveTenantConfig, resolveTenantField };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@classytic/repo-core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.24.0",
|
|
4
4
|
"description": "Driver-agnostic repository primitives: hooks, Filter IR, operations, pagination, cache contract. Foundation for mongokit, sqlitekit, pgkit, and prismakit. Lean by design — no plugins ship here; each kit owns its own.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|