@classytic/arc 2.15.3 → 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 -3036
- 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.d.mts +71 -2
- package/dist/integrations/streamline.mjs +81 -8
- 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 +22 -29
- package/skills/arc/SKILL.md +299 -689
- 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
package/README.md
CHANGED
package/bin/arc.js
CHANGED
|
@@ -292,6 +292,16 @@ function parseInitOptions(rawArgs) {
|
|
|
292
292
|
opts.edge = true;
|
|
293
293
|
break;
|
|
294
294
|
|
|
295
|
+
case '--docker':
|
|
296
|
+
// 2.16 — Docker scaffolding is opt-in (frameworks don't dictate
|
|
297
|
+
// deployment). `--no-docker` skips even the interactive prompt.
|
|
298
|
+
opts.docker = true;
|
|
299
|
+
break;
|
|
300
|
+
|
|
301
|
+
case '--no-docker':
|
|
302
|
+
opts.docker = false;
|
|
303
|
+
break;
|
|
304
|
+
|
|
295
305
|
case '--skip-install':
|
|
296
306
|
opts.skipInstall = true;
|
|
297
307
|
break;
|
|
@@ -342,6 +352,8 @@ INIT OPTIONS
|
|
|
342
352
|
--ts, --typescript Generate TypeScript (default)
|
|
343
353
|
--js, --javascript Generate JavaScript
|
|
344
354
|
--edge, --serverless Target Edge/Serverless environments
|
|
355
|
+
--docker Emit Dockerfile + docker-compose.yml (opt-in, 2.16)
|
|
356
|
+
--no-docker Skip Docker scaffolding even in interactive mode
|
|
345
357
|
--force, -f Overwrite existing directory
|
|
346
358
|
--skip-install Skip npm install after scaffolding
|
|
347
359
|
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { h as SYSTEM_FIELDS, o as DEFAULT_TENANT_FIELD } from "./constants-
|
|
1
|
+
import { h as SYSTEM_FIELDS, o as DEFAULT_TENANT_FIELD } from "./constants-TrJVIJl0.mjs";
|
|
2
2
|
import { arcLog } from "./logger/index.mjs";
|
|
3
3
|
import { d as createDomainError, f as createError, i as NotFoundError, r as ForbiddenError } from "./errors-j4aJm1Wg.mjs";
|
|
4
|
-
import { b as
|
|
5
|
-
import { N as simpleEqualityMatcher, _ as ArcQueryParser, r as scheduleBackground, t as getUserId } from "./utils-
|
|
6
|
-
import { d as applyFieldWritePermissions, p as resolveEffectiveRoles } from "./permissions-
|
|
4
|
+
import { b as isElevated, c as getOrgId, n as PUBLIC_SCOPE, x as isMember } from "./types-Bi0r0vjG.mjs";
|
|
5
|
+
import { N as simpleEqualityMatcher, _ as ArcQueryParser, r as scheduleBackground, t as getUserId } from "./utils-DC5ycPfr.mjs";
|
|
6
|
+
import { d as applyFieldWritePermissions, p as resolveEffectiveRoles } from "./permissions-CTxMrreC.mjs";
|
|
7
7
|
import { t as getUserRoles } from "./types-D57iXYb8.mjs";
|
|
8
|
-
import { t as buildQueryKey } from "./keys-
|
|
8
|
+
import { t as buildQueryKey } from "./keys-DopsCuyQ.mjs";
|
|
9
9
|
//#region src/core/AccessControl.ts
|
|
10
10
|
const log = arcLog("access-control");
|
|
11
11
|
var AccessControl = class {
|
|
@@ -892,9 +892,10 @@ var BaseCrudController = class {
|
|
|
892
892
|
const cacheConfig = this.resolveCacheConfig("list");
|
|
893
893
|
const qc = req.server?.queryCache;
|
|
894
894
|
if (!cacheConfig || !qc) return null;
|
|
895
|
-
const
|
|
895
|
+
const resourceName = this.resourceName ?? "_unnamed";
|
|
896
|
+
const version = await qc.getResourceVersion(resourceName);
|
|
896
897
|
const { userId, orgId } = this.cacheScope(req);
|
|
897
|
-
const key = buildQueryKey(
|
|
898
|
+
const key = buildQueryKey(resourceName, "list", version, options, userId, orgId);
|
|
898
899
|
const { data, status } = await qc.get(key);
|
|
899
900
|
if (status === "fresh") return this.cacheResponse(data, "HIT");
|
|
900
901
|
if (status === "stale") {
|
|
@@ -912,9 +913,10 @@ var BaseCrudController = class {
|
|
|
912
913
|
const cacheConfig = this.resolveCacheConfig("byId");
|
|
913
914
|
const qc = req.server?.queryCache;
|
|
914
915
|
if (!cacheConfig || !qc) return null;
|
|
915
|
-
const
|
|
916
|
+
const resourceName = this.resourceName ?? "_unnamed";
|
|
917
|
+
const version = await qc.getResourceVersion(resourceName);
|
|
916
918
|
const { userId, orgId } = this.cacheScope(req);
|
|
917
|
-
const key = buildQueryKey(
|
|
919
|
+
const key = buildQueryKey(resourceName, "get", version, {
|
|
918
920
|
id,
|
|
919
921
|
...options
|
|
920
922
|
}, userId, orgId);
|
|
@@ -1149,6 +1151,45 @@ var BaseCrudController = class {
|
|
|
1149
1151
|
}
|
|
1150
1152
|
};
|
|
1151
1153
|
//#endregion
|
|
1154
|
+
//#region src/utils/repoFeature.ts
|
|
1155
|
+
/**
|
|
1156
|
+
* Repository feature-detection helper for arc mixins.
|
|
1157
|
+
*
|
|
1158
|
+
* Arc's `RepositoryLike` is the minimum contract every adapter
|
|
1159
|
+
* implements (`getById`, `getOne`, `create`, `update`, `delete`,
|
|
1160
|
+
* `find`). Beyond that, kits MAY expose specialized methods —
|
|
1161
|
+
* `createMany`, `updateMany`, `deleteMany`, `getTree`, `getBySlug`,
|
|
1162
|
+
* `getDeleted`, `restore`, etc. — that arc mixins detect at call
|
|
1163
|
+
* time and surface as 501 when missing.
|
|
1164
|
+
*
|
|
1165
|
+
* Every mixin used to spell out its own narrowing cast inline:
|
|
1166
|
+
*
|
|
1167
|
+
* ```ts
|
|
1168
|
+
* const repo = this.repository as unknown as {
|
|
1169
|
+
* getTree?: (options?: unknown) => Promise<AnyRecord[]>;
|
|
1170
|
+
* };
|
|
1171
|
+
* ```
|
|
1172
|
+
*
|
|
1173
|
+
* Centralizing the cast keeps every feature-detection site reading
|
|
1174
|
+
* the same shape. The helper does not intersect with `RepositoryLike`
|
|
1175
|
+
* on purpose: some standard methods (`createMany`, `updateMany`) live
|
|
1176
|
+
* on `StandardRepo` with kit-generic signatures, and intersecting
|
|
1177
|
+
* would force the caller's narrowed signature to merge with the wider
|
|
1178
|
+
* default, producing mismatched return types. Declare every method
|
|
1179
|
+
* you need (base or feature) in `TFeatures`.
|
|
1180
|
+
*
|
|
1181
|
+
* @example
|
|
1182
|
+
* ```ts
|
|
1183
|
+
* const repo = withRepoFeatures<{
|
|
1184
|
+
* createMany?: (items: unknown[], options?: unknown) => Promise<AnyRecord[]>;
|
|
1185
|
+
* }>(this.repository);
|
|
1186
|
+
* if (!repo.createMany) throw createError(501, "...");
|
|
1187
|
+
* ```
|
|
1188
|
+
*/
|
|
1189
|
+
function withRepoFeatures(repo) {
|
|
1190
|
+
return repo;
|
|
1191
|
+
}
|
|
1192
|
+
//#endregion
|
|
1152
1193
|
//#region src/core/mixins/bulk.ts
|
|
1153
1194
|
/**
|
|
1154
1195
|
* BulkMixin — `bulkCreate` / `bulkUpdate` / `bulkDelete` endpoints.
|
|
@@ -1170,7 +1211,7 @@ var BaseCrudController = class {
|
|
|
1170
1211
|
function BulkMixin(Base) {
|
|
1171
1212
|
return class BulkController extends Base {
|
|
1172
1213
|
async bulkCreate(req) {
|
|
1173
|
-
const repo = this.repository;
|
|
1214
|
+
const repo = withRepoFeatures(this.repository);
|
|
1174
1215
|
if (!repo.createMany) throw createError(501, "Repository does not support createMany");
|
|
1175
1216
|
const rawItems = req.body?.items;
|
|
1176
1217
|
if (!Array.isArray(rawItems) || rawItems.length === 0) throw createError(400, "Bulk create requires a non-empty items array");
|
|
@@ -1293,7 +1334,7 @@ function BulkMixin(Base) {
|
|
|
1293
1334
|
};
|
|
1294
1335
|
}
|
|
1295
1336
|
async bulkUpdate(req) {
|
|
1296
|
-
const repo = this.repository;
|
|
1337
|
+
const repo = withRepoFeatures(this.repository);
|
|
1297
1338
|
if (!repo.updateMany) throw createError(501, "Repository does not support updateMany");
|
|
1298
1339
|
const body = req.body;
|
|
1299
1340
|
if (!body.filter || Object.keys(body.filter).length === 0) throw createError(400, "Bulk update requires a non-empty filter");
|
|
@@ -1332,7 +1373,7 @@ function BulkMixin(Base) {
|
|
|
1332
1373
|
* fetch loop. Per-doc lifecycle hooks do NOT fire.
|
|
1333
1374
|
*/
|
|
1334
1375
|
async bulkDelete(req) {
|
|
1335
|
-
const repo = this.repository;
|
|
1376
|
+
const repo = withRepoFeatures(this.repository);
|
|
1336
1377
|
if (!repo.deleteMany) throw createError(501, "Repository does not support deleteMany");
|
|
1337
1378
|
const body = req.body;
|
|
1338
1379
|
let userFilter;
|
|
@@ -1368,7 +1409,7 @@ function SlugMixin(Base) {
|
|
|
1368
1409
|
...this.queryResolver.resolve(req, this.meta(req)),
|
|
1369
1410
|
...this.tenantRepoOptions(req)
|
|
1370
1411
|
};
|
|
1371
|
-
const repo = this.repository;
|
|
1412
|
+
const repo = withRepoFeatures(this.repository);
|
|
1372
1413
|
let item = null;
|
|
1373
1414
|
if (repo.getBySlug) item = await repo.getBySlug(slug, options);
|
|
1374
1415
|
else if (repo.getOne) {
|
|
@@ -1399,7 +1440,7 @@ function SlugMixin(Base) {
|
|
|
1399
1440
|
function SoftDeleteMixin(Base) {
|
|
1400
1441
|
return class SoftDeleteController extends Base {
|
|
1401
1442
|
async getDeleted(req) {
|
|
1402
|
-
const repo = this.repository;
|
|
1443
|
+
const repo = withRepoFeatures(this.repository);
|
|
1403
1444
|
if (!repo.getDeleted) throw createError(501, "Soft delete not implemented");
|
|
1404
1445
|
const parsed = this.queryResolver.resolve(req, this.meta(req));
|
|
1405
1446
|
return {
|
|
@@ -1408,8 +1449,9 @@ function SoftDeleteMixin(Base) {
|
|
|
1408
1449
|
};
|
|
1409
1450
|
}
|
|
1410
1451
|
async restore(req) {
|
|
1411
|
-
const repo = this.repository;
|
|
1412
|
-
|
|
1452
|
+
const repo = withRepoFeatures(this.repository);
|
|
1453
|
+
const restore = repo.restore;
|
|
1454
|
+
if (!restore) throw createError(501, "Restore not implemented");
|
|
1413
1455
|
const id = req.params.id;
|
|
1414
1456
|
if (!id) throw createError(400, "ID parameter is required");
|
|
1415
1457
|
const existing = await this.accessControl.fetchWithAccessControl(id, req, repo, { includeDeleted: true });
|
|
@@ -1431,7 +1473,7 @@ function SoftDeleteMixin(Base) {
|
|
|
1431
1473
|
message: err.message
|
|
1432
1474
|
});
|
|
1433
1475
|
}
|
|
1434
|
-
const repoRestore = () =>
|
|
1476
|
+
const repoRestore = () => restore(repoId);
|
|
1435
1477
|
let item;
|
|
1436
1478
|
if (hooks && this.resourceName) item = await hooks.executeAround(this.resourceName, "restore", existing, repoRestore, {
|
|
1437
1479
|
user,
|
|
@@ -1458,7 +1500,7 @@ function SoftDeleteMixin(Base) {
|
|
|
1458
1500
|
function TreeMixin(Base) {
|
|
1459
1501
|
return class TreeController extends Base {
|
|
1460
1502
|
async getTree(req) {
|
|
1461
|
-
const repo = this.repository;
|
|
1503
|
+
const repo = withRepoFeatures(this.repository);
|
|
1462
1504
|
if (!repo.getTree) throw createError(501, "Tree structure not implemented");
|
|
1463
1505
|
const options = this.queryResolver.resolve(req, this.meta(req));
|
|
1464
1506
|
return {
|
|
@@ -1467,7 +1509,7 @@ function TreeMixin(Base) {
|
|
|
1467
1509
|
};
|
|
1468
1510
|
}
|
|
1469
1511
|
async getChildren(req) {
|
|
1470
|
-
const repo = this.repository;
|
|
1512
|
+
const repo = withRepoFeatures(this.repository);
|
|
1471
1513
|
if (!repo.getChildren) throw createError(501, "Tree structure not implemented");
|
|
1472
1514
|
const parentField = this._presetFields.parentField ?? "parent";
|
|
1473
1515
|
const parentId = req.params[parentField] ?? req.params.parent ?? req.params.id;
|
|
@@ -36,6 +36,7 @@ var HookSystem = class {
|
|
|
36
36
|
finalPriority = resourceOrOptions.priority ?? 10;
|
|
37
37
|
dependsOn = resourceOrOptions.dependsOn;
|
|
38
38
|
} else {
|
|
39
|
+
if (operation === void 0 || phase === void 0 || handler === void 0) throw new TypeError("[Arc] HookSystem.register(): positional form requires (resource, operation, phase, handler). Pass an object literal for the named form.");
|
|
39
40
|
resource = resourceOrOptions;
|
|
40
41
|
finalOperation = operation;
|
|
41
42
|
finalPhase = phase;
|
|
@@ -53,8 +54,9 @@ var HookSystem = class {
|
|
|
53
54
|
priority: finalPriority,
|
|
54
55
|
dependsOn
|
|
55
56
|
};
|
|
56
|
-
const hooks = this.hooks.get(key);
|
|
57
|
+
const hooks = this.hooks.get(key) ?? [];
|
|
57
58
|
hooks.push(registration);
|
|
59
|
+
if (!this.hooks.has(key)) this.hooks.set(key, hooks);
|
|
58
60
|
hooks.sort((a, b) => a.priority - b.priority);
|
|
59
61
|
return () => {
|
|
60
62
|
const idx = hooks.indexOf(registration);
|
|
@@ -95,6 +97,7 @@ var HookSystem = class {
|
|
|
95
97
|
const next = async () => {
|
|
96
98
|
if (index < allHooks.length) {
|
|
97
99
|
const hook = allHooks[index++];
|
|
100
|
+
if (!hook) return execute();
|
|
98
101
|
const ctx = {
|
|
99
102
|
resource,
|
|
100
103
|
operation,
|
|
@@ -195,15 +198,16 @@ var HookSystem = class {
|
|
|
195
198
|
}
|
|
196
199
|
const queue = [];
|
|
197
200
|
const result = [];
|
|
198
|
-
for (const hook of hooks) if (inDegree.get(hook) === 0) queue.push(hook);
|
|
201
|
+
for (const hook of hooks) if ((inDegree.get(hook) ?? 0) === 0) queue.push(hook);
|
|
199
202
|
queue.sort((a, b) => a.priority - b.priority);
|
|
200
203
|
while (queue.length > 0) {
|
|
201
204
|
const current = queue.shift();
|
|
205
|
+
if (!current) break;
|
|
202
206
|
result.push(current);
|
|
203
207
|
if (current.name && dependents.has(current.name)) {
|
|
204
|
-
const deps = dependents.get(current.name);
|
|
208
|
+
const deps = dependents.get(current.name) ?? [];
|
|
205
209
|
for (const dep of deps) {
|
|
206
|
-
const newDegree = inDegree.get(dep) - 1;
|
|
210
|
+
const newDegree = (inDegree.get(dep) ?? 0) - 1;
|
|
207
211
|
inDegree.set(dep, newDegree);
|
|
208
212
|
if (newDegree === 0) {
|
|
209
213
|
const insertIdx = queue.findIndex((q) => q.priority > dep.priority);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import "./constants-
|
|
1
|
+
import "./constants-TrJVIJl0.mjs";
|
|
2
2
|
//#region src/registry/ResourceRegistry.ts
|
|
3
3
|
/**
|
|
4
4
|
* Resource Registry
|
|
@@ -7,12 +7,32 @@ import "./constants-Cxde4rpC.mjs";
|
|
|
7
7
|
*/
|
|
8
8
|
var ResourceRegistry = class {
|
|
9
9
|
_resources;
|
|
10
|
+
/**
|
|
11
|
+
* Parallel map of full `DataAdapter` instances. The public `RegistryEntry.adapter`
|
|
12
|
+
* field is intentionally narrowed to `{ type, name }` so introspection can be
|
|
13
|
+
* JSON-serialized — but cascade / cleanup / migration helpers need the live
|
|
14
|
+
* adapter (and its `.repository`) to do real work. Stored here, behind
|
|
15
|
+
* `getAdapter(name)`, so the public metadata stays serializable and the
|
|
16
|
+
* runtime-only handle is still reachable.
|
|
17
|
+
*/
|
|
18
|
+
_adapters;
|
|
10
19
|
_frozen;
|
|
11
20
|
constructor() {
|
|
12
21
|
this._resources = /* @__PURE__ */ new Map();
|
|
22
|
+
this._adapters = /* @__PURE__ */ new Map();
|
|
13
23
|
this._frozen = false;
|
|
14
24
|
}
|
|
15
25
|
/**
|
|
26
|
+
* Get the live `DataAdapter` for a registered resource — the handle whose
|
|
27
|
+
* `.repository` cascade / cleanup helpers actually call. Returns `undefined`
|
|
28
|
+
* when the resource is unknown OR was registered without an adapter
|
|
29
|
+
* (service-only resources). Typed loosely (`unknown`-shaped fields) so
|
|
30
|
+
* consumers cast to their kit's adapter type at the use site.
|
|
31
|
+
*/
|
|
32
|
+
getAdapter(name) {
|
|
33
|
+
return this._adapters.get(name);
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
16
36
|
* Register a resource
|
|
17
37
|
*/
|
|
18
38
|
register(resource, options = {}) {
|
|
@@ -34,7 +54,7 @@ var ResourceRegistry = class {
|
|
|
34
54
|
customRoutes: (resource.routes ?? []).map((r) => ({
|
|
35
55
|
method: r.method,
|
|
36
56
|
path: r.path,
|
|
37
|
-
handler: typeof r.handler === "string" ? r.handler : r.handler.name || "anonymous",
|
|
57
|
+
handler: typeof r.handler === "string" ? r.handler : typeof r.handler === "function" ? r.handler.name || "anonymous" : "anonymous",
|
|
38
58
|
operation: r.operation,
|
|
39
59
|
summary: r.summary,
|
|
40
60
|
description: r.description,
|
|
@@ -45,6 +65,8 @@ var ResourceRegistry = class {
|
|
|
45
65
|
events: Object.keys(resource.events ?? {}),
|
|
46
66
|
registeredAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
47
67
|
disableDefaultRoutes: resource.disableDefaultRoutes,
|
|
68
|
+
tenantField: resource.tenantField,
|
|
69
|
+
resolvedTenantPurge: resource.resolvedTenantPurge,
|
|
48
70
|
updateMethod: resource.updateMethod,
|
|
49
71
|
disabledRoutes: resource.disabledRoutes ? [...resource.disabledRoutes] : void 0,
|
|
50
72
|
openApiSchemas: options.openApiSchemas,
|
|
@@ -60,7 +82,8 @@ var ResourceRegistry = class {
|
|
|
60
82
|
description: entry.description,
|
|
61
83
|
schema: entry.schema,
|
|
62
84
|
permissions: entry.permissions,
|
|
63
|
-
mcp: entry.mcp
|
|
85
|
+
mcp: entry.mcp,
|
|
86
|
+
id: entry.id
|
|
64
87
|
};
|
|
65
88
|
}) : void 0,
|
|
66
89
|
aggregations: resource.aggregations ? Object.entries(resource.aggregations).map(([name, entry]) => ({
|
|
@@ -78,6 +101,7 @@ var ResourceRegistry = class {
|
|
|
78
101
|
plugin: resource.toPlugin()
|
|
79
102
|
};
|
|
80
103
|
this._resources.set(resource.name, entry);
|
|
104
|
+
if (resource.adapter) this._adapters.set(resource.name, resource.adapter);
|
|
81
105
|
return this;
|
|
82
106
|
}
|
|
83
107
|
/**
|
|
@@ -216,11 +240,20 @@ var ResourceRegistry = class {
|
|
|
216
240
|
handler: typeof ar.handler === "string" ? ar.handler : void 0,
|
|
217
241
|
summary: ar.summary
|
|
218
242
|
});
|
|
219
|
-
if (r.actions && r.actions.length > 0)
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
243
|
+
if (r.actions && r.actions.length > 0) {
|
|
244
|
+
const hasIdBound = r.actions.some((a) => a.id !== false);
|
|
245
|
+
const hasIdLess = r.actions.some((a) => a.id === false);
|
|
246
|
+
if (hasIdBound) routes.push({
|
|
247
|
+
method: "POST",
|
|
248
|
+
path: `${r.prefix}/:id/action`,
|
|
249
|
+
operation: "action"
|
|
250
|
+
});
|
|
251
|
+
if (hasIdLess) routes.push({
|
|
252
|
+
method: "POST",
|
|
253
|
+
path: `${r.prefix}/action`,
|
|
254
|
+
operation: "action"
|
|
255
|
+
});
|
|
256
|
+
}
|
|
224
257
|
for (const agg of r.aggregations ?? []) routes.push({
|
|
225
258
|
method: "GET",
|
|
226
259
|
path: `${r.prefix}/aggregations/${agg.name}`,
|
|
@@ -248,10 +281,20 @@ var ResourceRegistry = class {
|
|
|
248
281
|
this._frozen = false;
|
|
249
282
|
}
|
|
250
283
|
/**
|
|
251
|
-
* Reset registry — clear all resources
|
|
284
|
+
* Reset registry — clear all resources, drop their parallel live-adapter
|
|
285
|
+
* handles, and unfreeze.
|
|
286
|
+
*
|
|
287
|
+
* The `_adapters` map (added in 2.15.5 so cascade / cleanup helpers can
|
|
288
|
+
* reach `adapter.repository` after registration) must clear alongside
|
|
289
|
+
* `_resources`. Pre-2.16 this method dropped the public entries but
|
|
290
|
+
* left the live adapters dangling, so a test that called `reset()`
|
|
291
|
+
* between cases would see ghost adapters and either (a) leak a live
|
|
292
|
+
* Mongo connection across describe blocks or (b) `cascadeDeleteForOrganization`
|
|
293
|
+
* iterate over a defunct registry entry. Clear both halves together.
|
|
252
294
|
*/
|
|
253
295
|
reset() {
|
|
254
296
|
this._resources.clear();
|
|
297
|
+
this._adapters.clear();
|
|
255
298
|
this._frozen = false;
|
|
256
299
|
}
|
|
257
300
|
/** @internal Alias for unfreeze() */
|
package/dist/audit/index.d.mts
CHANGED
package/dist/audit/index.mjs
CHANGED
|
@@ -144,8 +144,10 @@ var MemoryAuditStore = class {
|
|
|
144
144
|
const actions = Array.isArray(options.action) ? options.action : [options.action];
|
|
145
145
|
results = results.filter((e) => actions.includes(e.action));
|
|
146
146
|
}
|
|
147
|
-
|
|
148
|
-
if (
|
|
147
|
+
const from = options.from;
|
|
148
|
+
if (from !== void 0) results = results.filter((e) => e.timestamp >= from);
|
|
149
|
+
const to = options.to;
|
|
150
|
+
if (to !== void 0) results = results.filter((e) => e.timestamp <= to);
|
|
149
151
|
const offset = options.offset ?? 0;
|
|
150
152
|
const limit = options.limit ?? 100;
|
|
151
153
|
results = results.slice(offset, offset + limit);
|
package/dist/auth/index.d.mts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { c as PermissionCheck } from "../fields-
|
|
3
|
-
import { t as ExternalOpenApiPaths } from "../externalPaths-
|
|
4
|
-
import { a as SessionManagerOptions, c as createSessionManager, i as SessionData, n as MemorySessionStoreOptions, o as SessionManagerResult, r as SessionCookieOptions, s as SessionStore, t as MemorySessionStore } from "../sessionManager-
|
|
1
|
+
import { Bt as AuthHelpers, Vt as AuthPluginOptions } from "../index-CkW0flkU.mjs";
|
|
2
|
+
import { c as PermissionCheck } from "../fields-Anj0xdih.mjs";
|
|
3
|
+
import { t as ExternalOpenApiPaths } from "../externalPaths-DFg-2KTp.mjs";
|
|
4
|
+
import { a as SessionManagerOptions, c as createSessionManager, i as SessionData, n as MemorySessionStoreOptions, o as SessionManagerResult, r as SessionCookieOptions, s as SessionStore, t as MemorySessionStore } from "../sessionManager-BqFegc0W.mjs";
|
|
5
5
|
import { FastifyPluginAsync, FastifyReply as FastifyReply$1, FastifyRequest } from "fastify";
|
|
6
6
|
|
|
7
7
|
//#region src/auth/authPlugin.d.ts
|
package/dist/auth/index.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { d as createDomainError, l as UnauthorizedError, p as isArcError, t as ArcError } from "../errors-j4aJm1Wg.mjs";
|
|
2
|
-
import { _ as requireOrgMembership, v as requireOrgRole, x as requireTeamMembership } from "../permissions-
|
|
2
|
+
import { _ as requireOrgMembership, v as requireOrgRole, x as requireTeamMembership } from "../permissions-CTxMrreC.mjs";
|
|
3
3
|
import { n as normalizeRoles, t as getUserRoles } from "../types-D57iXYb8.mjs";
|
|
4
|
-
import { n as extractBetterAuthOpenApi } from "../betterAuthOpenApi
|
|
4
|
+
import { n as extractBetterAuthOpenApi } from "../betterAuthOpenApi-ClWxaceA.mjs";
|
|
5
5
|
import { createHmac, randomUUID, timingSafeEqual } from "node:crypto";
|
|
6
6
|
import fp from "fastify-plugin";
|
|
7
7
|
//#region src/auth/authPlugin.ts
|
|
@@ -13,7 +13,7 @@ function parseExpiresIn(input, defaultValue) {
|
|
|
13
13
|
if (/^\d+$/.test(input)) return parseInt(input, 10);
|
|
14
14
|
const match = /^(\d+)\s*([smhd])$/i.exec(input);
|
|
15
15
|
if (!match) return defaultValue;
|
|
16
|
-
return parseInt(match[1], 10) * ({
|
|
16
|
+
return parseInt(match[1] ?? "0", 10) * ({
|
|
17
17
|
s: 1,
|
|
18
18
|
m: 60,
|
|
19
19
|
h: 3600,
|
|
@@ -611,7 +611,7 @@ function createBetterAuthAdapter(options) {
|
|
|
611
611
|
if (!fastify.hasDecorator("authenticate")) fastify.decorate("authenticate", authenticate);
|
|
612
612
|
if (!fastify.hasDecorator("optionalAuthenticate")) fastify.decorate("optionalAuthenticate", optionalAuthenticate);
|
|
613
613
|
if (!extractedOpenApi && openapiOpt !== false && auth.api && typeof auth.api === "object") {
|
|
614
|
-
const { extractBetterAuthOpenApi } = await import("../betterAuthOpenApi
|
|
614
|
+
const { extractBetterAuthOpenApi } = await import("../betterAuthOpenApi-ClWxaceA.mjs").then((n) => n.t);
|
|
615
615
|
extractedOpenApi = extractBetterAuthOpenApi(auth.api, {
|
|
616
616
|
basePath,
|
|
617
617
|
userFields
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { i as SessionData, s as SessionStore } from "../sessionManager-
|
|
1
|
+
import { i as SessionData, s as SessionStore } from "../sessionManager-BqFegc0W.mjs";
|
|
2
2
|
|
|
3
3
|
//#region src/auth/redis-session.d.ts
|
|
4
4
|
/** Minimal Redis client interface — compatible with ioredis */
|
|
@@ -23,12 +23,16 @@ function toOpenApiPath(path) {
|
|
|
23
23
|
function extractPathParams(path) {
|
|
24
24
|
const params = [];
|
|
25
25
|
const matches = path.matchAll(/:(\w+)/g);
|
|
26
|
-
for (const match of matches)
|
|
27
|
-
name
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
26
|
+
for (const match of matches) {
|
|
27
|
+
const name = match[1];
|
|
28
|
+
if (!name) continue;
|
|
29
|
+
params.push({
|
|
30
|
+
name,
|
|
31
|
+
in: "path",
|
|
32
|
+
required: true,
|
|
33
|
+
schema: { type: "string" }
|
|
34
|
+
});
|
|
35
|
+
}
|
|
32
36
|
return params;
|
|
33
37
|
}
|
|
34
38
|
/**
|