@classytic/arc 2.22.0 → 2.23.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{BaseController-C6FlRWxF.mjs → BaseController-BQ8KNvWC.mjs} +3 -2
- package/dist/{ResourceRegistry-7RnrEWNP.mjs → ResourceRegistry-y9Hnypus.mjs} +1 -0
- package/dist/{actionPermissions-ze70xXJi.mjs → actionPermissions-lszoJle1.mjs} +1 -1
- package/dist/applyPermissionResult-Tw4iXtlf.mjs +264 -0
- package/dist/audit/index.d.mts +1 -1
- package/dist/auth/index.d.mts +1 -1
- package/dist/auth/index.mjs +2 -2
- package/dist/cli/commands/describe.d.mts +1 -1
- package/dist/cli/commands/describe.mjs +1 -1
- package/dist/cli/commands/docs.mjs +2 -2
- package/dist/cli/commands/init.mjs +1 -1
- package/dist/cli/commands/introspect.mjs +1 -1
- package/dist/cli/index.mjs +1 -1
- package/dist/core/index.d.mts +2 -2
- package/dist/core/index.mjs +3 -3
- package/dist/{core-b6fgFgnd.mjs → core-DQ6rDTyH.mjs} +10 -9
- package/dist/{createActionRouter-h1cEEaEX.mjs → createActionRouter-BuOv-m4Y.mjs} +3 -3
- package/dist/{createAggregationRouter-B1C0kvTL.mjs → createAggregationRouter-CgJv_BU_.mjs} +1 -1
- package/dist/{createApp-Ci7yG5gv.mjs → createApp-CjOeWJ9_.mjs} +11 -2
- package/dist/docs/index.d.mts +1 -1
- package/dist/docs/index.mjs +1 -1
- package/dist/{errorHandler-BTzXSwIg.mjs → errorHandler-Bq2OeEQL.mjs} +10 -7
- package/dist/events/index.mjs +5 -2
- package/dist/factory/index.d.mts +20 -4
- package/dist/factory/index.mjs +31 -11
- package/dist/{fastify-augmentation-9_fuetB4.d.mts → fastify-augmentation-g_f9omSc.d.mts} +1 -1
- package/dist/hooks/index.d.mts +1 -1
- package/dist/idempotency/index.d.mts +7 -0
- package/dist/idempotency/index.mjs +5 -17
- package/dist/{index-DbtgCOCj.d.mts → index-BBsvQt7U.d.mts} +26 -4
- package/dist/{index-BEu4wjbL.d.mts → index-Ba10fdKp.d.mts} +11 -2
- package/dist/{index-D8MKYGDa.d.mts → index-CC5ruDGh.d.mts} +2 -2
- package/dist/{index-ogIHvOjo.d.mts → index-wMoWBLkn.d.mts} +67 -1
- package/dist/index.d.mts +4 -4
- package/dist/index.mjs +6 -5
- package/dist/{init-DleK3p9f.mjs → init-CjO4p_q1.mjs} +5 -2
- package/dist/integrations/event-gateway.mjs +1 -1
- package/dist/integrations/index.d.mts +1 -1
- package/dist/integrations/mcp/index.d.mts +66 -3
- 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/integrations/webhooks.d.mts +123 -3
- package/dist/integrations/webhooks.mjs +160 -5
- package/dist/interface-n5hpKg1d.mjs +16 -0
- package/dist/{loadResourcesFromEntry-OMCvIEE7.mjs → loadResourcesFromEntry-kvGT6EcO.mjs} +1 -1
- package/dist/{mcpPlugin-ByA2tf6D.mjs → mcpPlugin-BtzIL_kM.mjs} +308 -39
- package/dist/middleware/index.d.mts +1 -1
- package/dist/{openapi-BRpMZD7C.mjs → openapi-jSDx-VDv.mjs} +3 -3
- package/dist/permissions/index.d.mts +2 -2
- package/dist/permissions/index.mjs +3 -2
- package/dist/{permissions-BJMuKMl7.mjs → permissions-tbxFHxjZ.mjs} +77 -264
- package/dist/pipeline/index.d.mts +1 -1
- package/dist/plugins/index.d.mts +6 -6
- package/dist/plugins/index.mjs +5 -4
- package/dist/plugins/tracing-entry.mjs +1 -1
- package/dist/presets/filesUpload.d.mts +1 -1
- package/dist/presets/filesUpload.mjs +1 -1
- 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/search.d.mts +1 -1
- package/dist/presets/search.mjs +1 -1
- package/dist/{presets-gw0ciIUD.mjs → presets-BOe4Ugkl.mjs} +1 -1
- package/dist/realtime-I8OrfnMR.mjs +375 -0
- package/dist/{registerRoute-xHc42fwB.mjs → registerRoute-CWO6mdYe.mjs} +1 -1
- package/dist/registry/index.d.mts +2 -2
- package/dist/registry/index.mjs +1 -1
- package/dist/{schemaConverter-Ds5reSGs.mjs → schemaConverter-DJvodiHj.mjs} +4 -1
- package/dist/{schemaIR-B-6SPe7g.mjs → schemaIR-Ca7ySNPx.mjs} +1 -1
- package/dist/scope/index.mjs +1 -1
- package/dist/scope-LONQ3mfZ.mjs +37 -0
- package/dist/simpleEqualityMatcher-Cg05HPsn.mjs +68 -0
- package/dist/{sse-CfUjwjDk.mjs → sse-0u4P4-OI.mjs} +7 -30
- package/dist/sseStream-BQ7Qaynp.mjs +57 -0
- package/dist/testing/index.d.mts +2 -2
- package/dist/testing/index.mjs +2 -2
- package/dist/types/index.d.mts +1 -1
- package/dist/{types-Bc42CUyk.d.mts → types-5OuqeTDX.d.mts} +37 -2
- package/dist/utils/index.d.mts +62 -2
- package/dist/utils/index.mjs +5 -3
- package/dist/{utils-FhJ88TJE.mjs → utils-CZyeRXTh.mjs} +21 -69
- package/dist/{versioning-DLwklPlG.d.mts → versioning-D6PvCwpq.d.mts} +43 -4
- package/package.json +64 -4
- package/skills/arc/references/production.md +734 -696
- package/dist/scope-k15RXfcq.mjs +0 -64
package/dist/testing/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { t as CRUD_OPERATIONS } from "../constants-DNre0wRg.mjs";
|
|
2
|
-
import { d as getModuleExports, h as preloadResourcesAsync, m as preloadResources, n as createApp } from "../createApp-
|
|
2
|
+
import { d as getModuleExports, h as preloadResourcesAsync, m as preloadResources, n as createApp } from "../createApp-CjOeWJ9_.mjs";
|
|
3
3
|
import { runStorageContract } from "./storageContract.mjs";
|
|
4
4
|
import Fastify from "fastify";
|
|
5
5
|
import { afterAll, describe, expect, it, vi } from "vitest";
|
|
@@ -1116,7 +1116,7 @@ function pickDefaultAuth(authMode, callerAuth) {
|
|
|
1116
1116
|
};
|
|
1117
1117
|
}
|
|
1118
1118
|
async function createTestApp(options = {}) {
|
|
1119
|
-
const { createApp } = await import("../createApp-
|
|
1119
|
+
const { createApp } = await import("../createApp-CjOeWJ9_.mjs").then((n) => n.r);
|
|
1120
1120
|
const { resources = [], db = "in-memory", connectMongoose = false, authMode = "jwt", defaultOrgId, plugins, auth: callerAuth, ...appOptions } = options;
|
|
1121
1121
|
let dbHandle;
|
|
1122
1122
|
let dbUri;
|
package/dist/types/index.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { $ as OpenApiSchemas, $t as
|
|
1
|
+
import { $ as OpenApiSchemas, $t as UserLike, A as RequestIdOptions, At as RouteDefinition, Bt as RouteHandler, C as RequestContext, D as HealthCheck, Dt as ActionEntry, E as GracefulShutdownOptions, Et as ActionDefinition, F as FastifyWithDecorators, Ft as ControllerLike, G as OnTenantDeleteConfig, Gt as AuthenticatorContext, H as CrudController, Ht as AuthHelpers, I as MiddlewareHandler, In as BaseControllerOptions, It as FastifyHandler, J as PresetHook, Jt as AnyRecord, K as ResolvedTenantPurge, Kt as JwtContext, L as RequestWithExtras, Lt as IController, M as EventsDecorator, Mt as RouteMethod, N as FastifyRequestExtras, Nt as RateLimitConfig, O as HealthOptions, Ot as ActionHandlerFn, P as FastifyWithAuth, Pt as ControllerHandler, Q as MiddlewareConfig, Qt as ObjectId, Rt as IControllerResponse, S as QueryParserInterface, T as CrudRouterOptions, Tt as ResourceCacheConfig, U as ResourceConfig, Ut as AuthPluginOptions, W as ResourcePermissions, Wt as Authenticator, X as CrudRouteKey, Xt as ArcRequest, Y as PresetResult, Yt as ApiResponse, Z as CrudSchemas, Zt as JWTPayload, _ as ControllerQueryOptions, a as InferAdapterDoc, at as ResourceExtensions, b as ParsedQuery, c as TypedController, d as PaginationResult, en as UserOrganization, et as RouteSchemaOptions, f as IntrospectionData, g as ArcInternalMetadata, h as ResourceMetadata, i as ValidationResult, it as ResourceHooks, j as ArcDecorator, jt as RouteMcpConfig, k as IntrospectionPluginOptions, kt as ActionsMap, l as TypedRepository, m as RegistryStats, n as ConfigError, nt as FieldRule, o as InferDocType, ot as EventDefinition, p as RegistryEntry, q as PresetFunction, qt as TokenPair, r as ValidateOptions, rt as ResourceHookContext, s as InferResourceDoc, t as RouteHandlerMethod, tt as ArcFieldRule, u as TypedResourceConfig, v as LookupOption, w as ServiceContext, x as PopulateOption, y as OwnershipCheck, zt as IRequestContext } from "../index-BBsvQt7U.mjs";
|
|
2
2
|
import { i as RequestScope } from "../types-DH0GupI7.mjs";
|
|
3
3
|
import { i as UserBase, n as PermissionContext, r as PermissionResult, t as PermissionCheck } from "../types-Csxga7d3.mjs";
|
|
4
4
|
import { n as ElevationOptions, t as ElevationEvent } from "../elevation-C9s5hFP_.mjs";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { V as ResourceDefinition } from "./index-
|
|
1
|
+
import { V as ResourceDefinition } from "./index-BBsvQt7U.mjs";
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
//#region src/integrations/mcp/types.d.ts
|
|
4
4
|
/** Behavioral hints for MCP clients (tool annotations per MCP spec) */
|
|
@@ -157,6 +157,41 @@ interface CrudDescriptionMeta {
|
|
|
157
157
|
* Arc's auto-derived filterable-fields blurb.
|
|
158
158
|
*/
|
|
159
159
|
type CrudDescriptionOverride = string | ((meta: CrudDescriptionMeta) => string);
|
|
160
|
+
/**
|
|
161
|
+
* Execution wiring for MCP tools — the app-level services HTTP requests
|
|
162
|
+
* receive via Fastify decorators (`fastify.arc.hooks`, `fastify.events`,
|
|
163
|
+
* `fastify.audit`, `fastify.log`, `fastify.idempotency`) and MCP's
|
|
164
|
+
* synthetic-context path historically dropped.
|
|
165
|
+
*
|
|
166
|
+
* When present, tool handlers thread it into the request context so the
|
|
167
|
+
* controller pipeline achieves FULL HTTP parity: resource hooks run
|
|
168
|
+
* (before/around/after), the arcCorePlugin after-hook publishes
|
|
169
|
+
* `<resource>.<op>d` events (→ realtime feeds, webhooks, event-driven
|
|
170
|
+
* cache invalidation all see MCP-originated mutations), `ctx.server.*`
|
|
171
|
+
* works inside handlers, and mutating CRUD tools accept an optional
|
|
172
|
+
* `_idempotencyKey` input for retry-safe execution.
|
|
173
|
+
*
|
|
174
|
+
* `mcpPlugin` builds this automatically from its Fastify instance (lazy
|
|
175
|
+
* getters — decorator registration order doesn't matter). Standalone
|
|
176
|
+
* `resourceToTools()` / `createMcpServer()` callers pass their own, or
|
|
177
|
+
* omit it to keep the legacy dispatch-only behavior.
|
|
178
|
+
*/
|
|
179
|
+
interface McpExecutionWiring {
|
|
180
|
+
/** Shared `HookSystem` — `fastify.arc.hooks`. */
|
|
181
|
+
readonly hooks?: unknown;
|
|
182
|
+
/** Event bus decorator — `fastify.events`. */
|
|
183
|
+
readonly events?: unknown;
|
|
184
|
+
/** Audit decorator — `fastify.audit`. */
|
|
185
|
+
readonly audit?: unknown;
|
|
186
|
+
/** Structured logger — `fastify.log`. */
|
|
187
|
+
readonly log?: unknown;
|
|
188
|
+
/**
|
|
189
|
+
* Idempotency store (the `IdempotencyStore` contract from
|
|
190
|
+
* `@classytic/arc/idempotency`). Presence advertises `_idempotencyKey`
|
|
191
|
+
* on mutating CRUD tools and enables executor-level check/lock/replay.
|
|
192
|
+
*/
|
|
193
|
+
readonly idempotencyStore?: unknown;
|
|
194
|
+
}
|
|
160
195
|
/** Per-resource MCP configuration overrides */
|
|
161
196
|
interface McpResourceConfig {
|
|
162
197
|
/** Which CRUD operations to expose (default: all enabled on the resource) */
|
|
@@ -392,4 +427,4 @@ interface CreateMcpServerConfig {
|
|
|
392
427
|
/** CRUD operation type */
|
|
393
428
|
type CrudOperation = "list" | "get" | "create" | "update" | "delete";
|
|
394
429
|
//#endregion
|
|
395
|
-
export { CrudDescriptionOverride as a, McpAuthResult as c,
|
|
430
|
+
export { ToolDefinition as _, CrudDescriptionOverride as a, McpAuthResult as c, McpResourceConfig as d, PromptDefinition as f, ToolContext as g, ToolAnnotations as h, CrudDescriptionMeta as i, McpExecutionWiring as l, SessionEntry as m, CallToolResult as n, CrudOperation as o, PromptResult as p, CreateMcpServerConfig as r, McpAuthResolver as s, BetterAuthHandler as t, McpPluginOptions as u };
|
package/dist/utils/index.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { $ as OpenApiSchemas,
|
|
1
|
+
import { $ as OpenApiSchemas, $t as UserLike, Jt as AnyRecord, S as QueryParserInterface, U as ResourceConfig, b as ParsedQuery } from "../index-BBsvQt7U.mjs";
|
|
2
2
|
import { n as ErrorMapper } from "../errorHandler-C6yv4ZTW.mjs";
|
|
3
3
|
import { a as NotFoundError, c as RateLimitError, d as ValidationError, f as createDomainError, i as ForbiddenError, l as ServiceUnavailableError, m as isArcError, n as ConflictError, o as OrgAccessDeniedError, p as createError, r as ErrorOptions, s as OrgRequiredError, t as ArcError, u as UnauthorizedError } from "../errors-DHcHVAgk.mjs";
|
|
4
4
|
import { errorContractSchema, errorDetailSchema } from "@classytic/repo-core/errors";
|
|
@@ -437,6 +437,34 @@ declare const NEUTRAL_DIALECT: FilterDialect;
|
|
|
437
437
|
*/
|
|
438
438
|
declare function handleRaw<T>(handler: (request: FastifyRequest, reply: FastifyReply) => Promise<T>, statusCode?: number): (request: FastifyRequest, reply: FastifyReply) => Promise<void>;
|
|
439
439
|
//#endregion
|
|
440
|
+
//#region src/utils/headers.d.ts
|
|
441
|
+
/**
|
|
442
|
+
* Canonical header typing + accessor.
|
|
443
|
+
*
|
|
444
|
+
* Node/Fastify header values are `string | string[] | undefined` — repeated
|
|
445
|
+
* headers arrive as arrays. `IRequestContext.headers` is DECLARED as
|
|
446
|
+
* `Record<string, string | undefined>` for ergonomic host code, but the HTTP
|
|
447
|
+
* adapter passes Fastify's headers object through unchanged, so `string[]`
|
|
448
|
+
* CAN appear at runtime behind the narrower type (the declared type is
|
|
449
|
+
* scheduled to widen to {@link ArcHeaders} in v3). Until then, read headers
|
|
450
|
+
* through {@link getHeader} instead of indexing — it's honest about arrays
|
|
451
|
+
* and deterministic about duplicates.
|
|
452
|
+
*/
|
|
453
|
+
/** What a headers bag actually contains at runtime — HTTP or synthetic MCP. */
|
|
454
|
+
type ArcHeaders = Readonly<Record<string, string | readonly string[] | undefined>>;
|
|
455
|
+
/**
|
|
456
|
+
* Read a single header value. Lowercases the name (Node normalizes incoming
|
|
457
|
+
* header names to lowercase) and returns the FIRST value when the header
|
|
458
|
+
* was repeated.
|
|
459
|
+
*
|
|
460
|
+
* Duplicate policy: first-value is deterministic, but for security-sensitive
|
|
461
|
+
* headers (authorization material, forwarded-for chains, content-length) a
|
|
462
|
+
* duplicate usually signals smuggling or a broken proxy — consumers that
|
|
463
|
+
* care should check `Array.isArray(headers[name])` and REJECT instead of
|
|
464
|
+
* picking a winner.
|
|
465
|
+
*/
|
|
466
|
+
declare function getHeader(headers: ArcHeaders | Record<string, string | undefined> | undefined, name: string): string | undefined;
|
|
467
|
+
//#endregion
|
|
440
468
|
//#region src/utils/queryParser.d.ts
|
|
441
469
|
interface ArcQueryParserOptions {
|
|
442
470
|
/** Maximum allowed limit value (default: 1000) */
|
|
@@ -1215,6 +1243,38 @@ declare function convertRouteSchema(schema: Record<string, unknown>, target?: Js
|
|
|
1215
1243
|
*/
|
|
1216
1244
|
declare function simpleEqualityMatcher(item: unknown, filters: Record<string, unknown>): boolean;
|
|
1217
1245
|
//#endregion
|
|
1246
|
+
//#region src/utils/sseStream.d.ts
|
|
1247
|
+
interface SseStreamOptions {
|
|
1248
|
+
/** Heartbeat comment interval in ms (default: 30000). */
|
|
1249
|
+
heartbeatMs?: number;
|
|
1250
|
+
/**
|
|
1251
|
+
* Reconnection-delay hint written once at connect as the standard SSE
|
|
1252
|
+
* `retry:` field (WHATWG EventSource honors it natively). Omit to send
|
|
1253
|
+
* no hint (browser default ~3s).
|
|
1254
|
+
*/
|
|
1255
|
+
retryMs?: number;
|
|
1256
|
+
}
|
|
1257
|
+
interface SseStream {
|
|
1258
|
+
/**
|
|
1259
|
+
* Write one SSE frame. `id` (optional) becomes the standard SSE `id:`
|
|
1260
|
+
* field — clients receive it as `lastEventId` (WHATWG), enabling
|
|
1261
|
+
* client-side dedup/resume bookkeeping even when the server keeps no
|
|
1262
|
+
* replay log. Returns `false` when the connection was destroyed
|
|
1263
|
+
* (backpressure) — the stream is already cleaned up; stop writing.
|
|
1264
|
+
*/
|
|
1265
|
+
write(eventName: string, data: string, id?: string): boolean;
|
|
1266
|
+
/** Idempotent teardown: heartbeat cleared, cleanups run, socket ended. */
|
|
1267
|
+
close(): void;
|
|
1268
|
+
/** Register teardown work (unsubscribes etc.) — runs exactly once. */
|
|
1269
|
+
onCleanup(fn: () => void): void;
|
|
1270
|
+
}
|
|
1271
|
+
/**
|
|
1272
|
+
* Take over the reply socket and open an SSE stream. Call ONLY after every
|
|
1273
|
+
* gate (auth, permissions, validation) has passed — after `hijack()` no
|
|
1274
|
+
* JSON error can be sent.
|
|
1275
|
+
*/
|
|
1276
|
+
declare function openSseStream(request: FastifyRequest, reply: FastifyReply, options?: SseStreamOptions): SseStream;
|
|
1277
|
+
//#endregion
|
|
1218
1278
|
//#region src/utils/stateMachine.d.ts
|
|
1219
1279
|
/**
|
|
1220
1280
|
* State Machine Utility
|
|
@@ -1475,4 +1535,4 @@ declare function hasEvents(instance: FastifyInstance): instance is FastifyInstan
|
|
|
1475
1535
|
*/
|
|
1476
1536
|
declare function getUserId(user: UserLike | null | undefined): string | undefined;
|
|
1477
1537
|
//#endregion
|
|
1478
|
-
export { ArcError, ArcQueryParser, type ArcQueryParserOptions, CircuitBreaker, CircuitBreakerError, type CircuitBreakerOptions, CircuitBreakerRegistry, type CircuitBreakerStats, CircuitState, type CompensationDefinition, type CompensationError, type CompensationHooks, type CompensationResult, type CompensationStep, type ConfigError, ConflictError, type ErrorOptions, type EventsDecorator, type FilterDialect, ForbiddenError, type Guard, type GuardConfig, type JsonSchema, type JsonSchemaTarget, MONGO_DIALECT, NEUTRAL_DIALECT, NotFoundError, OrgAccessDeniedError, OrgRequiredError, type PipeUIMessageStreamOptions, RateLimitError, ServiceUnavailableError, type StateMachine, type TransitionConfig, UI_MESSAGE_STREAM_HEADERS, UnauthorizedError, type ValidateOptions, ValidationError, type ValidationResult, aggregateListResponse, assertValidConfig, bareListResponse, convertOpenApiSchemas, convertRouteSchema, createCircuitBreaker, createCircuitBreakerRegistry, createDomainError, createError, createQueryParser, createStateMachine, defineCompensation, defineErrorMapper, defineGuard, deleteResponse, errorContractSchema, errorDetailSchema, formatValidationErrors, forwardedStreamHeaders, getDefaultCrudSchemas, getListQueryParams, getUserId, handleRaw, hasEvents, isArcError, isJsonSchema, isReadableStream, isZodSchema, keysetListResponse, listResponse, offsetListResponse, paginationSchema, pipeUIMessageStreamToReply, promoteStreamTokenToHeader, queryParams, responses, scheduleBackground, simpleEqualityMatcher, toJsonSchema, validateResourceConfig, withCompensation };
|
|
1538
|
+
export { ArcError, type ArcHeaders, ArcQueryParser, type ArcQueryParserOptions, CircuitBreaker, CircuitBreakerError, type CircuitBreakerOptions, CircuitBreakerRegistry, type CircuitBreakerStats, CircuitState, type CompensationDefinition, type CompensationError, type CompensationHooks, type CompensationResult, type CompensationStep, type ConfigError, ConflictError, type ErrorOptions, type EventsDecorator, type FilterDialect, ForbiddenError, type Guard, type GuardConfig, type JsonSchema, type JsonSchemaTarget, MONGO_DIALECT, NEUTRAL_DIALECT, NotFoundError, OrgAccessDeniedError, OrgRequiredError, type PipeUIMessageStreamOptions, RateLimitError, ServiceUnavailableError, type SseStream, type SseStreamOptions, type StateMachine, type TransitionConfig, UI_MESSAGE_STREAM_HEADERS, UnauthorizedError, type ValidateOptions, ValidationError, type ValidationResult, aggregateListResponse, assertValidConfig, bareListResponse, convertOpenApiSchemas, convertRouteSchema, createCircuitBreaker, createCircuitBreakerRegistry, createDomainError, createError, createQueryParser, createStateMachine, defineCompensation, defineErrorMapper, defineGuard, deleteResponse, errorContractSchema, errorDetailSchema, formatValidationErrors, forwardedStreamHeaders, getDefaultCrudSchemas, getHeader, getListQueryParams, getUserId, handleRaw, hasEvents, isArcError, isJsonSchema, isReadableStream, isZodSchema, keysetListResponse, listResponse, offsetListResponse, openSseStream, paginationSchema, pipeUIMessageStreamToReply, promoteStreamTokenToHeader, queryParams, responses, scheduleBackground, simpleEqualityMatcher, toJsonSchema, validateResourceConfig, withCompensation };
|
package/dist/utils/index.mjs
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { a as OrgAccessDeniedError, c as ServiceUnavailableError, d as createDomainError, f as createError, i as NotFoundError, l as UnauthorizedError, n as ConflictError, o as OrgRequiredError, p as isArcError, r as ForbiddenError, s as RateLimitError, t as ArcError, u as ValidationError } from "../errors-DkO8Nk6-.mjs";
|
|
2
|
-
import {
|
|
3
|
-
import { a as
|
|
2
|
+
import { t as simpleEqualityMatcher } from "../simpleEqualityMatcher-Cg05HPsn.mjs";
|
|
3
|
+
import { A as CircuitState, C as defineGuard, D as CircuitBreaker, E as withCompensation, F as validateResourceConfig, M as createCircuitBreakerRegistry, N as assertValidConfig, O as CircuitBreakerError, P as formatValidationErrors, S as NEUTRAL_DIALECT, T as defineCompensation, _ as ArcQueryParser, a as bareListResponse, b as handleRaw, c as errorDetailSchema, d as keysetListResponse, f as listResponse, g as responses, h as queryParams, i as aggregateListResponse, j as createCircuitBreaker, k as CircuitBreakerRegistry, l as getDefaultCrudSchemas, m as paginationSchema, n as createStateMachine, o as deleteResponse, p as offsetListResponse, r as scheduleBackground, s as errorContractSchema, t as getUserId, u as getListQueryParams, v as createQueryParser, w as defineErrorMapper, x as MONGO_DIALECT, y as getHeader } from "../utils-CZyeRXTh.mjs";
|
|
4
|
+
import { a as isZodSchema, i as isJsonSchema, n as convertRouteSchema, o as toJsonSchema, t as convertOpenApiSchemas } from "../schemaConverter-DJvodiHj.mjs";
|
|
4
5
|
import { a as promoteStreamTokenToHeader, i as pipeUIMessageStreamToReply, n as forwardedStreamHeaders, r as isReadableStream, t as UI_MESSAGE_STREAM_HEADERS } from "../streaming-BQcW6_Kq.mjs";
|
|
6
|
+
import { t as openSseStream } from "../sseStream-BQ7Qaynp.mjs";
|
|
5
7
|
import { t as hasEvents } from "../typeGuards-C9KXUly6.mjs";
|
|
6
|
-
export { ArcError, ArcQueryParser, CircuitBreaker, CircuitBreakerError, CircuitBreakerRegistry, CircuitState, ConflictError, ForbiddenError, MONGO_DIALECT, NEUTRAL_DIALECT, NotFoundError, OrgAccessDeniedError, OrgRequiredError, RateLimitError, ServiceUnavailableError, UI_MESSAGE_STREAM_HEADERS, UnauthorizedError, ValidationError, aggregateListResponse, assertValidConfig, bareListResponse, convertOpenApiSchemas, convertRouteSchema, createCircuitBreaker, createCircuitBreakerRegistry, createDomainError, createError, createQueryParser, createStateMachine, defineCompensation, defineErrorMapper, defineGuard, deleteResponse, errorContractSchema, errorDetailSchema, formatValidationErrors, forwardedStreamHeaders, getDefaultCrudSchemas, getListQueryParams, getUserId, handleRaw, hasEvents, isArcError, isJsonSchema, isReadableStream, isZodSchema, keysetListResponse, listResponse, offsetListResponse, paginationSchema, pipeUIMessageStreamToReply, promoteStreamTokenToHeader, queryParams, responses, scheduleBackground, simpleEqualityMatcher, toJsonSchema, validateResourceConfig, withCompensation };
|
|
8
|
+
export { ArcError, ArcQueryParser, CircuitBreaker, CircuitBreakerError, CircuitBreakerRegistry, CircuitState, ConflictError, ForbiddenError, MONGO_DIALECT, NEUTRAL_DIALECT, NotFoundError, OrgAccessDeniedError, OrgRequiredError, RateLimitError, ServiceUnavailableError, UI_MESSAGE_STREAM_HEADERS, UnauthorizedError, ValidationError, aggregateListResponse, assertValidConfig, bareListResponse, convertOpenApiSchemas, convertRouteSchema, createCircuitBreaker, createCircuitBreakerRegistry, createDomainError, createError, createQueryParser, createStateMachine, defineCompensation, defineErrorMapper, defineGuard, deleteResponse, errorContractSchema, errorDetailSchema, formatValidationErrors, forwardedStreamHeaders, getDefaultCrudSchemas, getHeader, getListQueryParams, getUserId, handleRaw, hasEvents, isArcError, isJsonSchema, isReadableStream, isZodSchema, keysetListResponse, listResponse, offsetListResponse, openSseStream, paginationSchema, pipeUIMessageStreamToReply, promoteStreamTokenToHeader, queryParams, responses, scheduleBackground, simpleEqualityMatcher, toJsonSchema, validateResourceConfig, withCompensation };
|
|
@@ -1,75 +1,8 @@
|
|
|
1
1
|
import { m as RESERVED_QUERY_PARAMS, t as CRUD_OPERATIONS } from "./constants-DNre0wRg.mjs";
|
|
2
2
|
import { arcLog } from "./logger/index.mjs";
|
|
3
3
|
import { t as ArcError } from "./errors-DkO8Nk6-.mjs";
|
|
4
|
-
import { r as getAvailablePresets } from "./presets-
|
|
4
|
+
import { r as getAvailablePresets } from "./presets-BOe4Ugkl.mjs";
|
|
5
5
|
import { errorContractSchema as errorContractSchema$1, errorDetailSchema as errorDetailSchema$1 } from "@classytic/repo-core/errors";
|
|
6
|
-
//#region src/utils/simpleEqualityMatcher.ts
|
|
7
|
-
/**
|
|
8
|
-
* `simpleEqualityMatcher` — a minimal, dialect-agnostic flat-key equality
|
|
9
|
-
* matcher for `DataAdapter.matchesFilter` / `BaseController({ matchesFilter })`.
|
|
10
|
-
*
|
|
11
|
-
* **What it does:** for each `[key, expected]` in the filter, compares
|
|
12
|
-
* `item[key]` to `expected` via string coercion (so Mongo `ObjectId` values
|
|
13
|
-
* match their string representation) and returns `true` only if every
|
|
14
|
-
* filter entry matches. Array item values are matched implicitly (contains).
|
|
15
|
-
*
|
|
16
|
-
* **What it does NOT do:**
|
|
17
|
-
* - No `$eq` / `$ne` / `$in` / `$nin` / `$gt` / `$lt` / `$regex` / `$exists`
|
|
18
|
-
* - No `$and` / `$or`
|
|
19
|
-
* - No dot-path traversal (`"owner.id"`)
|
|
20
|
-
* - No schema-specific coercion
|
|
21
|
-
*
|
|
22
|
-
* **Why it exists:** 95%+ of arc's `_policyFilters` are produced by built-in
|
|
23
|
-
* permission helpers and are shaped like `{ ownerId: "u1" }` or
|
|
24
|
-
* `{ organizationId: "org_x" }` — flat equality. For that common shape,
|
|
25
|
-
* this helper is a safe, tested, 15-line defense-in-depth matcher that
|
|
26
|
-
* hosts using minimal repos (no `getOne(compoundFilter)` DB path) can opt
|
|
27
|
-
* into without arc shipping a full Mongo-syntax engine.
|
|
28
|
-
*
|
|
29
|
-
* **When to use:**
|
|
30
|
-
* - Your adapter/repo doesn't natively filter on `getOne(compoundFilter)`
|
|
31
|
-
* - Your `_policyFilters` are flat equality (from arc's built-in permission helpers)
|
|
32
|
-
* - You want defense-in-depth on `validateItemAccess` / `fetchDetailed`'s `getById` fallback
|
|
33
|
-
*
|
|
34
|
-
* **When NOT to use:**
|
|
35
|
-
* - Your `_policyFilters` use operators (`$in`, `$ne`, etc.) — supply a
|
|
36
|
-
* native matcher (mongokit's repo does the filter at the DB layer; for
|
|
37
|
-
* custom repos, wrap the kit's own predicate engine).
|
|
38
|
-
* - You're a mongokit / sqlitekit / Prisma user — the DB-level filter
|
|
39
|
-
* applied by `getOne(compoundFilter)` already covers this.
|
|
40
|
-
*
|
|
41
|
-
* @example
|
|
42
|
-
* ```ts
|
|
43
|
-
* import { simpleEqualityMatcher } from '@classytic/arc/utils';
|
|
44
|
-
*
|
|
45
|
-
* // On a custom adapter
|
|
46
|
-
* const adapter: DataAdapter = {
|
|
47
|
-
* repository,
|
|
48
|
-
* type: 'custom',
|
|
49
|
-
* name: 'in-memory',
|
|
50
|
-
* matchesFilter: simpleEqualityMatcher,
|
|
51
|
-
* };
|
|
52
|
-
*
|
|
53
|
-
* // Or directly on BaseController for ad-hoc controllers
|
|
54
|
-
* new BaseController(repo, { matchesFilter: simpleEqualityMatcher });
|
|
55
|
-
* ```
|
|
56
|
-
*/
|
|
57
|
-
function simpleEqualityMatcher(item, filters) {
|
|
58
|
-
if (!item || typeof item !== "object") return false;
|
|
59
|
-
const obj = item;
|
|
60
|
-
for (const [key, expected] of Object.entries(filters)) {
|
|
61
|
-
if (expected && typeof expected === "object" && !Array.isArray(expected) && Object.getPrototypeOf(expected) === Object.prototype && Object.keys(expected).some((k) => k.startsWith("$"))) return false;
|
|
62
|
-
const actual = obj[key];
|
|
63
|
-
if (Array.isArray(actual)) {
|
|
64
|
-
const expectedStr = String(expected);
|
|
65
|
-
if (!actual.some((v) => String(v) === expectedStr)) return false;
|
|
66
|
-
continue;
|
|
67
|
-
}
|
|
68
|
-
if (String(actual) !== String(expected)) return false;
|
|
69
|
-
}
|
|
70
|
-
return true;
|
|
71
|
-
}
|
|
72
|
-
//#endregion
|
|
73
6
|
//#region src/core/validateResourceConfig.ts
|
|
74
7
|
/**
|
|
75
8
|
* Resource Configuration Validator
|
|
@@ -837,6 +770,25 @@ function handleRaw(handler, statusCode = 200) {
|
|
|
837
770
|
};
|
|
838
771
|
}
|
|
839
772
|
//#endregion
|
|
773
|
+
//#region src/utils/headers.ts
|
|
774
|
+
/**
|
|
775
|
+
* Read a single header value. Lowercases the name (Node normalizes incoming
|
|
776
|
+
* header names to lowercase) and returns the FIRST value when the header
|
|
777
|
+
* was repeated.
|
|
778
|
+
*
|
|
779
|
+
* Duplicate policy: first-value is deterministic, but for security-sensitive
|
|
780
|
+
* headers (authorization material, forwarded-for chains, content-length) a
|
|
781
|
+
* duplicate usually signals smuggling or a broken proxy — consumers that
|
|
782
|
+
* care should check `Array.isArray(headers[name])` and REJECT instead of
|
|
783
|
+
* picking a winner.
|
|
784
|
+
*/
|
|
785
|
+
function getHeader(headers, name) {
|
|
786
|
+
if (!headers) return void 0;
|
|
787
|
+
const value = headers[name.toLowerCase()];
|
|
788
|
+
if (Array.isArray(value)) return value[0];
|
|
789
|
+
return value;
|
|
790
|
+
}
|
|
791
|
+
//#endregion
|
|
840
792
|
//#region src/utils/queryParser.ts
|
|
841
793
|
/**
|
|
842
794
|
* Arc Query Parser - Default URL-to-Query Parser
|
|
@@ -1778,4 +1730,4 @@ function getUserId(user) {
|
|
|
1778
1730
|
return id ? String(id) : void 0;
|
|
1779
1731
|
}
|
|
1780
1732
|
//#endregion
|
|
1781
|
-
export {
|
|
1733
|
+
export { CircuitState as A, defineGuard as C, CircuitBreaker as D, withCompensation as E, validateResourceConfig as F, createCircuitBreakerRegistry as M, assertValidConfig as N, CircuitBreakerError as O, formatValidationErrors as P, NEUTRAL_DIALECT as S, defineCompensation as T, ArcQueryParser as _, bareListResponse as a, handleRaw as b, errorDetailSchema$1 as c, keysetListResponse as d, listResponse as f, responses as g, queryParams as h, aggregateListResponse as i, createCircuitBreaker as j, CircuitBreakerRegistry as k, getDefaultCrudSchemas as l, paginationSchema as m, createStateMachine as n, deleteResponse as o, offsetListResponse as p, scheduleBackground as r, errorContractSchema$1 as s, getUserId as t, getListQueryParams as u, createQueryParser as v, defineErrorMapper as w, MONGO_DIALECT as x, getHeader as y };
|
|
@@ -26,7 +26,7 @@ interface HealthOptions {
|
|
|
26
26
|
collectHttpMetrics?: boolean;
|
|
27
27
|
}
|
|
28
28
|
declare const healthPlugin: FastifyPluginAsync<HealthOptions>;
|
|
29
|
-
declare const _default$
|
|
29
|
+
declare const _default$5: FastifyPluginAsync<HealthOptions>;
|
|
30
30
|
//#endregion
|
|
31
31
|
//#region src/plugins/caching.d.ts
|
|
32
32
|
interface CachingRule {
|
|
@@ -54,7 +54,7 @@ interface CachingOptions {
|
|
|
54
54
|
rules?: CachingRule[];
|
|
55
55
|
}
|
|
56
56
|
declare const cachingPlugin: FastifyPluginAsync<CachingOptions>;
|
|
57
|
-
declare const _default$
|
|
57
|
+
declare const _default$4: FastifyPluginAsync<CachingOptions>;
|
|
58
58
|
//#endregion
|
|
59
59
|
//#region src/plugins/sse.d.ts
|
|
60
60
|
interface SSEOptions {
|
|
@@ -79,7 +79,46 @@ interface SSEOptions {
|
|
|
79
79
|
tokenQueryParam?: string | null;
|
|
80
80
|
}
|
|
81
81
|
declare const ssePlugin: FastifyPluginAsync<SSEOptions>;
|
|
82
|
-
declare const _default$
|
|
82
|
+
declare const _default$3: FastifyPluginAsync<SSEOptions>;
|
|
83
|
+
//#endregion
|
|
84
|
+
//#region src/plugins/realtime.d.ts
|
|
85
|
+
declare const CHANGE_OPERATIONS: readonly ["created", "updated", "deleted"];
|
|
86
|
+
type ChangeOperation = (typeof CHANGE_OPERATIONS)[number];
|
|
87
|
+
interface RealtimeOptions {
|
|
88
|
+
/**
|
|
89
|
+
* Route path — must contain `:resource` (default: `/realtime/:resource`).
|
|
90
|
+
*/
|
|
91
|
+
path?: string;
|
|
92
|
+
/** Heartbeat interval in ms (default: 30000). */
|
|
93
|
+
heartbeat?: number;
|
|
94
|
+
/**
|
|
95
|
+
* Subscribable resources. Default: every registered resource — safe
|
|
96
|
+
* because each subscription runs that resource's own `list` permission;
|
|
97
|
+
* narrow it when only some resources should have a change feed at all.
|
|
98
|
+
*/
|
|
99
|
+
resources?: readonly string[];
|
|
100
|
+
/** Operations streamed (default: created + updated + deleted). */
|
|
101
|
+
operations?: readonly ChangeOperation[];
|
|
102
|
+
/**
|
|
103
|
+
* Query parameter carrying the bearer token for browser `EventSource`
|
|
104
|
+
* clients (default `'token'`, matching the sse plugin / arc-next).
|
|
105
|
+
* Set `null` to disable promotion.
|
|
106
|
+
*/
|
|
107
|
+
tokenQueryParam?: string | null;
|
|
108
|
+
/**
|
|
109
|
+
* Hard ceiling (ms) on any single connection's lifetime. Bounds
|
|
110
|
+
* permission STALENESS: the row filters are snapshotted at connect, so a
|
|
111
|
+
* caller whose permissions are revoked mid-connection keeps receiving
|
|
112
|
+
* their old view until they reconnect. Independently, arc ALWAYS closes
|
|
113
|
+
* the feed when the auth token's `exp` passes (forcing reconnect +
|
|
114
|
+
* re-authorization with fresh filters) — this option adds a cap for
|
|
115
|
+
* session/cookie auth that carries no `exp`, or to tighten below the
|
|
116
|
+
* token TTL. Omit for token-exp-only bounding.
|
|
117
|
+
*/
|
|
118
|
+
maxConnectionMs?: number;
|
|
119
|
+
}
|
|
120
|
+
declare const realtimePlugin: FastifyPluginAsync<RealtimeOptions>;
|
|
121
|
+
declare const _default$2: FastifyPluginAsync<RealtimeOptions>;
|
|
83
122
|
//#endregion
|
|
84
123
|
//#region src/plugins/metrics.d.ts
|
|
85
124
|
interface MetricsOptions {
|
|
@@ -148,4 +187,4 @@ declare module "fastify" {
|
|
|
148
187
|
declare const versioningPlugin: FastifyPluginAsync<VersioningOptions>;
|
|
149
188
|
declare const _default: FastifyPluginAsync<VersioningOptions>;
|
|
150
189
|
//#endregion
|
|
151
|
-
export {
|
|
190
|
+
export { healthPlugin as C, _default$5 as S, CachingRule as _, MetricsCollector as a, HealthCheck as b, metricsPlugin as c, _default$2 as d, realtimePlugin as f, CachingOptions as g, ssePlugin as h, MetricEntry as i, ChangeOperation as l, _default$3 as m, _default as n, MetricsOptions as o, SSEOptions as p, versioningPlugin as r, _default$1 as s, VersioningOptions as t, RealtimeOptions as u, _default$4 as v, HealthOptions as x, cachingPlugin as y };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@classytic/arc",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.23.0",
|
|
4
4
|
"description": "Resource-oriented backend framework for Fastify - clean, minimal, powerful, tree-shakable",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -222,6 +222,62 @@
|
|
|
222
222
|
"access": "public"
|
|
223
223
|
},
|
|
224
224
|
"sideEffects": false,
|
|
225
|
+
"arc": {
|
|
226
|
+
"subpathStability": {
|
|
227
|
+
".": "stable",
|
|
228
|
+
"./permissions": "stable",
|
|
229
|
+
"./cache": "stable",
|
|
230
|
+
"./types": "stable",
|
|
231
|
+
"./types/storage": "stable",
|
|
232
|
+
"./core": "stable",
|
|
233
|
+
"./presets": "stable",
|
|
234
|
+
"./presets/tenant": "stable",
|
|
235
|
+
"./presets/files-upload": "stable",
|
|
236
|
+
"./presets/search": "stable",
|
|
237
|
+
"./auth": "stable",
|
|
238
|
+
"./hooks": "stable",
|
|
239
|
+
"./middleware": "stable",
|
|
240
|
+
"./pipeline": "stable",
|
|
241
|
+
"./context": "stable",
|
|
242
|
+
"./logger": "stable",
|
|
243
|
+
"./registry": "stable",
|
|
244
|
+
"./utils": "stable",
|
|
245
|
+
"./cli": "stable",
|
|
246
|
+
"./plugins": "stable",
|
|
247
|
+
"./plugins/tracing": "stable",
|
|
248
|
+
"./audit": "stable",
|
|
249
|
+
"./docs": "stable",
|
|
250
|
+
"./encryption": "stable",
|
|
251
|
+
"./idempotency": "stable",
|
|
252
|
+
"./idempotency/redis": "stable",
|
|
253
|
+
"./events": "stable",
|
|
254
|
+
"./events/redis": "stable",
|
|
255
|
+
"./events/redis-stream": "stable",
|
|
256
|
+
"./testing": "stable",
|
|
257
|
+
"./testing/storage": "stable",
|
|
258
|
+
"./factory": "stable",
|
|
259
|
+
"./schemas": "stable",
|
|
260
|
+
"./migrations": "stable",
|
|
261
|
+
"./discovery": "stable",
|
|
262
|
+
"./integrations": "stable",
|
|
263
|
+
"./integrations/streamline": "stable",
|
|
264
|
+
"./integrations/websocket": "stable",
|
|
265
|
+
"./integrations/websocket-redis": "stable",
|
|
266
|
+
"./integrations/websocket-pushref-redis": "stable",
|
|
267
|
+
"./integrations/jobs": "stable",
|
|
268
|
+
"./integrations/event-gateway": "experimental",
|
|
269
|
+
"./integrations/webhooks": "stable",
|
|
270
|
+
"./auth/redis": "stable",
|
|
271
|
+
"./plugins/response-cache": "stable",
|
|
272
|
+
"./scope": "stable",
|
|
273
|
+
"./mcp": "stable",
|
|
274
|
+
"./mcp/testing": "stable",
|
|
275
|
+
"./scim": "stable",
|
|
276
|
+
"./auth/audit": "stable",
|
|
277
|
+
"./sync": "experimental",
|
|
278
|
+
"./usage": "experimental"
|
|
279
|
+
}
|
|
280
|
+
},
|
|
225
281
|
"files": [
|
|
226
282
|
"dist",
|
|
227
283
|
"bin",
|
|
@@ -253,14 +309,15 @@
|
|
|
253
309
|
"release": "npm run push -- main && npm run release:tag && npm publish",
|
|
254
310
|
"check:peer-skew": "node scripts/check-peer-skew.mjs",
|
|
255
311
|
"check:docs-drift": "node scripts/check-docs-drift.mjs",
|
|
256
|
-
"
|
|
312
|
+
"check:boundaries": "node scripts/check-boundaries.mjs",
|
|
313
|
+
"prepublishOnly": "npm run check:peer-skew && npm run check:docs-drift && npm run check:boundaries && npm run typecheck && npm run lint && npm run build && npm run test:ci && npm run smoke"
|
|
257
314
|
},
|
|
258
315
|
"engines": {
|
|
259
316
|
"node": ">=22"
|
|
260
317
|
},
|
|
261
318
|
"peerDependencies": {
|
|
262
319
|
"@classytic/primitives": ">=0.9.0",
|
|
263
|
-
"@classytic/repo-core": ">=0.
|
|
320
|
+
"@classytic/repo-core": ">=0.14.0",
|
|
264
321
|
"@classytic/streamline": ">=2.7.0",
|
|
265
322
|
"@fastify/cors": ">=11.0.0",
|
|
266
323
|
"@fastify/helmet": ">=13.0.0",
|
|
@@ -379,11 +436,14 @@
|
|
|
379
436
|
"@better-auth/mongo-adapter": "^1.6.19",
|
|
380
437
|
"@biomejs/biome": "^2.5.0",
|
|
381
438
|
"@classytic/dev-tools": "^0.2.0",
|
|
439
|
+
"@classytic/gitkit": ">=0.1.0",
|
|
382
440
|
"@classytic/mongokit": "^3.22.0",
|
|
441
|
+
"@classytic/pgkit": "^0.1.0",
|
|
383
442
|
"@classytic/primitives": "^0.9.1",
|
|
384
|
-
"@classytic/repo-core": "^0.
|
|
443
|
+
"@classytic/repo-core": "^0.14.0",
|
|
385
444
|
"@classytic/sqlitekit": "^0.6.0",
|
|
386
445
|
"@classytic/streamline": "^2.7.0",
|
|
446
|
+
"@electric-sql/pglite": "^0.5.4",
|
|
387
447
|
"@fastify/cors": "^11.2.0",
|
|
388
448
|
"@fastify/helmet": "^13.0.2",
|
|
389
449
|
"@fastify/jwt": "^10.1.0",
|