@classytic/arc 2.15.4 → 2.16.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/README.md +1 -0
- package/bin/arc.js +12 -0
- package/dist/{BaseController-dx3m2J8V.mjs → BaseController-DlCCTIxJ.mjs} +61 -19
- package/dist/{HookSystem-Iiebom92.mjs → HookSystem-Cmf7-Etp.mjs} +8 -4
- package/dist/{QueryCache-D41bfdBB.d.mts → QueryCache-SvmT_9ti.d.mts} +1 -1
- package/dist/{ResourceRegistry-CTERg_2x.mjs → ResourceRegistry-f48hFk3m.mjs} +52 -9
- package/dist/audit/index.d.mts +1 -1
- package/dist/audit/index.mjs +4 -2
- package/dist/auth/index.d.mts +4 -4
- package/dist/auth/index.mjs +4 -4
- package/dist/auth/redis-session.d.mts +1 -1
- package/dist/{betterAuthOpenApi--M_i87dQ.mjs → betterAuthOpenApi-ClWxaceA.mjs} +10 -6
- package/dist/buildHandler-BZX6zzDM.mjs +300 -0
- package/dist/cache/index.d.mts +3 -3
- package/dist/cache/index.mjs +3 -3
- package/dist/{caching-SM8gghN6.mjs → caching-TeHE8G-v.mjs} +1 -1
- package/dist/cli/commands/describe.d.mts +35 -1
- package/dist/cli/commands/describe.mjs +52 -12
- package/dist/cli/commands/docs.d.mts +1 -4
- package/dist/cli/commands/docs.mjs +4 -16
- package/dist/cli/commands/generate.d.mts +2 -20
- package/dist/cli/commands/generate.mjs +1 -546
- package/dist/cli/commands/init.d.mts +2 -40
- package/dist/cli/commands/init.mjs +1 -3045
- package/dist/cli/commands/introspect.mjs +53 -64
- package/dist/cli/index.d.mts +2 -2
- package/dist/cli/index.mjs +2 -2
- package/dist/{constants-Cxde4rpC.mjs → constants-TrJVIJl0.mjs} +7 -0
- package/dist/core/index.d.mts +3 -3
- package/dist/core/index.mjs +5 -5
- package/dist/{core-CvmOqEms.mjs → core-DBJ_j6rX.mjs} +222 -44
- package/dist/createActionRouter-DUpN3Dd1.mjs +288 -0
- package/dist/{createAggregationRouter-B0bPDf5b.mjs → createAggregationRouter-Dq-TUCuY.mjs} +3 -2
- package/dist/{createApp-PFegs47-.mjs → createApp-DNccuhyI.mjs} +16 -14
- package/dist/{defineEvent-D5h7EvAx.mjs → defineEvent-DRwY0fYm.mjs} +1 -1
- package/dist/docs/index.d.mts +2 -2
- package/dist/docs/index.mjs +1 -1
- package/dist/{errorHandler-Bk-AGhkU.mjs → errorHandler-DpoXQHZ9.mjs} +17 -14
- package/dist/errors-C1lX_jlm.d.mts +91 -0
- package/dist/{eventPlugin-CaKTYkYM.mjs → eventPlugin-C2cGqtRO.mjs} +1 -1
- package/dist/{eventPlugin-qXpqTebY.d.mts → eventPlugin-CtHC_av1.d.mts} +1 -1
- package/dist/events/index.d.mts +3 -3
- package/dist/events/index.mjs +5 -5
- package/dist/events/transports/redis-stream-entry.d.mts +1 -1
- package/dist/events/transports/redis.d.mts +1 -1
- package/dist/factory/index.d.mts +1 -1
- package/dist/factory/index.mjs +2 -2
- package/dist/{fields-COhcH3fk.d.mts → fields-Anj0xdih.d.mts} +1 -1
- package/dist/generate-BWFwgcCM.d.mts +38 -0
- package/dist/generate-CYac-OLv.mjs +654 -0
- package/dist/hooks/index.d.mts +1 -1
- package/dist/hooks/index.mjs +1 -1
- package/dist/idempotency/index.d.mts +2 -2
- package/dist/idempotency/index.mjs +1 -1
- package/dist/idempotency/redis.d.mts +1 -1
- package/dist/{index-BTqLEvhu.d.mts → index-3oIimXQn.d.mts} +12 -12
- package/dist/{index-BstGxcc3.d.mts → index-B-ulKx5P.d.mts} +55 -4
- package/dist/{index-BswOSJCE.d.mts → index-CkW0flkU.d.mts} +355 -16
- package/dist/index.d.mts +6 -6
- package/dist/index.mjs +7 -8
- package/dist/init-Dv71MsJr.d.mts +71 -0
- package/dist/init-HDvoO9L5.mjs +3098 -0
- package/dist/integrations/event-gateway.d.mts +2 -2
- package/dist/integrations/event-gateway.mjs +1 -1
- package/dist/integrations/index.d.mts +2 -2
- package/dist/integrations/jobs.mjs +3 -3
- package/dist/integrations/mcp/index.d.mts +239 -7
- package/dist/integrations/mcp/index.mjs +2 -528
- package/dist/integrations/mcp/testing.d.mts +2 -2
- package/dist/integrations/mcp/testing.mjs +6 -10
- package/dist/integrations/streamline.mjs +26 -1
- package/dist/integrations/websocket-redis.d.mts +1 -1
- package/dist/integrations/websocket.d.mts +1 -1
- package/dist/integrations/websocket.mjs +1 -0
- package/dist/loadResourcesFromEntry-BLMEI2Xa.mjs +51 -0
- package/dist/{resourceToTools-tFYUNmM0.mjs → mcpPlugin-7vGV51ED.mjs} +1021 -318
- package/dist/{memory-UBydS5ku.mjs → memory-QOLe11D5.mjs} +2 -0
- package/dist/middleware/index.d.mts +1 -1
- package/dist/middleware/index.mjs +1 -1
- package/dist/{openapi-BHXhoX8O.mjs → openapi-34T9yNwd.mjs} +47 -36
- package/dist/permissions/index.d.mts +2 -2
- package/dist/permissions/index.mjs +1 -1
- package/dist/{permissions-ohQyv50e.mjs → permissions-CTxMrreC.mjs} +2 -2
- package/dist/{pipe-Zr0KXjQe.mjs → pipe-DiCyvyPN.mjs} +1 -0
- package/dist/pipeline/index.d.mts +1 -1
- package/dist/pipeline/index.mjs +1 -1
- package/dist/plugins/index.d.mts +5 -5
- package/dist/plugins/index.mjs +10 -10
- package/dist/plugins/response-cache.mjs +5 -5
- package/dist/plugins/tracing-entry.d.mts +1 -1
- package/dist/plugins/tracing-entry.mjs +1 -1
- package/dist/{pluralize-DQgqgifU.mjs → pluralize-B9M8xvy-.mjs} +2 -1
- package/dist/presets/filesUpload.d.mts +4 -4
- package/dist/presets/filesUpload.mjs +2 -2
- package/dist/presets/index.d.mts +1 -1
- package/dist/presets/index.mjs +1 -1
- package/dist/presets/multiTenant.d.mts +1 -1
- package/dist/presets/multiTenant.mjs +4 -3
- package/dist/presets/search.d.mts +2 -2
- package/dist/presets/search.mjs +1 -1
- package/dist/{presets-BbkjdPeH.mjs → presets-C9BE6WaZ.mjs} +2 -2
- package/dist/{queryCachePlugin-m1XsgAIJ.mjs → queryCachePlugin-B4XMSSe7.mjs} +2 -2
- package/dist/{queryCachePlugin-CqMdLI2-.d.mts → queryCachePlugin-Biqzfbi5.d.mts} +2 -2
- package/dist/{redis-DiMkdHEl.d.mts → redis-Cyzrz6SX.d.mts} +1 -1
- package/dist/{redis-stream-D6HzR1Z_.d.mts → redis-stream-DT-YjzrB.d.mts} +1 -1
- package/dist/registry/index.d.mts +319 -2
- package/dist/registry/index.mjs +3 -3
- package/dist/registry-BBE23CDj.mjs +576 -0
- package/dist/{routerShared-DrOa-26E.mjs → routerShared-CZV5aabX.mjs} +3 -3
- package/dist/scope/index.d.mts +3 -3
- package/dist/scope/index.mjs +3 -3
- package/dist/{sse-Bz-5ZeTt.mjs → sse-BY6sTy4P.mjs} +1 -1
- package/dist/testing/index.d.mts +2 -2
- package/dist/testing/index.mjs +16 -7
- package/dist/testing/storageContract.d.mts +1 -1
- package/dist/types/index.d.mts +5 -5
- package/dist/types/storage.d.mts +1 -1
- package/dist/{types-C_s5moIu.mjs → types-Bi0r0vjG.mjs} +53 -1
- package/dist/{types-BQsjgQzS.d.mts → types-BsJMEQ4D.d.mts} +106 -12
- package/dist/{types-DrBaUwyV.d.mts → types-D-fYtKjb.d.mts} +33 -10
- package/dist/{types-CTYvcwHe.d.mts → types-DVfpSfx2.d.mts} +42 -1
- package/dist/utils/index.d.mts +1286 -2
- package/dist/utils/index.mjs +1 -1
- package/dist/{utils-_h9B3c57.mjs → utils-DC5ycPfr.mjs} +89 -40
- package/dist/{buildHandler-CcFOpJLh.mjs → validate-By96rH0r.mjs} +8 -299
- package/dist/{versioning-hmkPcDlX.d.mts → versioning-ZwX9tmbS.d.mts} +1 -1
- package/package.json +21 -28
- package/skills/arc/SKILL.md +300 -706
- package/skills/arc/references/auth.md +19 -7
- package/skills/arc-code-review/SKILL.md +1 -1
- package/skills/arc-code-review/references/arc-cheatsheet.md +100 -322
- package/dist/createActionRouter-S3MLVYot.mjs +0 -220
- package/dist/index-bRjYu21O.d.mts +0 -1320
- package/dist/org/index.d.mts +0 -66
- package/dist/org/index.mjs +0 -486
- package/dist/org/types.d.mts +0 -82
- package/dist/org/types.mjs +0 -1
- package/dist/registry-I-ogLgL9.mjs +0 -46
- /package/dist/{EventTransport-CT_52aWU.d.mts → EventTransport-C-2oAHtw.d.mts} +0 -0
- /package/dist/{EventTransport-DLWoUMHy.mjs → EventTransport-Hxvv5QQz.mjs} +0 -0
- /package/dist/{actionPermissions-CyUkQu6O.mjs → actionPermissions-Bjmvn7Eb.mjs} +0 -0
- /package/dist/{elevation-BXOWoGCF.d.mts → elevation-0YBpa663.d.mts} +0 -0
- /package/dist/{elevation-DgoeTyfX.mjs → elevation-Dci0AYLT.mjs} +0 -0
- /package/dist/{errorHandler-DFr45ZG4.d.mts → errorHandler-mHuyWzZE.d.mts} +0 -0
- /package/dist/{externalPaths-BD5nw6St.d.mts → externalPaths-DFg-2KTp.d.mts} +0 -0
- /package/dist/{interface-beEtJyWM.d.mts → interface-CH0OQudo.d.mts} +0 -0
- /package/dist/{interface-DfLGcus7.d.mts → interface-NwJ_qPlY.d.mts} +0 -0
- /package/dist/{keys-CGcCbNyu.mjs → keys-DopsCuyQ.mjs} +0 -0
- /package/dist/{loadResources-DBMQg_Aj.mjs → loadResources-ChQEj8ih.mjs} +0 -0
- /package/dist/{metrics-Qnvwc-LQ.mjs → metrics-TuOmguhi.mjs} +0 -0
- /package/dist/{replyHelpers-CK-FNO8E.mjs → replyHelpers-C-gD32oF.mjs} +0 -0
- /package/dist/{schemaIR-lYhC2gE5.mjs → schemaIR-Ctc89DSn.mjs} +0 -0
- /package/dist/{sessionManager-C4Le_UB3.d.mts → sessionManager-BqFegc0W.d.mts} +0 -0
- /package/dist/{storage-Dfzt4VTl.d.mts → storage-D2KZJAmn.d.mts} +0 -0
- /package/dist/{store-helpers-BkIN9-vu.mjs → store-helpers-B0sunfZZ.mjs} +0 -0
- /package/dist/{tracing-QJVprktp.d.mts → tracing-Dm8n7Cnn.d.mts} +0 -0
- /package/dist/{versioning-BUrT5aP4.mjs → versioning-B6mimogM.mjs} +0 -0
- /package/dist/{websocket-ChC2rqe1.d.mts → websocket-BkjeGZRn.d.mts} +0 -0
|
@@ -0,0 +1,576 @@
|
|
|
1
|
+
import { t as CRUD_OPERATIONS } from "./constants-TrJVIJl0.mjs";
|
|
2
|
+
import fp from "fastify-plugin";
|
|
3
|
+
//#region src/registry/assertNoTenantData.ts
|
|
4
|
+
/**
|
|
5
|
+
* Walk every cascading resource, run a tenant-scoped count, compare
|
|
6
|
+
* against the strategy's expected outcome.
|
|
7
|
+
*
|
|
8
|
+
* Designed for use inside compliance tests:
|
|
9
|
+
*
|
|
10
|
+
* ```ts
|
|
11
|
+
* import { assertNoTenantData } from '@classytic/arc/registry';
|
|
12
|
+
*
|
|
13
|
+
* it('after org delete, no tenant data leaks', async () => {
|
|
14
|
+
* await cascadeDeleteForOrganization(arc.registry, { organizationId: 'test-org' });
|
|
15
|
+
* const report = await assertNoTenantData(arc.registry, { organizationId: 'test-org' });
|
|
16
|
+
* expect(report.ok).toBe(true);
|
|
17
|
+
* expect(report.leaks).toHaveLength(0);
|
|
18
|
+
* });
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
async function assertNoTenantData(registry, options) {
|
|
22
|
+
const { organizationId, only, skipAnonymize = true } = options;
|
|
23
|
+
if (!organizationId) throw new Error("assertNoTenantData: `organizationId` is required");
|
|
24
|
+
const onlySet = only ? new Set(only) : void 0;
|
|
25
|
+
const leaks = [];
|
|
26
|
+
const skipped = [];
|
|
27
|
+
let checked = 0;
|
|
28
|
+
for (const r of registry.getAll()) {
|
|
29
|
+
const resolved = r.resolvedTenantPurge;
|
|
30
|
+
if (!resolved || resolved.source === "disabled") continue;
|
|
31
|
+
if (onlySet && !onlySet.has(r.name)) continue;
|
|
32
|
+
const tenantField = typeof r.tenantField === "string" && r.tenantField || "organizationId";
|
|
33
|
+
const strategy = resolved.strategy;
|
|
34
|
+
if (strategy.type === "skip") {
|
|
35
|
+
skipped.push({
|
|
36
|
+
resource: r.name,
|
|
37
|
+
reason: strategy.reason
|
|
38
|
+
});
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
if (strategy.type === "anonymize" && skipAnonymize) {
|
|
42
|
+
skipped.push({
|
|
43
|
+
resource: r.name,
|
|
44
|
+
reason: "anonymize (rows legitimately retained)"
|
|
45
|
+
});
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
const repo = registry.getAdapter(r.name)?.repository;
|
|
49
|
+
if (!repo?.count) {
|
|
50
|
+
skipped.push({
|
|
51
|
+
resource: r.name,
|
|
52
|
+
reason: "adapter repository has no `count` method — cannot verify"
|
|
53
|
+
});
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
const actual = await repo.count({ [tenantField]: organizationId });
|
|
57
|
+
checked++;
|
|
58
|
+
const expected = 0;
|
|
59
|
+
if (actual !== expected) leaks.push({
|
|
60
|
+
resource: r.name,
|
|
61
|
+
tenantField,
|
|
62
|
+
strategy: strategy.type,
|
|
63
|
+
expected,
|
|
64
|
+
actual
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
return {
|
|
68
|
+
ok: leaks.length === 0,
|
|
69
|
+
organizationId,
|
|
70
|
+
checked,
|
|
71
|
+
skipped,
|
|
72
|
+
leaks
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
//#endregion
|
|
76
|
+
//#region src/registry/purgeResource.ts
|
|
77
|
+
/**
|
|
78
|
+
* Run the resolved strategy against one resource's repository.
|
|
79
|
+
*
|
|
80
|
+
* - `skip` strategy: returns `path: 'skipped'` without touching the repo.
|
|
81
|
+
* - `purgeByField` present: routes through it (chunked, progress, abort).
|
|
82
|
+
* - `purgeByField` absent + hard strategy: legacy `deleteMany` fallback.
|
|
83
|
+
* - `purgeByField` absent + non-hard strategy: returns `path: 'unsupported'`
|
|
84
|
+
* with a clear error — soft/anonymize require the new primitive.
|
|
85
|
+
*/
|
|
86
|
+
async function purgeResource(resourceName, tenantField, organizationId, resolved, repo, options = {}) {
|
|
87
|
+
const { strategy } = resolved;
|
|
88
|
+
if (strategy.type === "skip") return {
|
|
89
|
+
resource: resourceName,
|
|
90
|
+
tenantField,
|
|
91
|
+
strategy: "skip",
|
|
92
|
+
processed: 0,
|
|
93
|
+
ok: true,
|
|
94
|
+
path: "skipped",
|
|
95
|
+
skipReason: strategy.reason
|
|
96
|
+
};
|
|
97
|
+
if (typeof repo.purgeByField === "function") {
|
|
98
|
+
const result = await repo.purgeByField(tenantField, organizationId, strategy, {
|
|
99
|
+
batchSize: options.batchSize ?? resolved.batchSize,
|
|
100
|
+
onProgress: options.onProgress,
|
|
101
|
+
signal: options.signal
|
|
102
|
+
});
|
|
103
|
+
return {
|
|
104
|
+
resource: resourceName,
|
|
105
|
+
tenantField,
|
|
106
|
+
strategy: result.strategy,
|
|
107
|
+
processed: result.processed,
|
|
108
|
+
ok: result.ok,
|
|
109
|
+
path: "purgeByField",
|
|
110
|
+
durationMs: result.durationMs,
|
|
111
|
+
...result.error ? { error: result.error } : {}
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
if (strategy.type !== "hard") return {
|
|
115
|
+
resource: resourceName,
|
|
116
|
+
tenantField,
|
|
117
|
+
strategy: strategy.type,
|
|
118
|
+
processed: 0,
|
|
119
|
+
ok: false,
|
|
120
|
+
path: "unsupported",
|
|
121
|
+
error: {
|
|
122
|
+
code: "arc.purge.unsupported_strategy",
|
|
123
|
+
message: `Resource '${resourceName}' uses strategy '${strategy.type}' but its adapter repository does not implement \`purgeByField\`. Upgrade the adapter (@classytic/mongokit ≥ 3.13.4 / @classytic/sqlitekit ≥ 0.3.4) or change the strategy to 'hard'.`
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
const op = repo.deleteMany ?? repo.deleteByFilter ?? repo.removeMany;
|
|
127
|
+
if (!op) return {
|
|
128
|
+
resource: resourceName,
|
|
129
|
+
tenantField,
|
|
130
|
+
strategy: "hard",
|
|
131
|
+
processed: 0,
|
|
132
|
+
ok: false,
|
|
133
|
+
path: "unsupported",
|
|
134
|
+
error: {
|
|
135
|
+
code: "arc.purge.no_bulk_op",
|
|
136
|
+
message: `Resource '${resourceName}' adapter exposes neither \`purgeByField\` nor \`deleteMany\` / \`deleteByFilter\` / \`removeMany\` — bulk cleanup is not supported.`
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
try {
|
|
140
|
+
const start = Date.now();
|
|
141
|
+
const result = await op.call(repo, { [tenantField]: organizationId });
|
|
142
|
+
return {
|
|
143
|
+
resource: resourceName,
|
|
144
|
+
tenantField,
|
|
145
|
+
strategy: "hard",
|
|
146
|
+
processed: typeof result === "number" ? result : typeof result?.deletedCount === "number" ? result.deletedCount : typeof result?.count === "number" ? result.count : -1,
|
|
147
|
+
ok: true,
|
|
148
|
+
path: "legacy-deleteMany",
|
|
149
|
+
durationMs: Date.now() - start
|
|
150
|
+
};
|
|
151
|
+
} catch (err) {
|
|
152
|
+
return {
|
|
153
|
+
resource: resourceName,
|
|
154
|
+
tenantField,
|
|
155
|
+
strategy: "hard",
|
|
156
|
+
processed: 0,
|
|
157
|
+
ok: false,
|
|
158
|
+
path: "legacy-deleteMany",
|
|
159
|
+
error: {
|
|
160
|
+
code: err?.code,
|
|
161
|
+
message: err instanceof Error ? err.message : String(err)
|
|
162
|
+
}
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
//#endregion
|
|
167
|
+
//#region src/registry/cascadeOrgDelete.ts
|
|
168
|
+
/**
|
|
169
|
+
* Names of resources flagged for cascade — used by audit scripts.
|
|
170
|
+
* A resource is cascading when its resolved strategy isn't `disabled`
|
|
171
|
+
* (i.e. the host declared `onTenantDelete`).
|
|
172
|
+
*/
|
|
173
|
+
function getCascadingResources(registry) {
|
|
174
|
+
return registry.getAll().filter((r) => isResourceCascading(r)).map((r) => r.name);
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Rich introspection — returns the resolved strategy + source per
|
|
178
|
+
* cascading resource. Use for audit dashboards that answer "what
|
|
179
|
+
* happens to this resource on org-delete?" without grepping the
|
|
180
|
+
* source.
|
|
181
|
+
*/
|
|
182
|
+
function getCascadingResourcesWithMetadata(registry) {
|
|
183
|
+
return registry.getAll().filter((r) => isResourceCascading(r)).map((r) => {
|
|
184
|
+
const resolved = r.resolvedTenantPurge;
|
|
185
|
+
const tenantField = typeof r.tenantField === "string" && r.tenantField || "organizationId";
|
|
186
|
+
const r0 = resolved ?? {
|
|
187
|
+
strategy: { type: "hard" },
|
|
188
|
+
priority: 100,
|
|
189
|
+
source: "declared"
|
|
190
|
+
};
|
|
191
|
+
return {
|
|
192
|
+
name: r.name,
|
|
193
|
+
tenantField,
|
|
194
|
+
strategy: r0.strategy.type,
|
|
195
|
+
source: r0.source,
|
|
196
|
+
priority: r0.priority
|
|
197
|
+
};
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
/** A resource cascades iff its resolved strategy isn't `disabled`. */
|
|
201
|
+
function isResourceCascading(r) {
|
|
202
|
+
return r.resolvedTenantPurge ? r.resolvedTenantPurge.source !== "disabled" : false;
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Cascade-cleanup every tenant-scoped resource for the given organization.
|
|
206
|
+
* Walks the registry in **ascending priority order** (`onTenantDelete.priority`
|
|
207
|
+
* — leaf data first, references last), runs each resource's resolved
|
|
208
|
+
* strategy via `purgeByField` when available (chunked, progress, abort),
|
|
209
|
+
* and returns a structured report.
|
|
210
|
+
*
|
|
211
|
+
* **Strategy resolution** lives in `resolveTenantPurge.ts` — this runner
|
|
212
|
+
* just reads `resource.resolvedTenantPurge` (computed once at boot).
|
|
213
|
+
*
|
|
214
|
+
* **Per-resource execution** lives in `purgeResource.ts` — preferred
|
|
215
|
+
* path is the kit's `purgeByField` (chunked + plugin-composed); falls
|
|
216
|
+
* back to legacy `deleteMany` only for `hard` strategy on adapters that
|
|
217
|
+
* haven't been upgraded.
|
|
218
|
+
*
|
|
219
|
+
* **Failure semantics**: continues on per-resource error, returns the
|
|
220
|
+
* full report. Hosts decide whether a partial cascade is a hard
|
|
221
|
+
* failure (re-throw) or degraded mode (log + alert).
|
|
222
|
+
*
|
|
223
|
+
* @param registry The arc resource registry (`fastify.arc.registry`).
|
|
224
|
+
* @param options Org id + filters + logger + progress + signal.
|
|
225
|
+
*/
|
|
226
|
+
async function cascadeDeleteForOrganization(registry, options) {
|
|
227
|
+
const { organizationId, skip, only, logger, onProgress, signal, batchSize, concurrency = 1, checkpoint } = options;
|
|
228
|
+
if (!organizationId) throw new Error("cascadeDeleteForOrganization: `organizationId` is required");
|
|
229
|
+
if (!Number.isInteger(concurrency) || concurrency < 1) throw new Error("cascadeDeleteForOrganization: `concurrency` must be a positive integer");
|
|
230
|
+
const skipSet = skip ? new Set(skip) : void 0;
|
|
231
|
+
const onlySet = only ? new Set(only) : void 0;
|
|
232
|
+
const start = Date.now();
|
|
233
|
+
const resumeState = await checkpoint?.read();
|
|
234
|
+
const completedSet = resumeState ? new Set(resumeState.completedResources) : void 0;
|
|
235
|
+
const flagged = registry.getAll().filter((r) => {
|
|
236
|
+
if (!r.resolvedTenantPurge || r.resolvedTenantPurge.source === "disabled") return false;
|
|
237
|
+
if (skipSet?.has(r.name)) return false;
|
|
238
|
+
if (onlySet && !onlySet.has(r.name)) return false;
|
|
239
|
+
if (completedSet?.has(r.name)) return false;
|
|
240
|
+
return true;
|
|
241
|
+
}).sort((a, b) => {
|
|
242
|
+
return (a.resolvedTenantPurge?.priority ?? 100) - (b.resolvedTenantPurge?.priority ?? 100);
|
|
243
|
+
});
|
|
244
|
+
const results = [];
|
|
245
|
+
const completed = resumeState ? [...resumeState.completedResources] : [];
|
|
246
|
+
const groups = groupByPriority(flagged);
|
|
247
|
+
for (const group of groups) {
|
|
248
|
+
if (signal?.aborted) break;
|
|
249
|
+
const groupResults = await runWithConcurrency(group, concurrency, async (r) => {
|
|
250
|
+
if (signal?.aborted) return;
|
|
251
|
+
return runOneResource(r, registry, organizationId, {
|
|
252
|
+
onProgress,
|
|
253
|
+
signal,
|
|
254
|
+
batchSize,
|
|
255
|
+
logger
|
|
256
|
+
});
|
|
257
|
+
});
|
|
258
|
+
for (const report of groupResults) {
|
|
259
|
+
if (!report) continue;
|
|
260
|
+
results.push(report);
|
|
261
|
+
if (!report.error) {
|
|
262
|
+
completed.push(report.resource);
|
|
263
|
+
if (checkpoint) await checkpoint.write({ completedResources: [...completed] });
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
const successes = results.filter((row) => !row.error);
|
|
268
|
+
return {
|
|
269
|
+
organizationId,
|
|
270
|
+
resources: results,
|
|
271
|
+
successes,
|
|
272
|
+
failures: results.filter((row) => row.error),
|
|
273
|
+
totalDeleted: successes.reduce((sum, r) => sum + (r.deletedCount > 0 ? r.deletedCount : 0), 0),
|
|
274
|
+
durationMs: Date.now() - start
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
/** Process a single registry entry under its resolved strategy. */
|
|
278
|
+
async function runOneResource(r, registry, organizationId, ctx) {
|
|
279
|
+
const tenantField = typeof r.tenantField === "string" && r.tenantField || "organizationId";
|
|
280
|
+
const repo = registry.getAdapter(r.name)?.repository;
|
|
281
|
+
if (!repo) {
|
|
282
|
+
const report = {
|
|
283
|
+
resource: r.name,
|
|
284
|
+
tenantField,
|
|
285
|
+
deletedCount: 0,
|
|
286
|
+
path: "unsupported",
|
|
287
|
+
error: {
|
|
288
|
+
code: "arc.no_adapter",
|
|
289
|
+
message: `Resource '${r.name}' has no adapter repository — cascade skipped`
|
|
290
|
+
}
|
|
291
|
+
};
|
|
292
|
+
ctx.logger?.warn?.(report, "[Arc/Cascade] resource skipped (no adapter)");
|
|
293
|
+
return report;
|
|
294
|
+
}
|
|
295
|
+
const resourceProgress = ctx.onProgress ? (event) => ctx.onProgress?.({
|
|
296
|
+
...event,
|
|
297
|
+
resource: r.name
|
|
298
|
+
}) : void 0;
|
|
299
|
+
const outcome = await purgeResource(r.name, tenantField, organizationId, r.resolvedTenantPurge ?? {
|
|
300
|
+
strategy: { type: "hard" },
|
|
301
|
+
priority: 100,
|
|
302
|
+
source: "declared"
|
|
303
|
+
}, repo, {
|
|
304
|
+
onProgress: resourceProgress,
|
|
305
|
+
signal: ctx.signal,
|
|
306
|
+
batchSize: ctx.batchSize
|
|
307
|
+
});
|
|
308
|
+
const report = {
|
|
309
|
+
resource: outcome.resource,
|
|
310
|
+
tenantField: outcome.tenantField,
|
|
311
|
+
deletedCount: outcome.processed,
|
|
312
|
+
strategy: outcome.strategy,
|
|
313
|
+
strategySource: r.resolvedTenantPurge?.source,
|
|
314
|
+
path: outcome.path,
|
|
315
|
+
...outcome.skipReason ? { skipReason: outcome.skipReason } : {},
|
|
316
|
+
...outcome.error ? { error: outcome.error } : {}
|
|
317
|
+
};
|
|
318
|
+
if (outcome.ok) ctx.logger?.info?.(report, "[Arc/Cascade] resource processed");
|
|
319
|
+
else ctx.logger?.error?.({ report }, "[Arc/Cascade] resource cascade failed");
|
|
320
|
+
return report;
|
|
321
|
+
}
|
|
322
|
+
/**
|
|
323
|
+
* Group a priority-sorted list of resources into priority barriers.
|
|
324
|
+
* Each returned sub-array contains resources of identical priority;
|
|
325
|
+
* the outer array preserves ascending order. The cascade runner uses
|
|
326
|
+
* this so all priority-N resources finish before any priority-(N+M)
|
|
327
|
+
* starts — leaf-before-references invariant survives concurrency.
|
|
328
|
+
*/
|
|
329
|
+
function groupByPriority(sorted) {
|
|
330
|
+
if (sorted.length === 0) return [];
|
|
331
|
+
const first = sorted[0];
|
|
332
|
+
if (!first) return [];
|
|
333
|
+
const groups = [];
|
|
334
|
+
let current = [first];
|
|
335
|
+
let currentPriority = first.resolvedTenantPurge?.priority ?? 100;
|
|
336
|
+
for (let i = 1; i < sorted.length; i++) {
|
|
337
|
+
const item = sorted[i];
|
|
338
|
+
if (!item) continue;
|
|
339
|
+
const p = item.resolvedTenantPurge?.priority ?? 100;
|
|
340
|
+
if (p === currentPriority) current.push(item);
|
|
341
|
+
else {
|
|
342
|
+
groups.push(current);
|
|
343
|
+
current = [item];
|
|
344
|
+
currentPriority = p;
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
groups.push(current);
|
|
348
|
+
return groups;
|
|
349
|
+
}
|
|
350
|
+
/**
|
|
351
|
+
* Run `fn` over `items` with bounded concurrency. Order of results
|
|
352
|
+
* matches input order. Simpler shape than a worker-pool because the
|
|
353
|
+
* group sizes are small (typically <=12 resources per priority band).
|
|
354
|
+
*/
|
|
355
|
+
async function runWithConcurrency(items, concurrency, fn) {
|
|
356
|
+
if (concurrency === 1 || items.length <= 1) {
|
|
357
|
+
const results = [];
|
|
358
|
+
for (const item of items) results.push(await fn(item));
|
|
359
|
+
return results;
|
|
360
|
+
}
|
|
361
|
+
const results = new Array(items.length);
|
|
362
|
+
let cursor = 0;
|
|
363
|
+
const workers = Array.from({ length: Math.min(concurrency, items.length) }, async () => {
|
|
364
|
+
while (true) {
|
|
365
|
+
const i = cursor++;
|
|
366
|
+
if (i >= items.length) break;
|
|
367
|
+
const item = items[i];
|
|
368
|
+
if (item === void 0) continue;
|
|
369
|
+
results[i] = await fn(item);
|
|
370
|
+
}
|
|
371
|
+
});
|
|
372
|
+
await Promise.all(workers);
|
|
373
|
+
return results;
|
|
374
|
+
}
|
|
375
|
+
//#endregion
|
|
376
|
+
//#region src/registry/introspectionPlugin.ts
|
|
377
|
+
const introspectionPlugin = async (fastify, opts = {}) => {
|
|
378
|
+
const { prefix = "/_resources", authRoles = ["superadmin"], enabled = true } = opts;
|
|
379
|
+
if (!enabled) {
|
|
380
|
+
fastify.log?.debug?.("Introspection plugin disabled");
|
|
381
|
+
return;
|
|
382
|
+
}
|
|
383
|
+
const typedFastify = fastify;
|
|
384
|
+
const authMiddleware = authRoles.length > 0 && typedFastify.authenticate ? [typedFastify.authenticate, typedFastify.authorize?.(...authRoles)].filter(Boolean) : [];
|
|
385
|
+
const getRegistry = () => fastify.arc?.registry;
|
|
386
|
+
await fastify.register(async (instance) => {
|
|
387
|
+
instance.get("/", { preHandler: authMiddleware }, async (_req, _reply) => {
|
|
388
|
+
return getRegistry()?.getIntrospection() ?? {
|
|
389
|
+
resources: [],
|
|
390
|
+
stats: {},
|
|
391
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
392
|
+
};
|
|
393
|
+
});
|
|
394
|
+
instance.get("/stats", { preHandler: authMiddleware }, async (_req, _reply) => {
|
|
395
|
+
return getRegistry()?.getStats() ?? {
|
|
396
|
+
totalResources: 0,
|
|
397
|
+
byModule: {},
|
|
398
|
+
presetUsage: {},
|
|
399
|
+
totalRoutes: 0,
|
|
400
|
+
totalEvents: 0
|
|
401
|
+
};
|
|
402
|
+
});
|
|
403
|
+
instance.get("/:name", {
|
|
404
|
+
schema: { params: {
|
|
405
|
+
type: "object",
|
|
406
|
+
properties: { name: { type: "string" } },
|
|
407
|
+
required: ["name"]
|
|
408
|
+
} },
|
|
409
|
+
preHandler: authMiddleware
|
|
410
|
+
}, async (req, reply) => {
|
|
411
|
+
const resource = getRegistry()?.get(req.params.name);
|
|
412
|
+
if (!resource) return reply.code(404).send({ error: `Resource '${req.params.name}' not found` });
|
|
413
|
+
return resource;
|
|
414
|
+
});
|
|
415
|
+
}, { prefix });
|
|
416
|
+
fastify.log?.debug?.(`Introspection API at ${prefix}`);
|
|
417
|
+
};
|
|
418
|
+
var introspectionPlugin_default = fp(introspectionPlugin, { name: "arc-introspection" });
|
|
419
|
+
//#endregion
|
|
420
|
+
//#region src/registry/manifest.ts
|
|
421
|
+
/**
|
|
422
|
+
* Resource manifest for frontend code generators (v2.15.5).
|
|
423
|
+
*
|
|
424
|
+
* Every host that hand-rolls a `createCrudApi('foo')` helper on the frontend
|
|
425
|
+
* ends up rewriting the same shim for declarative actions: `postAction(id,
|
|
426
|
+
* name, body)` — and forgets to add a method for every NEW action declared
|
|
427
|
+
* on the resource. The OpenAI-team report flagged this in fajr's
|
|
428
|
+
* `invoices.js`, and now matches needs the same.
|
|
429
|
+
*
|
|
430
|
+
* Arc owns the resource definition, so arc can ship the metadata FE codegen
|
|
431
|
+
* needs in one canonical shape:
|
|
432
|
+
*
|
|
433
|
+
* {
|
|
434
|
+
* name: 'invoice',
|
|
435
|
+
* prefix: '/invoices',
|
|
436
|
+
* idField: '_id',
|
|
437
|
+
* crudOps: ['list', 'get', 'create', 'update', 'delete'],
|
|
438
|
+
* actions: [
|
|
439
|
+
* { name: 'recordPayment', mount: '/:id/action', requiresId: true, description: '...' },
|
|
440
|
+
* { name: 'propose', mount: '/action', requiresId: false, description: '...' },
|
|
441
|
+
* ],
|
|
442
|
+
* }
|
|
443
|
+
*
|
|
444
|
+
* Hosts feed this into their FE generator (or a runtime `createActionsApi`
|
|
445
|
+
* shim) and every action is wired automatically. New actions land on the FE
|
|
446
|
+
* the same moment they're declared on the resource — no parallel list to
|
|
447
|
+
* keep in sync.
|
|
448
|
+
*
|
|
449
|
+
* The helper is BE-side only (lives under `@classytic/arc/registry`) — arc
|
|
450
|
+
* stays a backend framework and doesn't ship FE runtime code. The host
|
|
451
|
+
* decides whether the manifest powers a fetch-based helper, a TanStack
|
|
452
|
+
* Query factory, an RPC client, or static `.d.ts` generation.
|
|
453
|
+
*
|
|
454
|
+
* @example BE → emit JSON at build time / on a route
|
|
455
|
+
* ```ts
|
|
456
|
+
* // Build-time codegen
|
|
457
|
+
* import { buildResourceManifest } from '@classytic/arc/registry';
|
|
458
|
+
* import { invoiceResource } from './invoice.resource.js';
|
|
459
|
+
* writeFileSync('./fe-gen/invoice.manifest.json',
|
|
460
|
+
* JSON.stringify(buildResourceManifest(invoiceResource)));
|
|
461
|
+
*
|
|
462
|
+
* // Runtime introspection endpoint
|
|
463
|
+
* fastify.get('/manifest/:name', async (req) => {
|
|
464
|
+
* const r = arc.registry.get(req.params.name);
|
|
465
|
+
* return r ? buildResourceManifestFromRegistry(r) : reply.code(404);
|
|
466
|
+
* });
|
|
467
|
+
* ```
|
|
468
|
+
*
|
|
469
|
+
* @example FE → generate fetch methods from the manifest
|
|
470
|
+
* ```ts
|
|
471
|
+
* import manifest from './fe-gen/invoice.manifest.json';
|
|
472
|
+
* import { createActionsApi } from './my-fe-shim.js'; // host-owned
|
|
473
|
+
*
|
|
474
|
+
* export const invoicesApi = {
|
|
475
|
+
* ...createCrudApi(manifest.prefix),
|
|
476
|
+
* ...createActionsApi(manifest.prefix, manifest.actions),
|
|
477
|
+
* // → invoicesApi.recordPayment(id, body)
|
|
478
|
+
* // → invoicesApi.propose(body) // id-less
|
|
479
|
+
* };
|
|
480
|
+
* ```
|
|
481
|
+
*/
|
|
482
|
+
/**
|
|
483
|
+
* Build a manifest from a `ResourceDefinition` (the value returned by
|
|
484
|
+
* `defineResource(...)`). Use at build time when you have direct access
|
|
485
|
+
* to the resource module — typically the simplest codegen path.
|
|
486
|
+
*/
|
|
487
|
+
function buildResourceManifest(resource) {
|
|
488
|
+
const disabled = new Set(resource.disabledRoutes ?? []);
|
|
489
|
+
const crudOps = resource.disableDefaultRoutes ? [] : CRUD_OPERATIONS.filter((op) => !disabled.has(op));
|
|
490
|
+
const actions = [];
|
|
491
|
+
for (const [name, entry] of Object.entries(resource.actions ?? {})) {
|
|
492
|
+
const requiresId = typeof entry === "function" ? true : entry.id !== false;
|
|
493
|
+
actions.push({
|
|
494
|
+
name,
|
|
495
|
+
mount: requiresId ? "/:id/action" : "/action",
|
|
496
|
+
requiresId,
|
|
497
|
+
...typeof entry !== "function" && entry.description ? { description: entry.description } : {}
|
|
498
|
+
});
|
|
499
|
+
}
|
|
500
|
+
const aggregations = [];
|
|
501
|
+
for (const [name, agg] of Object.entries(resource.aggregations ?? {})) aggregations.push({
|
|
502
|
+
name,
|
|
503
|
+
path: `${resource.prefix}/aggregations/${name}`,
|
|
504
|
+
...agg.summary ? { summary: agg.summary } : {},
|
|
505
|
+
...agg.description ? { description: agg.description } : {}
|
|
506
|
+
});
|
|
507
|
+
const customRoutes = [];
|
|
508
|
+
for (const route of resource.routes ?? []) customRoutes.push({
|
|
509
|
+
method: route.method,
|
|
510
|
+
path: `${resource.prefix}${route.path}`,
|
|
511
|
+
...route.operation ? { operation: route.operation } : {},
|
|
512
|
+
...route.summary ? { summary: route.summary } : {}
|
|
513
|
+
});
|
|
514
|
+
return {
|
|
515
|
+
name: resource.name,
|
|
516
|
+
displayName: resource.displayName,
|
|
517
|
+
prefix: resource.prefix,
|
|
518
|
+
idField: resource.idField ?? "_id",
|
|
519
|
+
updateMethod: resource.updateMethod ?? "PATCH",
|
|
520
|
+
crudOps,
|
|
521
|
+
actions,
|
|
522
|
+
aggregations,
|
|
523
|
+
customRoutes,
|
|
524
|
+
...resource.tenantField !== void 0 ? { tenantField: resource.tenantField } : {}
|
|
525
|
+
};
|
|
526
|
+
}
|
|
527
|
+
/**
|
|
528
|
+
* Build a manifest from a `RegistryEntry` — use when the resource is
|
|
529
|
+
* already registered (introspection endpoint, runtime audit script).
|
|
530
|
+
*
|
|
531
|
+
* Equivalent to {@link buildResourceManifest} but reads from the projected
|
|
532
|
+
* registry shape, so a host that doesn't have the original `defineResource`
|
|
533
|
+
* value in scope (e.g. an FE-gen sidecar that scrapes a running server's
|
|
534
|
+
* `/_resources` endpoint) gets the same output without dragging the resource
|
|
535
|
+
* module into the codegen path.
|
|
536
|
+
*/
|
|
537
|
+
function buildResourceManifestFromRegistry(entry) {
|
|
538
|
+
const disabled = new Set(entry.disabledRoutes ?? []);
|
|
539
|
+
const crudOps = entry.disableDefaultRoutes ? [] : CRUD_OPERATIONS.filter((op) => !disabled.has(op));
|
|
540
|
+
const actions = (entry.actions ?? []).map((a) => {
|
|
541
|
+
const requiresId = a.id !== false;
|
|
542
|
+
return {
|
|
543
|
+
name: a.name,
|
|
544
|
+
mount: requiresId ? "/:id/action" : "/action",
|
|
545
|
+
requiresId,
|
|
546
|
+
...a.description ? { description: a.description } : {}
|
|
547
|
+
};
|
|
548
|
+
});
|
|
549
|
+
const aggregations = (entry.aggregations ?? []).map((agg) => ({
|
|
550
|
+
name: agg.name,
|
|
551
|
+
path: `${entry.prefix}/aggregations/${agg.name}`,
|
|
552
|
+
...agg.summary ? { summary: agg.summary } : {},
|
|
553
|
+
...agg.description ? { description: agg.description } : {}
|
|
554
|
+
}));
|
|
555
|
+
const customRoutes = (entry.customRoutes ?? []).map((r) => ({
|
|
556
|
+
method: r.method,
|
|
557
|
+
path: `${entry.prefix}${r.path}`,
|
|
558
|
+
...r.operation ? { operation: r.operation } : {},
|
|
559
|
+
...r.summary ? { summary: r.summary } : {}
|
|
560
|
+
}));
|
|
561
|
+
const updateMethod = entry.updateMethod ?? "PATCH";
|
|
562
|
+
return {
|
|
563
|
+
name: entry.name,
|
|
564
|
+
displayName: entry.displayName ?? entry.name,
|
|
565
|
+
prefix: entry.prefix,
|
|
566
|
+
idField: "_id",
|
|
567
|
+
updateMethod,
|
|
568
|
+
crudOps,
|
|
569
|
+
actions,
|
|
570
|
+
aggregations,
|
|
571
|
+
customRoutes,
|
|
572
|
+
...entry.tenantField !== void 0 ? { tenantField: entry.tenantField } : {}
|
|
573
|
+
};
|
|
574
|
+
}
|
|
575
|
+
//#endregion
|
|
576
|
+
export { cascadeDeleteForOrganization as a, assertNoTenantData as c, introspectionPlugin_default as i, buildResourceManifestFromRegistry as n, getCascadingResources as o, introspectionPlugin as r, getCascadingResourcesWithMetadata as s, buildResourceManifest as t };
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { f as createError } from "./errors-j4aJm1Wg.mjs";
|
|
2
|
-
import { b as
|
|
2
|
+
import { b as isElevated, c as getOrgId, g as getUserId, n as PUBLIC_SCOPE, x as isMember } from "./types-Bi0r0vjG.mjs";
|
|
3
3
|
import { t as requestContext } from "./requestContext-SSaaTgW8.mjs";
|
|
4
|
-
import { I as evaluateAndApplyPermission, p as resolveEffectiveRoles, u as applyFieldReadPermissions } from "./permissions-
|
|
4
|
+
import { I as evaluateAndApplyPermission, p as resolveEffectiveRoles, u as applyFieldReadPermissions } from "./permissions-CTxMrreC.mjs";
|
|
5
5
|
import { t as getUserRoles } from "./types-D57iXYb8.mjs";
|
|
6
|
-
import { t as executePipeline } from "./pipe-
|
|
6
|
+
import { t as executePipeline } from "./pipe-DiCyvyPN.mjs";
|
|
7
7
|
import { isPaginatedResult, toCanonicalList } from "@classytic/repo-core/pagination";
|
|
8
8
|
//#region src/scope/projection.ts
|
|
9
9
|
/**
|
package/dist/scope/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { C as
|
|
2
|
-
import { i as elevationPlugin, n as ElevationOptions, r as _default, t as ElevationEvent } from "../elevation-
|
|
1
|
+
import { C as isMember, D as requireOrgId, E as requireClientId, O as requireTeamId, S as isElevated, T as isService, _ as getTenantFromRequest, a as getAncestorOrgIds, b as hasOrgAccess, c as getMandate, d as getOrgRoles, f as getRequestScope, g as getTeamId, h as getServiceScopes, i as RequestScope, k as requireUserId, l as getOrgContext, m as getScopeContextMap, n as Mandate, o as getClientId, p as getScopeContext, r as PUBLIC_SCOPE, s as getDPoPJkt, t as AUTHENTICATED_SCOPE, u as getOrgId, v as getUserId, w as isOrgInScope, x as isAuthenticated, y as getUserRoles } from "../types-DVfpSfx2.mjs";
|
|
2
|
+
import { i as elevationPlugin, n as ElevationOptions, r as _default, t as ElevationEvent } from "../elevation-0YBpa663.mjs";
|
|
3
3
|
import { FastifyReply, FastifyRequest } from "fastify";
|
|
4
4
|
|
|
5
5
|
//#region src/scope/rateLimitKey.d.ts
|
|
@@ -30,4 +30,4 @@ interface ResolveOrgFromHeaderOptions {
|
|
|
30
30
|
*/
|
|
31
31
|
declare function resolveOrgFromHeader(options: ResolveOrgFromHeaderOptions): (request: FastifyRequest, reply: FastifyReply) => Promise<void>;
|
|
32
32
|
//#endregion
|
|
33
|
-
export { AUTHENTICATED_SCOPE, type ElevationEvent, type ElevationOptions, type Mandate, PUBLIC_SCOPE, type RateLimitKeyContext, type RequestScope, type ResolveOrgFromHeaderOptions, type TenantKeyGeneratorOptions, createTenantKeyGenerator, _default as elevationPlugin, elevationPlugin as elevationPluginFn, getAncestorOrgIds, getClientId, getDPoPJkt, getMandate, getOrgContext, getOrgId, getOrgRoles, getRequestScope, getScopeContext, getScopeContextMap, getServiceScopes, getTeamId, getUserId, getUserRoles, hasOrgAccess, isAuthenticated, isElevated, isMember, isOrgInScope, isService, requireClientId, requireOrgId, requireTeamId, requireUserId, resolveOrgFromHeader };
|
|
33
|
+
export { AUTHENTICATED_SCOPE, type ElevationEvent, type ElevationOptions, type Mandate, PUBLIC_SCOPE, type RateLimitKeyContext, type RequestScope, type ResolveOrgFromHeaderOptions, type TenantKeyGeneratorOptions, createTenantKeyGenerator, _default as elevationPlugin, elevationPlugin as elevationPluginFn, getAncestorOrgIds, getClientId, getDPoPJkt, getMandate, getOrgContext, getOrgId, getOrgRoles, getRequestScope, getScopeContext, getScopeContextMap, getServiceScopes, getTeamId, getTenantFromRequest, getUserId, getUserRoles, hasOrgAccess, isAuthenticated, isElevated, isMember, isOrgInScope, isService, requireClientId, requireOrgId, requireTeamId, requireUserId, resolveOrgFromHeader };
|
package/dist/scope/index.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { C as
|
|
1
|
+
import { C as isService, D as requireUserId, E as requireTeamId, S as isOrgInScope, T as requireOrgId, _ as getUserRoles, a as getDPoPJkt, b as isElevated, c as getOrgId, d as getScopeContext, f as getScopeContextMap, g as getUserId, h as getTenantFromRequest, i as getClientId, l as getOrgRoles, m as getTeamId, n as PUBLIC_SCOPE, o as getMandate, p as getServiceScopes, r as getAncestorOrgIds, s as getOrgContext, t as AUTHENTICATED_SCOPE, u as getRequestScope, v as hasOrgAccess, w as requireClientId, x as isMember, y as isAuthenticated } from "../types-Bi0r0vjG.mjs";
|
|
2
2
|
import { n as normalizeRoles } from "../types-D57iXYb8.mjs";
|
|
3
|
-
import { n as elevation_default, t as elevationPlugin } from "../elevation-
|
|
3
|
+
import { n as elevation_default, t as elevationPlugin } from "../elevation-Dci0AYLT.mjs";
|
|
4
4
|
//#region src/scope/rateLimitKey.ts
|
|
5
5
|
function createTenantKeyGenerator(opts) {
|
|
6
6
|
if (opts?.strategy) return opts.strategy;
|
|
@@ -76,4 +76,4 @@ function resolveOrgFromHeader(options) {
|
|
|
76
76
|
};
|
|
77
77
|
}
|
|
78
78
|
//#endregion
|
|
79
|
-
export { AUTHENTICATED_SCOPE, PUBLIC_SCOPE, createTenantKeyGenerator, elevation_default as elevationPlugin, elevationPlugin as elevationPluginFn, getAncestorOrgIds, getClientId, getDPoPJkt, getMandate, getOrgContext, getOrgId, getOrgRoles, getRequestScope, getScopeContext, getScopeContextMap, getServiceScopes, getTeamId, getUserId, getUserRoles, hasOrgAccess, isAuthenticated, isElevated, isMember, isOrgInScope, isService, requireClientId, requireOrgId, requireTeamId, requireUserId, resolveOrgFromHeader };
|
|
79
|
+
export { AUTHENTICATED_SCOPE, PUBLIC_SCOPE, createTenantKeyGenerator, elevation_default as elevationPlugin, elevationPlugin as elevationPluginFn, getAncestorOrgIds, getClientId, getDPoPJkt, getMandate, getOrgContext, getOrgId, getOrgRoles, getRequestScope, getScopeContext, getScopeContextMap, getServiceScopes, getTeamId, getTenantFromRequest, getUserId, getUserRoles, hasOrgAccess, isAuthenticated, isElevated, isMember, isOrgInScope, isService, requireClientId, requireOrgId, requireTeamId, requireUserId, resolveOrgFromHeader };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { t as __exportAll } from "./chunk-BpYLSNr0.mjs";
|
|
2
2
|
import { arcLog } from "./logger/index.mjs";
|
|
3
|
-
import { c as getOrgId, n as PUBLIC_SCOPE } from "./types-
|
|
3
|
+
import { c as getOrgId, n as PUBLIC_SCOPE } from "./types-Bi0r0vjG.mjs";
|
|
4
4
|
import fp from "fastify-plugin";
|
|
5
5
|
//#region src/plugins/sse.ts
|
|
6
6
|
var sse_exports = /* @__PURE__ */ __exportAll({
|
package/dist/testing/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { d as ResourceLike, r as CreateAppOptions } from "../types-
|
|
1
|
+
import { Kt as AnyRecord, V as ResourceDefinition } from "../index-CkW0flkU.mjs";
|
|
2
|
+
import { d as ResourceLike, r as CreateAppOptions } from "../types-D-fYtKjb.mjs";
|
|
3
3
|
import { StorageContractSetup, StorageContractSetupResult, runStorageContract } from "./storageContract.mjs";
|
|
4
4
|
import { FastifyInstance, FastifyServerOptions } from "fastify";
|
|
5
5
|
import { Mock } from "vitest";
|