@classytic/arc 2.25.0 → 2.30.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 +2 -0
- package/dist/{BaseController--M8EMzt0.mjs → BaseController-B5DwufbB.mjs} +15 -11
- package/dist/EventTransport-C6YA8xY9.mjs +103 -0
- package/dist/{HookSystem-C1vB_Gc9.mjs → HookSystem-WkIougo6.mjs} +15 -35
- package/dist/applyPermissionResult-Cf6zUCvb.mjs +257 -0
- package/dist/audit/index.d.mts +2 -2
- package/dist/audit/index.mjs +1 -1
- package/dist/auth/index.d.mts +2 -2
- package/dist/auth/index.mjs +1 -1
- package/dist/{buildHandler-Dtwd8fdz.mjs → buildHandler-BYS3Vkfz.mjs} +32 -6
- package/dist/cleanup/index.d.mts +313 -0
- package/dist/cleanup/index.mjs +1267 -0
- package/dist/cli/commands/describe.d.mts +1 -1
- package/dist/cli/commands/docs.mjs +1 -1
- package/dist/core/index.d.mts +2 -2
- package/dist/core/index.mjs +5 -3
- package/dist/core-poalJLvp.mjs +175 -0
- package/dist/{createActionRouter-B6GvAuAX.mjs → createActionRouter-BxuywVRB.mjs} +2 -2
- package/dist/{createAggregationRouter-wzwFa_dW.mjs → createAggregationRouter-BiY-lGdY.mjs} +7 -20
- package/dist/{preloadResources-DfyngIN9.mjs → createApp-CXrKFciH.mjs} +25 -76
- package/dist/defineAggregation-ufWjUwml.mjs +47 -0
- package/dist/{core-EUNu0u07.mjs → defineResource-CxWqDCAy.mjs} +36 -261
- package/dist/docs/index.d.mts +1 -1
- package/dist/docs/index.mjs +1 -1
- package/dist/{eventPlugin-DrQ_FesK.mjs → eventPlugin-D2NNR4py.mjs} +2 -103
- package/dist/events/index.d.mts +28 -2
- package/dist/events/index.mjs +24 -343
- package/dist/factory/index.d.mts +4 -3
- package/dist/factory/index.mjs +3 -1
- package/dist/{fastify-augmentation-dPNBVeIj.d.mts → fastify-augmentation-0WhPnUH7.d.mts} +3 -2
- package/dist/{applyPermissionResult-Tw4iXtlf.mjs → fields-CQ_7KQWw.mjs} +1 -97
- package/dist/hooks/index.d.mts +1 -1
- package/dist/hooks/index.mjs +1 -1
- package/dist/idempotency/index.mjs +1 -1
- package/dist/index-CCzVR-N3.d.mts +107 -0
- package/dist/{index-BMnbzvtO.d.mts → index-Ch16aAah.d.mts} +201 -21
- package/dist/{index-f4PTrk3B.d.mts → index-Cx7bro4R.d.mts} +4 -4
- package/dist/{index-DPRH0Cme.d.mts → index-Q5gzEIWY.d.mts} +97 -66
- package/dist/index.d.mts +6 -6
- package/dist/index.mjs +8 -6
- package/dist/integrations/index.d.mts +1 -1
- package/dist/integrations/mcp/index.d.mts +6 -6
- package/dist/integrations/mcp/index.mjs +1 -1
- package/dist/integrations/mcp/testing.d.mts +1 -1
- package/dist/integrations/mcp/testing.mjs +1 -1
- package/dist/{mcpPlugin-Cb7olw0j.mjs → mcpPlugin-nRzm5g2s.mjs} +36 -43
- package/dist/middleware/index.d.mts +1 -1
- package/dist/migrations/index.d.mts +0 -3
- package/dist/migrations/index.mjs +0 -3
- package/dist/module-_-M7C5ES.mjs +29 -0
- package/dist/{openapi-DrLtog_B.mjs → openapi-B8Vkbxbn.mjs} +1 -1
- package/dist/permissions/index.d.mts +3 -3
- package/dist/permissions/index.mjs +4 -3
- package/dist/{permissions-BrtiBKGs.mjs → permissions-Bfu_8y1U.mjs} +318 -289
- package/dist/pipeline/index.d.mts +1 -1
- package/dist/{pipeline-BN5G05rd.mjs → pipeline-BJnpkY9i.mjs} +4 -18
- package/dist/plugins/index.d.mts +4 -4
- package/dist/plugins/index.mjs +2 -2
- package/dist/plugins/tracing-entry.mjs +1 -1
- package/dist/{preloadResources-0oe-JSSS.d.mts → preloadResources-DCfa2egz.d.mts} +3 -2
- package/dist/preloadResources-DSY5HGpS.mjs +45 -0
- package/dist/presets/filesUpload.d.mts +2 -2
- package/dist/presets/filesUpload.mjs +1 -1
- package/dist/presets/index.d.mts +14 -2
- package/dist/presets/index.mjs +1 -1
- package/dist/presets/multiTenant.d.mts +1 -1
- package/dist/presets/search.d.mts +2 -2
- package/dist/presets/search.mjs +1 -1
- package/dist/{presets-DwFDVKP0.mjs → presets-CJOEXLuk.mjs} +25 -9
- package/dist/projection-UBDgfMwu.mjs +17 -0
- package/dist/{realtime-Cuw_deA-.mjs → realtime-B9nmIY5V.mjs} +2 -1
- package/dist/{registerRoute-v0Wb6Plt.mjs → registerRoute-JkxIEBJq.mjs} +3 -1
- package/dist/registry/index.d.mts +2 -2
- package/dist/registry/index.mjs +1 -1
- package/dist/{registry-vcOCtPij.mjs → registry-BYXUUg21.mjs} +16 -1
- package/dist/repositoryFilter-Dz8k3S4P.mjs +44 -0
- package/dist/subscribe-helpers-DfzsivM6.mjs +339 -0
- package/dist/testing/cleanupStoreContract.d.mts +10 -0
- package/dist/testing/cleanupStoreContract.mjs +375 -0
- package/dist/testing/index.d.mts +34 -5
- package/dist/testing/index.mjs +316 -211
- package/dist/testing/outboxStoreContract.d.mts +13 -0
- package/dist/testing/outboxStoreContract.mjs +247 -0
- package/dist/types/index.d.mts +3 -3
- package/dist/{types-QFGc3Cjv.d.mts → types-Bmmn-fO0.d.mts} +1 -1
- package/dist/types-C_ZZRcD6.d.mts +450 -0
- package/dist/{types-Dmw-6Qeo.d.mts → types-ChvvVNP8.d.mts} +93 -88
- package/dist/{index-KUoxjIq1.d.mts → types-D_n8L6VY.d.mts} +33 -106
- package/dist/usage/index.d.mts +1 -1
- package/dist/usage/index.mjs +0 -0
- package/dist/utils/index.d.mts +2 -5
- package/package.json +539 -524
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Ct as PipelineStep, Rt as IControllerResponse, St as PipelineContext, _t as Guard, bt as OperationFilter, vt as Interceptor, wt as Transform, xt as PipelineConfig, yt as NextFunction } from "../index-
|
|
1
|
+
import { Ct as PipelineStep, Rt as IControllerResponse, St as PipelineContext, _t as Guard, bt as OperationFilter, vt as Interceptor, wt as Transform, xt as PipelineConfig, yt as NextFunction } from "../index-Q5gzEIWY.mjs";
|
|
2
2
|
//#region src/pipeline/guard.d.ts
|
|
3
3
|
interface GuardOptions {
|
|
4
4
|
operations?: OperationFilter;
|
|
@@ -1,23 +1,9 @@
|
|
|
1
1
|
import { n as getUserRoles } from "./userHelpers-Cg_y3fBz.mjs";
|
|
2
|
-
import { b as isElevated,
|
|
3
|
-
import { i as
|
|
2
|
+
import { b as isElevated, n as PUBLIC_SCOPE, x as isMember } from "./types-CSyCxl2V.mjs";
|
|
3
|
+
import { i as resolveEffectiveRoles, t as applyFieldReadPermissions } from "./fields-CQ_7KQWw.mjs";
|
|
4
4
|
import { t as executePipeline } from "./pipe-BOLmGIxa.mjs";
|
|
5
|
+
import { t as buildRequestScopeProjection } from "./projection-UBDgfMwu.mjs";
|
|
5
6
|
import { isPaginatedResult, toCanonicalList } from "@classytic/repo-core/pagination";
|
|
6
|
-
//#region src/scope/projection.ts
|
|
7
|
-
/**
|
|
8
|
-
* Compute the request-scope projection. Returns `undefined` when no
|
|
9
|
-
* scope is attached (public / unscoped routes) so hosts can idiomatically
|
|
10
|
-
* write `ctx.scope?.organizationId` without a double-null check.
|
|
11
|
-
*/
|
|
12
|
-
function buildRequestScopeProjection(scope) {
|
|
13
|
-
if (!scope) return void 0;
|
|
14
|
-
return {
|
|
15
|
-
organizationId: getOrgId(scope),
|
|
16
|
-
userId: getUserId(scope),
|
|
17
|
-
orgRoles: isMember(scope) ? scope.orgRoles : void 0
|
|
18
|
-
};
|
|
19
|
-
}
|
|
20
|
-
//#endregion
|
|
21
7
|
//#region src/core/fastifyAdapter.ts
|
|
22
8
|
/** Type guard for Mongoose-like documents with toObject() */
|
|
23
9
|
function isMongooseDoc(obj) {
|
|
@@ -334,4 +320,4 @@ function buildActionPipelineHandler(handler, steps, operation, resourceName) {
|
|
|
334
320
|
};
|
|
335
321
|
}
|
|
336
322
|
//#endregion
|
|
337
|
-
export { createFastifyHandler as a, getControllerScope as c, createCrudHandlers as i, sendControllerResponse as l, buildPipelineHandler as n, createRequestContext as o, resolvePipelineSteps as r, getControllerContext as s, buildActionPipelineHandler as t
|
|
323
|
+
export { createFastifyHandler as a, getControllerScope as c, createCrudHandlers as i, sendControllerResponse as l, buildPipelineHandler as n, createRequestContext as o, resolvePipelineSteps as r, getControllerContext as s, buildActionPipelineHandler as t };
|
package/dist/plugins/index.d.mts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { At as RouteDefinition, J as PresetHook, Q as MiddlewareConfig, et as RouteSchemaOptions } from "../index-
|
|
2
|
-
import {
|
|
1
|
+
import { At as RouteDefinition, J as PresetHook, Q as MiddlewareConfig, et as RouteSchemaOptions } from "../index-Q5gzEIWY.mjs";
|
|
2
|
+
import { l as AnyRecord } from "../types-ChvvVNP8.mjs";
|
|
3
3
|
import { i as errorHandlerPlugin, n as ErrorMapper, r as defaultIsDuplicateKeyError, t as ErrorHandlerOptions } from "../errorHandler-C6yv4ZTW.mjs";
|
|
4
|
-
import {
|
|
5
|
-
import { a as arcCorePlugin, i as _default, n as ArcCorePluginOptions, r as PluginMeta, t as ArcCore } from "../fastify-augmentation-
|
|
4
|
+
import { _ as _default$3, d as ScheduleStats, f as SchedulesPluginOptions, g as HealthOptions, h as HealthCheck, m as schedulesPlugin, p as _default$7, u as ScheduleDefinition, v as healthPlugin } from "../types-D_n8L6VY.mjs";
|
|
5
|
+
import { a as arcCorePlugin, i as _default, n as ArcCorePluginOptions, r as PluginMeta, t as ArcCore } from "../fastify-augmentation-0WhPnUH7.mjs";
|
|
6
6
|
import { _ as CachingRule, a as MetricsCollector, c as metricsPlugin, d as _default$5, f as realtimePlugin, g as CachingOptions, h as ssePlugin, i as MetricEntry, l as ChangeOperation, m as _default$8, n as _default$9, o as MetricsOptions, p as SSEOptions, r as versioningPlugin, s as _default$4, t as VersioningOptions, u as RealtimeOptions, v as _default$1, y as cachingPlugin } from "../versioning-DXgY5BUm.mjs";
|
|
7
7
|
import { t as TracingOptions } from "../tracing-CsGKQJGg.mjs";
|
|
8
8
|
import { PaginatedResult } from "@classytic/repo-core/pagination";
|
package/dist/plugins/index.mjs
CHANGED
|
@@ -2,13 +2,13 @@ import { p as MUTATION_OPERATIONS } from "../constants-DNre0wRg.mjs";
|
|
|
2
2
|
import { c as getOrgId } from "../types-CSyCxl2V.mjs";
|
|
3
3
|
import { n as requestContext } from "../requestContext-MrlJwECf.mjs";
|
|
4
4
|
import { t as hasEvents } from "../typeGuards-C9KXUly6.mjs";
|
|
5
|
-
import { t as HookSystem } from "../HookSystem-
|
|
5
|
+
import { t as HookSystem } from "../HookSystem-WkIougo6.mjs";
|
|
6
6
|
import { t as ResourceRegistry } from "../ResourceRegistry-y9Hnypus.mjs";
|
|
7
7
|
import { a as requestIdPlugin, i as createRequestIdGenerator, n as health_default, o as requestId_default, s as sanitizeRequestId, t as healthPlugin } from "../health-CB_m4S32.mjs";
|
|
8
8
|
import { n as caching_default, t as cachingPlugin } from "../caching-_NJeDd1_.mjs";
|
|
9
9
|
import { n as errorHandlerPlugin, t as defaultIsDuplicateKeyError } from "../errorHandler-Bq2OeEQL.mjs";
|
|
10
10
|
import { n as metrics_default, t as metricsPlugin } from "../metrics-fdxI5mn_.mjs";
|
|
11
|
-
import { n as realtime_default, t as realtimePlugin } from "../realtime-
|
|
11
|
+
import { n as realtime_default, t as realtimePlugin } from "../realtime-B9nmIY5V.mjs";
|
|
12
12
|
import { t as replyHelpersPlugin } from "../replyHelpers-67tRawQZ.mjs";
|
|
13
13
|
import { n as schedules_default, t as schedulesPlugin } from "../schedules-CjOd2MG9.mjs";
|
|
14
14
|
import { n as sse_default, t as ssePlugin } from "../sse-BQ1w0dfl.mjs";
|
|
@@ -58,7 +58,7 @@ try {
|
|
|
58
58
|
function createTracerProvider(options) {
|
|
59
59
|
if (!isAvailable || !NodeTracerProvider || !BatchSpanProcessor || !OTLPTraceExporter) return null;
|
|
60
60
|
const { serviceName = "@classytic/arc", serviceVersion, exporterUrl = "http://localhost:4318/v1/traces" } = options;
|
|
61
|
-
const resolvedVersion = serviceVersion ?? "2.
|
|
61
|
+
const resolvedVersion = serviceVersion ?? "2.30.0";
|
|
62
62
|
const exporter = new OTLPTraceExporter({ url: exporterUrl });
|
|
63
63
|
const provider = new NodeTracerProvider({ resource: { attributes: {
|
|
64
64
|
"service.name": serviceName,
|
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
import { Wt as Authenticator } from "./index-
|
|
1
|
+
import { Wt as Authenticator } from "./index-Q5gzEIWY.mjs";
|
|
2
2
|
import { r as CacheStore } from "./interface-BGZCzOGL.mjs";
|
|
3
3
|
import { n as ElevationOptions } from "./elevation-C9s5hFP_.mjs";
|
|
4
4
|
import { a as EventTransport } from "./EventTransport-QmY7A258.mjs";
|
|
5
5
|
import { t as ExternalOpenApiPaths } from "./externalPaths-BE6wFpYv.mjs";
|
|
6
6
|
import { r as QueryCachePluginOptions } from "./queryCachePlugin-br4otiqF.mjs";
|
|
7
7
|
import { t as ErrorHandlerOptions } from "./errorHandler-C6yv4ZTW.mjs";
|
|
8
|
-
import {
|
|
8
|
+
import { f as SchedulesPluginOptions, g as HealthOptions, n as ArcModuleInput, s as ResourceLike } from "./types-D_n8L6VY.mjs";
|
|
9
|
+
import "./index-CCzVR-N3.mjs";
|
|
9
10
|
import { t as EventPluginOptions } from "./eventPlugin-Bg3cP2_u.mjs";
|
|
10
11
|
import { g as CachingOptions, o as MetricsOptions, p as SSEOptions, t as VersioningOptions, u as RealtimeOptions } from "./versioning-DXgY5BUm.mjs";
|
|
11
12
|
import { r as IdempotencyStore } from "./interface-Uk8ONJv7.mjs";
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
//#region src/factory/preloadResources.ts
|
|
2
|
+
/**
|
|
3
|
+
* Normalize an eager `import.meta.glob` result into a `ResourceLike[]`.
|
|
4
|
+
*
|
|
5
|
+
* Accepts either:
|
|
6
|
+
* - `{ import: 'default' }` form: values are the resource directly
|
|
7
|
+
* - default form: values are the full module — picks first export with `toPlugin()`
|
|
8
|
+
*
|
|
9
|
+
* Throws if any module doesn't yield a valid `ResourceLike`.
|
|
10
|
+
*/
|
|
11
|
+
function preloadResources(globResult) {
|
|
12
|
+
const resources = [];
|
|
13
|
+
for (const [path, value] of Object.entries(globResult)) {
|
|
14
|
+
const resource = pickResource(value);
|
|
15
|
+
if (!resource) throw new Error(`preloadResources: ${path} does not export a valid resource.\n Expected: a default export OR a named export with toPlugin().`);
|
|
16
|
+
resources.push(resource);
|
|
17
|
+
}
|
|
18
|
+
return resources.sort((a, b) => (a.name ?? "").localeCompare(b.name ?? ""));
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Normalize a lazy `import.meta.glob` result into a `Promise<ResourceLike[]>`.
|
|
22
|
+
*
|
|
23
|
+
* Use this when resources depend on prior bootstrap (e.g., engine init) and
|
|
24
|
+
* cannot be evaluated at import time of the preload file.
|
|
25
|
+
*/
|
|
26
|
+
async function preloadResourcesAsync(globResult) {
|
|
27
|
+
return (await Promise.all(Object.entries(globResult).map(async ([path, loader]) => {
|
|
28
|
+
const resource = pickResource(await loader());
|
|
29
|
+
if (!resource) throw new Error(`preloadResourcesAsync: ${path} does not export a valid resource.\n Expected: a default export OR a named export with toPlugin().`);
|
|
30
|
+
return resource;
|
|
31
|
+
}))).sort((a, b) => (a.name ?? "").localeCompare(b.name ?? ""));
|
|
32
|
+
}
|
|
33
|
+
function pickResource(value) {
|
|
34
|
+
if (!value || typeof value !== "object") return void 0;
|
|
35
|
+
if (typeof value.toPlugin === "function") return value;
|
|
36
|
+
const mod = value;
|
|
37
|
+
const candidates = [
|
|
38
|
+
mod.default,
|
|
39
|
+
mod.resource,
|
|
40
|
+
...Object.values(mod)
|
|
41
|
+
];
|
|
42
|
+
for (const c of candidates) if (c && typeof c === "object" && typeof c.toPlugin === "function") return c;
|
|
43
|
+
}
|
|
44
|
+
//#endregion
|
|
45
|
+
export { preloadResourcesAsync as n, preloadResources as t };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { Y as PresetResult } from "../index-
|
|
1
|
+
import { Y as PresetResult } from "../index-Q5gzEIWY.mjs";
|
|
2
2
|
import { i as RequestScope } from "../types-DjC-KUCz.mjs";
|
|
3
|
-
import {
|
|
3
|
+
import { r as PermissionCheck } from "../types-ChvvVNP8.mjs";
|
|
4
4
|
import { a as StorageReadResult, i as StorageReadRange, n as StorageContext, o as StorageUploadInput, r as StorageFile, t as Storage } from "../storage-DLUZ04kK.mjs";
|
|
5
5
|
//#region src/presets/filesUpload.d.ts
|
|
6
6
|
interface FilesUploadPresetRoutes {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { i as NotFoundError, u as ValidationError } from "../errors-DkO8Nk6-.mjs";
|
|
2
2
|
import { c as getOrgId, g as getUserId } from "../types-CSyCxl2V.mjs";
|
|
3
|
-
import {
|
|
3
|
+
import { I as requireAuth, j as allowPublic } from "../permissions-Bfu_8y1U.mjs";
|
|
4
4
|
import { t as multipartBody } from "../multipartBody-D0eBvkeq.mjs";
|
|
5
5
|
//#region src/presets/filesUpload.ts
|
|
6
6
|
const DEFAULT_FIELD_NAME = "file";
|
package/dist/presets/index.d.mts
CHANGED
|
@@ -1,11 +1,23 @@
|
|
|
1
|
-
import { Rt as IControllerResponse, U as ResourceConfig, Y as PresetResult, d as PaginationResult, zt as IRequestContext } from "../index-
|
|
2
|
-
import {
|
|
1
|
+
import { Rt as IControllerResponse, U as ResourceConfig, Y as PresetResult, d as PaginationResult, zt as IRequestContext } from "../index-Q5gzEIWY.mjs";
|
|
2
|
+
import { l as AnyRecord } from "../types-ChvvVNP8.mjs";
|
|
3
3
|
import { FilesUploadPresetOptions, FilesUploadPresetPermissions, FilesUploadPresetRoutes, filesUploadPreset } from "./filesUpload.mjs";
|
|
4
4
|
import { MultiTenantOptions, TenantFieldSpec, multiTenantPreset } from "./multiTenant.mjs";
|
|
5
5
|
import { SearchHandler, SearchPresetOptions, SearchRouteConfig, searchPreset } from "./search.mjs";
|
|
6
6
|
//#region src/presets/ownedByUser.d.ts
|
|
7
7
|
interface OwnedByUserOptions {
|
|
8
8
|
ownerField?: string;
|
|
9
|
+
/**
|
|
10
|
+
* What to do when a target record has NO value in `ownerField` (legacy /
|
|
11
|
+
* unowned records).
|
|
12
|
+
*
|
|
13
|
+
* - `"deny"` (default): fail closed — an unowned record is NOT modifiable
|
|
14
|
+
* through an ownership-gated route. This is the secure default; ownership
|
|
15
|
+
* that silently passes for records missing the owner is not an ownership
|
|
16
|
+
* primitive.
|
|
17
|
+
* - `"allow"`: legacy compatibility — permit modifying records that predate
|
|
18
|
+
* the owner field. Visibly unsafe; opt in only for a bounded migration.
|
|
19
|
+
*/
|
|
20
|
+
missingOwner?: "deny" | "allow";
|
|
9
21
|
}
|
|
10
22
|
declare function ownedByUserPreset(options?: OwnedByUserOptions): PresetResult;
|
|
11
23
|
//#endregion
|
package/dist/presets/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { multiTenantPreset } from "./multiTenant.mjs";
|
|
2
|
-
import { a as registerPreset, c as auditedPreset, d as ownedByUserPreset, i as getPreset, l as softDeletePreset, n as flexibleMultiTenantPreset, o as treePreset, r as getAvailablePresets, s as bulkPreset, t as applyPresets, u as slugLookupPreset } from "../presets-
|
|
2
|
+
import { a as registerPreset, c as auditedPreset, d as ownedByUserPreset, i as getPreset, l as softDeletePreset, n as flexibleMultiTenantPreset, o as treePreset, r as getAvailablePresets, s as bulkPreset, t as applyPresets, u as slugLookupPreset } from "../presets-CJOEXLuk.mjs";
|
|
3
3
|
import { filesUploadPreset } from "./filesUpload.mjs";
|
|
4
4
|
import { searchPreset } from "./search.mjs";
|
|
5
5
|
export { applyPresets, auditedPreset, bulkPreset, filesUploadPreset, flexibleMultiTenantPreset, getAvailablePresets, getPreset, multiTenantPreset, ownedByUserPreset, registerPreset, searchPreset, slugLookupPreset, softDeletePreset, treePreset };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { X as CrudRouteKey, Y as PresetResult } from "../index-
|
|
1
|
+
import { X as CrudRouteKey, Y as PresetResult } from "../index-Q5gzEIWY.mjs";
|
|
2
2
|
//#region src/presets/multiTenant.d.ts
|
|
3
3
|
/**
|
|
4
4
|
* One tenant dimension for multi-field filtering. Discriminated by source:
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { At as RouteDefinition, Pt as ControllerHandler, Y as PresetResult, jt as RouteMcpConfig } from "../index-
|
|
2
|
-
import {
|
|
1
|
+
import { At as RouteDefinition, Pt as ControllerHandler, Y as PresetResult, jt as RouteMcpConfig } from "../index-Q5gzEIWY.mjs";
|
|
2
|
+
import { r as PermissionCheck } from "../types-ChvvVNP8.mjs";
|
|
3
3
|
//#region src/presets/search.d.ts
|
|
4
4
|
/**
|
|
5
5
|
* Search Preset — backend-agnostic search / vector / embed routes
|
package/dist/presets/search.mjs
CHANGED
|
@@ -1,29 +1,40 @@
|
|
|
1
|
+
import { l as UnauthorizedError } from "./errors-DkO8Nk6-.mjs";
|
|
1
2
|
import { b as isElevated, n as PUBLIC_SCOPE } from "./types-CSyCxl2V.mjs";
|
|
2
|
-
import {
|
|
3
|
+
import { I as requireAuth, R as requireRoles, j as allowPublic } from "./permissions-Bfu_8y1U.mjs";
|
|
3
4
|
import { multiTenantPreset } from "./presets/multiTenant.mjs";
|
|
4
5
|
//#region src/presets/ownedByUser.ts
|
|
5
6
|
/**
|
|
6
7
|
* Create ownership check middleware.
|
|
7
|
-
*
|
|
8
|
+
*
|
|
9
|
+
* FAIL-CLOSED: ownership is meaningless without an authenticated identity, so a
|
|
10
|
+
* request with no user (or a user with no id) is DENIED here rather than sliding
|
|
11
|
+
* through with no ownership check installed. Elevated scope (platform admin)
|
|
12
|
+
* bypasses the check by design.
|
|
8
13
|
*/
|
|
9
|
-
function createOwnershipCheck(ownerField) {
|
|
14
|
+
function createOwnershipCheck(ownerField, missingOwner) {
|
|
10
15
|
return async (request, _reply) => {
|
|
11
|
-
const user = request.user;
|
|
12
|
-
if (!user) return;
|
|
13
16
|
if (isElevated(request.scope ?? PUBLIC_SCOPE)) return;
|
|
17
|
+
const user = request.user;
|
|
18
|
+
if (!user) throw new UnauthorizedError("Authentication required for this operation");
|
|
14
19
|
const userWithId = user;
|
|
15
20
|
const userId = userWithId._id ?? userWithId.id;
|
|
16
|
-
if (userId)
|
|
21
|
+
if (!userId) throw new UnauthorizedError("Authenticated identity has no usable id");
|
|
22
|
+
request._ownershipCheck = {
|
|
17
23
|
field: ownerField,
|
|
18
|
-
userId
|
|
24
|
+
userId,
|
|
25
|
+
missingOwner
|
|
19
26
|
};
|
|
20
27
|
};
|
|
21
28
|
}
|
|
22
29
|
function ownedByUserPreset(options = {}) {
|
|
23
|
-
const { ownerField = "userId" } = options;
|
|
24
|
-
const ownershipMiddleware = createOwnershipCheck(ownerField);
|
|
30
|
+
const { ownerField = "userId", missingOwner = "deny" } = options;
|
|
31
|
+
const ownershipMiddleware = createOwnershipCheck(ownerField, missingOwner);
|
|
25
32
|
return {
|
|
26
33
|
name: "ownedByUser",
|
|
34
|
+
permissions: {
|
|
35
|
+
update: requireAuth(),
|
|
36
|
+
delete: requireAuth()
|
|
37
|
+
},
|
|
27
38
|
middlewares: {
|
|
28
39
|
update: [ownershipMiddleware],
|
|
29
40
|
delete: [ownershipMiddleware]
|
|
@@ -366,6 +377,11 @@ function mergePreset(config, preset) {
|
|
|
366
377
|
result.middlewares[key] = [...result.middlewares[key] ?? [], ...mws ?? []];
|
|
367
378
|
}
|
|
368
379
|
}
|
|
380
|
+
if (preset.permissions) {
|
|
381
|
+
const merged = { ...result.permissions };
|
|
382
|
+
for (const [op, check] of Object.entries(preset.permissions)) if (merged[op] === void 0 && check !== void 0) merged[op] = check;
|
|
383
|
+
result.permissions = merged;
|
|
384
|
+
}
|
|
369
385
|
if (preset.schemaOptions) result.schemaOptions = {
|
|
370
386
|
...result.schemaOptions,
|
|
371
387
|
...preset.schemaOptions,
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { c as getOrgId, g as getUserId, x as isMember } from "./types-CSyCxl2V.mjs";
|
|
2
|
+
//#region src/scope/projection.ts
|
|
3
|
+
/**
|
|
4
|
+
* Compute the request-scope projection. Returns `undefined` when no
|
|
5
|
+
* scope is attached (public / unscoped routes) so hosts can idiomatically
|
|
6
|
+
* write `ctx.scope?.organizationId` without a double-null check.
|
|
7
|
+
*/
|
|
8
|
+
function buildRequestScopeProjection(scope) {
|
|
9
|
+
if (!scope) return void 0;
|
|
10
|
+
return {
|
|
11
|
+
organizationId: getOrgId(scope),
|
|
12
|
+
userId: getUserId(scope),
|
|
13
|
+
orgRoles: isMember(scope) ? scope.orgRoles : void 0
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
//#endregion
|
|
17
|
+
export { buildRequestScopeProjection as t };
|
|
@@ -3,7 +3,8 @@ import { arcLog } from "./logger/index.mjs";
|
|
|
3
3
|
import { n as getUserRoles } from "./userHelpers-Cg_y3fBz.mjs";
|
|
4
4
|
import { c as getOrgId } from "./types-CSyCxl2V.mjs";
|
|
5
5
|
import { t as simpleEqualityMatcher } from "./simpleEqualityMatcher-Cg05HPsn.mjs";
|
|
6
|
-
import {
|
|
6
|
+
import { r as evaluateAndApplyPermission } from "./applyPermissionResult-Cf6zUCvb.mjs";
|
|
7
|
+
import { t as applyFieldReadPermissions } from "./fields-CQ_7KQWw.mjs";
|
|
7
8
|
import { a as promoteStreamTokenToHeader } from "./streaming-BQcW6_Kq.mjs";
|
|
8
9
|
import { t as openSseStream } from "./sseStream-BQ7Qaynp.mjs";
|
|
9
10
|
import fp from "fastify-plugin";
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { f as createError } from "./errors-DkO8Nk6-.mjs";
|
|
2
|
-
import {
|
|
2
|
+
import { u as getRequestScope } from "./types-CSyCxl2V.mjs";
|
|
3
|
+
import { r as evaluateAndApplyPermission } from "./applyPermissionResult-Cf6zUCvb.mjs";
|
|
3
4
|
//#region src/core/middlewares/permissions.ts
|
|
4
5
|
/**
|
|
5
6
|
* Build a PermissionContext from a Fastify request. Extracted so the CRUD
|
|
@@ -11,6 +12,7 @@ function buildPermissionContext(req, opts) {
|
|
|
11
12
|
const params = req.params;
|
|
12
13
|
return {
|
|
13
14
|
user: reqWithExtras.user ?? null,
|
|
15
|
+
scope: getRequestScope(req),
|
|
14
16
|
request: req,
|
|
15
17
|
resource: opts.resource,
|
|
16
18
|
action: opts.action,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { K as ResolvedTenantPurge, R as RegisterOptions, V as ResourceDefinition, X as CrudRouteKey, k as IntrospectionPluginOptions, p as RegistryEntry, z as ResourceRegistry } from "../index-
|
|
2
|
-
import "../fastify-augmentation-
|
|
1
|
+
import { K as ResolvedTenantPurge, R as RegisterOptions, V as ResourceDefinition, X as CrudRouteKey, k as IntrospectionPluginOptions, p as RegistryEntry, z as ResourceRegistry } from "../index-Q5gzEIWY.mjs";
|
|
2
|
+
import "../fastify-augmentation-0WhPnUH7.mjs";
|
|
3
3
|
import { FastifyPluginAsync } from "fastify";
|
|
4
4
|
import { TenantPurgeProgress, TenantPurgeStrategy } from "@classytic/repo-core/repository";
|
|
5
5
|
//#region src/registry/assertNoTenantData.d.ts
|
package/dist/registry/index.mjs
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { a as cascadeDeleteForOrganization, c as assertNoTenantData, i as introspectionPlugin_default, n as buildResourceManifestFromRegistry, o as getCascadingResources, r as introspectionPlugin, s as getCascadingResourcesWithMetadata, t as buildResourceManifest } from "../registry-
|
|
1
|
+
import { a as cascadeDeleteForOrganization, c as assertNoTenantData, i as introspectionPlugin_default, n as buildResourceManifestFromRegistry, o as getCascadingResources, r as introspectionPlugin, s as getCascadingResourcesWithMetadata, t as buildResourceManifest } from "../registry-BYXUUg21.mjs";
|
|
2
2
|
import { t as ResourceRegistry } from "../ResourceRegistry-y9Hnypus.mjs";
|
|
3
3
|
export { ResourceRegistry, assertNoTenantData, buildResourceManifest, buildResourceManifestFromRegistry, cascadeDeleteForOrganization, getCascadingResources, getCascadingResourcesWithMetadata, introspectionPlugin_default as introspectionPlugin, introspectionPlugin as introspectionPluginFn };
|
|
@@ -79,6 +79,9 @@ async function assertNoTenantData(registry, options) {
|
|
|
79
79
|
*
|
|
80
80
|
* - `skip` strategy: returns `path: 'skipped'` without touching the repo.
|
|
81
81
|
* - `purgeByField` present: routes through it (chunked, progress, abort).
|
|
82
|
+
* - `purgeByField` absent + `requireChunked`: returns `path: 'unsupported'`
|
|
83
|
+
* (`arc.purge.chunked_required`) — authoritative cleanup never runs the
|
|
84
|
+
* unbounded fallback.
|
|
82
85
|
* - `purgeByField` absent + hard strategy: legacy `deleteMany` fallback.
|
|
83
86
|
* - `purgeByField` absent + non-hard strategy: returns `path: 'unsupported'`
|
|
84
87
|
* with a clear error — soft/anonymize require the new primitive.
|
|
@@ -111,6 +114,18 @@ async function purgeResource(resourceName, tenantField, organizationId, resolved
|
|
|
111
114
|
...result.error ? { error: result.error } : {}
|
|
112
115
|
};
|
|
113
116
|
}
|
|
117
|
+
if (options.requireChunked) return {
|
|
118
|
+
resource: resourceName,
|
|
119
|
+
tenantField,
|
|
120
|
+
strategy,
|
|
121
|
+
processed: 0,
|
|
122
|
+
ok: false,
|
|
123
|
+
path: "unsupported",
|
|
124
|
+
error: {
|
|
125
|
+
code: "arc.purge.chunked_required",
|
|
126
|
+
message: `Resource '${resourceName}' requires chunked purge for authoritative cleanup, but its adapter repository does not implement \`purgeByField\`. The legacy unbounded \`deleteMany\` fallback is disabled here (no chunking / progress / abort). Upgrade the adapter (@classytic/mongokit ≥ 3.13.4 / @classytic/sqlitekit ≥ 0.3.4).`
|
|
127
|
+
}
|
|
128
|
+
};
|
|
114
129
|
if (strategy.type !== "hard") return {
|
|
115
130
|
resource: resourceName,
|
|
116
131
|
tenantField,
|
|
@@ -425,7 +440,7 @@ var introspectionPlugin_default = fp(introspectionPlugin, { name: "arc-introspec
|
|
|
425
440
|
//#endregion
|
|
426
441
|
//#region src/registry/manifest.ts
|
|
427
442
|
/**
|
|
428
|
-
* Resource manifest for frontend code generators
|
|
443
|
+
* Resource manifest for frontend code generators.
|
|
429
444
|
*
|
|
430
445
|
* Every host that hand-rolls a `createCrudApi('foo')` helper on the frontend
|
|
431
446
|
* ends up rewriting the same shim for declarative actions: `postAction(id,
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { arcLog } from "./logger/index.mjs";
|
|
2
|
+
import { policyRecordToFilter } from "@classytic/repo-core/filter";
|
|
3
|
+
//#region src/core/repositoryFilter.ts
|
|
4
|
+
const log = arcLog("repository-filter");
|
|
5
|
+
let warnedUnconvertible = false;
|
|
6
|
+
/** True for a `{ $op: ... }` operator object (all keys `$`-prefixed). */
|
|
7
|
+
function isOperatorObject(value) {
|
|
8
|
+
if (value === null || typeof value !== "object") return false;
|
|
9
|
+
if (Array.isArray(value) || value instanceof Date) return false;
|
|
10
|
+
const keys = Object.keys(value);
|
|
11
|
+
return keys.length > 0 && keys.some((k) => k.startsWith("$"));
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Does this compound filter contain any `$`-operator (top-level logical like
|
|
15
|
+
* `$and`/`$or`, or a field operator object like `{ $in: [...] }`)? If not, it is
|
|
16
|
+
* pure flat equality and portable as-is.
|
|
17
|
+
*/
|
|
18
|
+
function filterHasDollarOperator(record) {
|
|
19
|
+
for (const [key, value] of Object.entries(record)) {
|
|
20
|
+
if (key.startsWith("$")) return true;
|
|
21
|
+
if (isOperatorObject(value)) return true;
|
|
22
|
+
}
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Normalize a compound query/policy filter for the repository layer. Flat
|
|
27
|
+
* equality records pass through unchanged; anything carrying `$`-operators is
|
|
28
|
+
* converted to the portable repo-core {@link Filter} IR so it compiles
|
|
29
|
+
* identically on Mongo, SQLite, Postgres, and custom adapters.
|
|
30
|
+
*/
|
|
31
|
+
function toRepositoryFilter(record) {
|
|
32
|
+
if (!filterHasDollarOperator(record)) return record;
|
|
33
|
+
try {
|
|
34
|
+
return policyRecordToFilter(record);
|
|
35
|
+
} catch (err) {
|
|
36
|
+
if (!warnedUnconvertible) {
|
|
37
|
+
warnedUnconvertible = true;
|
|
38
|
+
log.warn(`A \`\$\`-operator filter could not be normalized to portable Filter IR (operator outside arc's policy dialect). Passing the raw record to the repository — MongoKit handles it; non-Mongo kits may not. (${err instanceof Error ? err.message : String(err)})`);
|
|
39
|
+
}
|
|
40
|
+
return record;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
//#endregion
|
|
44
|
+
export { toRepositoryFilter as t };
|