@classytic/arc 2.10.3 → 2.11.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 -1
- package/dist/{BaseController-CbKKIflT.mjs → BaseController-JNV08qOT.mjs} +595 -537
- package/dist/{queryCachePlugin-BKbWjgDG.d.mts → QueryCache-DOBNHBE0.d.mts} +2 -32
- package/dist/actionPermissions-C8YYU92K.mjs +22 -0
- package/dist/adapters/index.d.mts +2 -2
- package/dist/adapters/index.mjs +1 -1
- package/dist/{adapters-BXY4i-hw.mjs → adapters-D0tT2Tyo.mjs} +54 -0
- package/dist/audit/index.d.mts +2 -2
- package/dist/audit/index.mjs +15 -17
- package/dist/auth/index.d.mts +4 -4
- package/dist/auth/index.mjs +3 -3
- package/dist/auth/redis-session.d.mts +1 -1
- package/dist/{betterAuthOpenApi-BBRVhjQN.mjs → betterAuthOpenApi-DwxtK3uG.mjs} +1 -1
- package/dist/cache/index.d.mts +3 -2
- package/dist/cache/index.mjs +3 -3
- package/dist/cli/commands/docs.mjs +2 -2
- package/dist/cli/commands/generate.mjs +37 -27
- package/dist/cli/commands/init.mjs +47 -34
- package/dist/cli/commands/introspect.mjs +1 -1
- package/dist/context/index.d.mts +58 -0
- package/dist/context/index.mjs +2 -0
- package/dist/core/index.d.mts +3 -3
- package/dist/core/index.mjs +4 -3
- package/dist/core-DXdSSFW-.mjs +1037 -0
- package/dist/createActionRouter-BwaSM0No.mjs +166 -0
- package/dist/{createApp-BuvPma24.mjs → createApp-DvNYEhpb.mjs} +118 -36
- package/dist/docs/index.d.mts +2 -2
- package/dist/docs/index.mjs +1 -1
- package/dist/{elevation-C7hgL_aI.mjs → elevation-DOFoxoDs.mjs} +1 -1
- package/dist/errorHandler-Co3lnVmJ.d.mts +114 -0
- package/dist/{eventPlugin-DCUjuiQT.mjs → eventPlugin--5HIkdPU.mjs} +1 -1
- package/dist/{eventPlugin-CxWgpd6K.d.mts → eventPlugin-CUNjYYRY.d.mts} +1 -1
- package/dist/events/index.d.mts +4 -4
- package/dist/events/index.mjs +69 -51
- 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-Lo1VUDpt.d.mts → fields-C8Y0XLAu.d.mts} +1 -1
- package/dist/hooks/index.d.mts +1 -1
- package/dist/hooks/index.mjs +1 -1
- package/dist/idempotency/index.d.mts +3 -3
- package/dist/idempotency/index.mjs +38 -27
- package/dist/idempotency/redis.d.mts +1 -1
- package/dist/{index-ChIw3776.d.mts → index-BYCqHCVu.d.mts} +4 -4
- package/dist/{index-Cl0uoKd5.d.mts → index-Cm0vUrr_.d.mts} +2100 -1688
- package/dist/{index-DStwgFUK.d.mts → index-DAushRTt.d.mts} +29 -10
- package/dist/index-DsJ1MNfC.d.mts +1179 -0
- package/dist/{index-8qw4y6ff.d.mts → index-t8pLpPFW.d.mts} +13 -10
- package/dist/index.d.mts +7 -251
- package/dist/index.mjs +8 -128
- 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/mcp/index.d.mts +2 -2
- 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/streamline.d.mts +46 -5
- package/dist/integrations/streamline.mjs +50 -21
- package/dist/integrations/websocket-redis.d.mts +1 -1
- package/dist/integrations/websocket.d.mts +2 -154
- package/dist/integrations/websocket.mjs +292 -224
- package/dist/{keys-qcD-TVJl.mjs → keys-CARyUjiR.mjs} +2 -0
- package/dist/{loadResources-BAzJItAJ.mjs → loadResources-YNwKHvRA.mjs} +3 -1
- package/dist/logger/index.d.mts +81 -0
- package/dist/{logger-DLg8-Ueg.mjs → logger/index.mjs} +1 -6
- package/dist/middleware/index.d.mts +109 -0
- package/dist/middleware/index.mjs +70 -0
- package/dist/multipartBody-CvTR1Un6.mjs +123 -0
- package/dist/{openapi-B5F8AddX.mjs → openapi-C0L9ar7m.mjs} +9 -7
- package/dist/org/index.d.mts +2 -2
- package/dist/permissions/index.d.mts +2 -2
- package/dist/permissions/index.mjs +1 -3
- package/dist/{permissions-Dk6mshja.mjs → permissions-B4vU9L0Q.mjs} +220 -2
- package/dist/pipe-DVoIheVC.mjs +62 -0
- package/dist/pipeline/index.d.mts +62 -0
- package/dist/pipeline/index.mjs +53 -0
- package/dist/plugins/index.d.mts +25 -5
- package/dist/plugins/index.mjs +10 -10
- package/dist/plugins/response-cache.mjs +1 -1
- package/dist/plugins/tracing-entry.d.mts +1 -1
- package/dist/plugins/tracing-entry.mjs +42 -24
- package/dist/presets/filesUpload.d.mts +4 -4
- package/dist/presets/filesUpload.mjs +255 -1
- package/dist/presets/index.d.mts +1 -1
- package/dist/presets/index.mjs +2 -2
- package/dist/presets/multiTenant.d.mts +1 -1
- package/dist/presets/multiTenant.mjs +48 -8
- package/dist/presets/search.d.mts +2 -2
- package/dist/presets/search.mjs +1 -1
- package/dist/{presets-fLJVXdVn.mjs → presets-k604Lj99.mjs} +1 -1
- package/dist/queryCachePlugin-BUXBSm4F.d.mts +34 -0
- package/dist/{queryCachePlugin-DQCEfJis.mjs → queryCachePlugin-Bq6bO6vc.mjs} +3 -3
- package/dist/{redis-DqyeggCa.d.mts → redis-Cm1gnRDf.d.mts} +1 -1
- package/dist/{redis-stream-CakIQmwR.d.mts → redis-stream-CM8TXTix.d.mts} +1 -1
- package/dist/registry/index.d.mts +1 -1
- package/dist/registry/index.mjs +2 -2
- package/dist/{requestContext-xHIKedG6.mjs → requestContext-CfRkaxwf.mjs} +1 -1
- package/dist/{resourceToTools-BElv3xPT.mjs → resourceToTools--okX6QBr.mjs} +534 -415
- package/dist/routerShared-DeESFp4a.mjs +515 -0
- package/dist/schemaIR-BlG9bY7v.mjs +137 -0
- package/dist/scope/index.d.mts +2 -2
- package/dist/scope/index.mjs +1 -1
- package/dist/{sse-yBCgOLGu.mjs → sse-V7aXc3bW.mjs} +1 -1
- package/dist/{store-helpers-ZCSMJJAX.mjs → store-helpers-BhrzxvyQ.mjs} +4 -0
- package/dist/testing/index.d.mts +367 -711
- package/dist/testing/index.mjs +646 -1434
- package/dist/testing/storageContract.d.mts +1 -1
- package/dist/{tracing-65B51Dw3.d.mts → tracing-DokiEsuz.d.mts} +9 -4
- package/dist/types/index.d.mts +5 -5
- package/dist/types/index.mjs +1 -3
- package/dist/types/storage.d.mts +1 -1
- package/dist/{types-Co8k3NyS.d.mts → types-CgikqKAj.d.mts} +133 -21
- package/dist/{types-Btdda02s.d.mts → types-D9NqiYIw.d.mts} +1 -1
- package/dist/utils/index.d.mts +2 -898
- package/dist/utils/index.mjs +4 -5
- package/dist/utils-D3Yxnrwr.mjs +1639 -0
- package/dist/versioning-M9lNLhO8.d.mts +117 -0
- package/dist/websocket-CyJ1VIFI.d.mts +186 -0
- package/package.json +26 -8
- package/skills/arc/SKILL.md +124 -39
- package/skills/arc/references/testing.md +212 -183
- package/dist/applyPermissionResult-QhV1Pa-g.mjs +0 -37
- package/dist/core-CcR01lup.mjs +0 -1411
- package/dist/createActionRouter-Bp_5c_2b.mjs +0 -249
- package/dist/errorHandler-DRQ3EqfL.d.mts +0 -218
- package/dist/errors-CCSsMpXE.d.mts +0 -140
- package/dist/fields-bxkeltzz.mjs +0 -126
- package/dist/filesUpload-t21LS-py.mjs +0 -377
- package/dist/queryParser-DBqBB6AC.mjs +0 -352
- package/dist/types-Csi3FLfq.mjs +0 -27
- package/dist/utils-B2fNOD_i.mjs +0 -929
- /package/dist/{EventTransport-CUw5NNWe.d.mts → EventTransport-CfVEGaEl.d.mts} +0 -0
- /package/dist/{HookSystem-BNYKnrXF.mjs → HookSystem-CGsMd6oK.mjs} +0 -0
- /package/dist/{ResourceRegistry-BPd6NQDm.mjs → ResourceRegistry-DkAeAuTX.mjs} +0 -0
- /package/dist/{caching-CBpK_SCM.mjs → caching-CheW3m-S.mjs} +0 -0
- /package/dist/{elevation-C5SwtkAn.d.mts → elevation-s5ykdNHr.d.mts} +0 -0
- /package/dist/{errorHandler-Bb49BvPD.mjs → errorHandler-BQm8ZxTK.mjs} +0 -0
- /package/dist/{externalPaths-BQ8QijNH.d.mts → externalPaths-Bapitwvd.d.mts} +0 -0
- /package/dist/{interface-CSbZdv_3.d.mts → interface-CkkWm5uR.d.mts} +0 -0
- /package/dist/{interface-D218ikEo.d.mts → interface-Da0r7Lna.d.mts} +0 -0
- /package/dist/{memory-B5Amv9A1.mjs → memory-DikHSvWa.mjs} +0 -0
- /package/dist/{metrics-DuhiSEZI.mjs → metrics-Csh4nsvv.mjs} +0 -0
- /package/dist/{pluralize-A0tWEl1K.mjs → pluralize-BneOJkpi.mjs} +0 -0
- /package/dist/{registry-B3lRFBWo.mjs → registry-D63ee7fl.mjs} +0 -0
- /package/dist/{replyHelpers-CXtJDAZ0.mjs → replyHelpers-ByllIXXV.mjs} +0 -0
- /package/dist/{schemaConverter-BxFDdtXu.mjs → schemaConverter-B0oKLuqI.mjs} +0 -0
- /package/dist/{sessionManager-BkzVU8h2.d.mts → sessionManager-D-oNWHz3.d.mts} +0 -0
- /package/dist/{storage-CVk_SEn2.d.mts → storage-BwGQXUpd.d.mts} +0 -0
- /package/dist/{typeGuards-Cj5Rgvlg.mjs → typeGuards-CcFZXgU7.mjs} +0 -0
- /package/dist/{types-BD85MlEK.d.mts → types-tgR4Pt8F.d.mts} +0 -0
- /package/dist/{versioning-C2U_bLY0.mjs → versioning-CGPjkqAg.mjs} +0 -0
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { Ct as OperationFilter, Dt as Transform, Et as PipelineStep, St as NextFunction, Tt as PipelineContext, _t as IControllerResponse, bt as Guard, wt as PipelineConfig, xt as Interceptor } from "../index-Cm0vUrr_.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/pipeline/guard.d.ts
|
|
4
|
+
interface GuardOptions {
|
|
5
|
+
operations?: OperationFilter;
|
|
6
|
+
handler: (ctx: PipelineContext) => boolean | Promise<boolean>;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Create a named guard.
|
|
10
|
+
*
|
|
11
|
+
* @param name - Guard name (for debugging/introspection)
|
|
12
|
+
* @param handlerOrOptions - Handler function or options object
|
|
13
|
+
*/
|
|
14
|
+
declare function guard(name: string, handlerOrOptions: ((ctx: PipelineContext) => boolean | Promise<boolean>) | GuardOptions): Guard;
|
|
15
|
+
//#endregion
|
|
16
|
+
//#region src/pipeline/intercept.d.ts
|
|
17
|
+
interface InterceptOptions {
|
|
18
|
+
operations?: OperationFilter;
|
|
19
|
+
handler: (ctx: PipelineContext, next: NextFunction) => Promise<IControllerResponse<unknown>>;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Create a named interceptor.
|
|
23
|
+
*
|
|
24
|
+
* @param name - Interceptor name (for debugging/introspection)
|
|
25
|
+
* @param handlerOrOptions - Handler function or options object
|
|
26
|
+
*/
|
|
27
|
+
declare function intercept(name: string, handlerOrOptions: ((ctx: PipelineContext, next: NextFunction) => Promise<IControllerResponse<unknown>>) | InterceptOptions): Interceptor;
|
|
28
|
+
//#endregion
|
|
29
|
+
//#region src/pipeline/pipe.d.ts
|
|
30
|
+
/**
|
|
31
|
+
* Compose pipeline steps into an ordered array.
|
|
32
|
+
* Accepts guards, transforms, and interceptors in any order.
|
|
33
|
+
*/
|
|
34
|
+
declare function pipe(...steps: PipelineStep[]): PipelineStep[];
|
|
35
|
+
/**
|
|
36
|
+
* Execute a pipeline against a request context.
|
|
37
|
+
*
|
|
38
|
+
* This is the core runtime that createCrudRouter uses to execute pipelines.
|
|
39
|
+
* External usage is not needed — this is wired automatically when `pipe` is set.
|
|
40
|
+
*
|
|
41
|
+
* @param steps - Pipeline steps to execute
|
|
42
|
+
* @param ctx - The pipeline context (extends IRequestContext)
|
|
43
|
+
* @param handler - The actual controller method to call
|
|
44
|
+
* @param operation - The CRUD operation name
|
|
45
|
+
* @returns The controller response (possibly modified by interceptors)
|
|
46
|
+
*/
|
|
47
|
+
declare function executePipeline(steps: PipelineStep[], ctx: PipelineContext, handler: (ctx: PipelineContext) => Promise<IControllerResponse<unknown>>, operation: string): Promise<IControllerResponse<unknown>>;
|
|
48
|
+
//#endregion
|
|
49
|
+
//#region src/pipeline/transform.d.ts
|
|
50
|
+
interface TransformOptions {
|
|
51
|
+
operations?: OperationFilter;
|
|
52
|
+
handler: (ctx: PipelineContext) => PipelineContext | undefined | Promise<PipelineContext | undefined>;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Create a named transform.
|
|
56
|
+
*
|
|
57
|
+
* @param name - Transform name (for debugging/introspection)
|
|
58
|
+
* @param handlerOrOptions - Handler function or options object
|
|
59
|
+
*/
|
|
60
|
+
declare function transform(name: string, handlerOrOptions: ((ctx: PipelineContext) => PipelineContext | undefined | Promise<PipelineContext | undefined>) | TransformOptions): Transform;
|
|
61
|
+
//#endregion
|
|
62
|
+
export { type Guard, type Interceptor, type NextFunction, type OperationFilter, type PipelineConfig, type PipelineContext, type PipelineStep, type Transform, executePipeline, guard, intercept, pipe, transform };
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { n as pipe, t as executePipeline } from "../pipe-DVoIheVC.mjs";
|
|
2
|
+
//#region src/pipeline/guard.ts
|
|
3
|
+
/**
|
|
4
|
+
* Create a named guard.
|
|
5
|
+
*
|
|
6
|
+
* @param name - Guard name (for debugging/introspection)
|
|
7
|
+
* @param handlerOrOptions - Handler function or options object
|
|
8
|
+
*/
|
|
9
|
+
function guard(name, handlerOrOptions) {
|
|
10
|
+
const opts = typeof handlerOrOptions === "function" ? { handler: handlerOrOptions } : handlerOrOptions;
|
|
11
|
+
return {
|
|
12
|
+
_type: "guard",
|
|
13
|
+
name,
|
|
14
|
+
operations: opts.operations,
|
|
15
|
+
handler: opts.handler
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
//#endregion
|
|
19
|
+
//#region src/pipeline/intercept.ts
|
|
20
|
+
/**
|
|
21
|
+
* Create a named interceptor.
|
|
22
|
+
*
|
|
23
|
+
* @param name - Interceptor name (for debugging/introspection)
|
|
24
|
+
* @param handlerOrOptions - Handler function or options object
|
|
25
|
+
*/
|
|
26
|
+
function intercept(name, handlerOrOptions) {
|
|
27
|
+
const opts = typeof handlerOrOptions === "function" ? { handler: handlerOrOptions } : handlerOrOptions;
|
|
28
|
+
return {
|
|
29
|
+
_type: "interceptor",
|
|
30
|
+
name,
|
|
31
|
+
operations: opts.operations,
|
|
32
|
+
handler: opts.handler
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
//#endregion
|
|
36
|
+
//#region src/pipeline/transform.ts
|
|
37
|
+
/**
|
|
38
|
+
* Create a named transform.
|
|
39
|
+
*
|
|
40
|
+
* @param name - Transform name (for debugging/introspection)
|
|
41
|
+
* @param handlerOrOptions - Handler function or options object
|
|
42
|
+
*/
|
|
43
|
+
function transform(name, handlerOrOptions) {
|
|
44
|
+
const opts = typeof handlerOrOptions === "function" ? { handler: handlerOrOptions } : handlerOrOptions;
|
|
45
|
+
return {
|
|
46
|
+
_type: "transform",
|
|
47
|
+
name,
|
|
48
|
+
operations: opts.operations,
|
|
49
|
+
handler: opts.handler
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
//#endregion
|
|
53
|
+
export { executePipeline, guard, intercept, pipe, transform };
|
package/dist/plugins/index.d.mts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { t as ExternalOpenApiPaths } from "../externalPaths-
|
|
3
|
-
import {
|
|
4
|
-
import { t as
|
|
1
|
+
import { Pt as AnyRecord, Q as MiddlewareConfig, ft as RouteSchemaOptions, gn as HookSystem, lt as RouteDefinition, tt as PresetHook, z as ResourceRegistry } from "../index-Cm0vUrr_.mjs";
|
|
2
|
+
import { t as ExternalOpenApiPaths } from "../externalPaths-Bapitwvd.mjs";
|
|
3
|
+
import { a as MetricsCollector, c as metricsPlugin, d as ssePlugin, f as CachingOptions, h as cachingPlugin, i as MetricEntry, l as SSEOptions, m as _default$1, n as _default$7, o as MetricsOptions, p as CachingRule, r as versioningPlugin, s as _default$4, t as VersioningOptions, u as _default$6 } from "../versioning-M9lNLhO8.mjs";
|
|
4
|
+
import { i as errorHandlerPlugin, n as ErrorMapper, r as defaultIsDuplicateKeyError, t as ErrorHandlerOptions } from "../errorHandler-Co3lnVmJ.mjs";
|
|
5
|
+
import { t as TracingOptions } from "../tracing-DokiEsuz.mjs";
|
|
5
6
|
import { FastifyInstance, FastifyPluginAsync } from "fastify";
|
|
6
7
|
import * as _$node_stream0 from "node:stream";
|
|
7
8
|
|
|
@@ -34,7 +35,26 @@ interface ArcCore {
|
|
|
34
35
|
}
|
|
35
36
|
declare module "fastify" {
|
|
36
37
|
interface FastifyInstance {
|
|
37
|
-
|
|
38
|
+
/**
|
|
39
|
+
* Arc core decorator. Optional because:
|
|
40
|
+
*
|
|
41
|
+
* 1. Consumers who import any `@classytic/arc/*` subpath get this module
|
|
42
|
+
* augmentation merged into every `FastifyInstance` they see — even
|
|
43
|
+
* instances on apps that never register {@link arcCorePlugin}. Making
|
|
44
|
+
* it required would force those apps to assert or guard every
|
|
45
|
+
* property access.
|
|
46
|
+
* 2. Hosts that extend `FastifyInstance` to narrow `arc` to their own
|
|
47
|
+
* type (`interface X extends FastifyInstance { arc?: MyArc }`) were
|
|
48
|
+
* previously blocked because non-optional `arc: ArcCore` conflicts
|
|
49
|
+
* with the re-declaration. An optional field lets hosts re-declare
|
|
50
|
+
* with a compatible subtype without fighting TS.
|
|
51
|
+
*
|
|
52
|
+
* Inside Arc's own code, any call site that runs *after* `arcCorePlugin`
|
|
53
|
+
* has registered treats this as non-null — see the call sites in
|
|
54
|
+
* `factory/registerAuth.ts` and `factory/createApp.ts` which assert
|
|
55
|
+
* with `fastify.arc!` or narrow explicitly.
|
|
56
|
+
*/
|
|
57
|
+
arc?: ArcCore;
|
|
38
58
|
}
|
|
39
59
|
}
|
|
40
60
|
declare const arcCorePlugin: FastifyPluginAsync<ArcCorePluginOptions>;
|
package/dist/plugins/index.mjs
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import { p as MUTATION_OPERATIONS } from "../constants-BhY1OHoH.mjs";
|
|
2
2
|
import { o as getOrgId } from "../types-AOD8fxIw.mjs";
|
|
3
|
-
import { t as requestContext } from "../requestContext-
|
|
4
|
-
import { t as hasEvents } from "../typeGuards-
|
|
5
|
-
import { t as HookSystem } from "../HookSystem-
|
|
6
|
-
import { t as ResourceRegistry } from "../ResourceRegistry-
|
|
7
|
-
import { n as caching_default, t as cachingPlugin } from "../caching-
|
|
8
|
-
import { n as errorHandlerPlugin, t as defaultIsDuplicateKeyError } from "../errorHandler-
|
|
9
|
-
import { n as metrics_default, t as metricsPlugin } from "../metrics-
|
|
10
|
-
import { t as replyHelpersPlugin } from "../replyHelpers-
|
|
11
|
-
import { n as sse_default, t as ssePlugin } from "../sse-
|
|
12
|
-
import { n as versioning_default, t as versioningPlugin } from "../versioning-
|
|
3
|
+
import { t as requestContext } from "../requestContext-CfRkaxwf.mjs";
|
|
4
|
+
import { t as hasEvents } from "../typeGuards-CcFZXgU7.mjs";
|
|
5
|
+
import { t as HookSystem } from "../HookSystem-CGsMd6oK.mjs";
|
|
6
|
+
import { t as ResourceRegistry } from "../ResourceRegistry-DkAeAuTX.mjs";
|
|
7
|
+
import { n as caching_default, t as cachingPlugin } from "../caching-CheW3m-S.mjs";
|
|
8
|
+
import { n as errorHandlerPlugin, t as defaultIsDuplicateKeyError } from "../errorHandler-BQm8ZxTK.mjs";
|
|
9
|
+
import { n as metrics_default, t as metricsPlugin } from "../metrics-Csh4nsvv.mjs";
|
|
10
|
+
import { t as replyHelpersPlugin } from "../replyHelpers-ByllIXXV.mjs";
|
|
11
|
+
import { n as sse_default, t as ssePlugin } from "../sse-V7aXc3bW.mjs";
|
|
12
|
+
import { n as versioning_default, t as versioningPlugin } from "../versioning-CGPjkqAg.mjs";
|
|
13
13
|
import { randomUUID } from "node:crypto";
|
|
14
14
|
import fp from "fastify-plugin";
|
|
15
15
|
//#region src/core/arcCorePlugin.ts
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as traced, i as isTracingAvailable, n as _default, r as createSpan, t as TracingOptions } from "../tracing-
|
|
1
|
+
import { a as traced, i as isTracingAvailable, n as _default, r as createSpan, t as TracingOptions } from "../tracing-DokiEsuz.mjs";
|
|
2
2
|
export { type TracingOptions, createSpan, isTracingAvailable, traced, _default as tracingPlugin };
|
|
@@ -14,6 +14,20 @@ import fp from "fastify-plugin";
|
|
|
14
14
|
* serviceName: 'my-api',
|
|
15
15
|
* exporterUrl: 'http://localhost:4318/v1/traces', // OTLP endpoint
|
|
16
16
|
* });
|
|
17
|
+
*
|
|
18
|
+
* ## Type strategy
|
|
19
|
+
*
|
|
20
|
+
* `@opentelemetry/api` is the stable surface — its types are type-only
|
|
21
|
+
* imported here so hook handlers, `createSpan`, and `traced` don't hand
|
|
22
|
+
* out `any` for Tracer / Span. Only the runtime-loaded module BODIES
|
|
23
|
+
* (`trace.getTracer`, `context.with`, etc.) go through the optional-require
|
|
24
|
+
* path; everything downstream keeps its shape through the plugin.
|
|
25
|
+
*
|
|
26
|
+
* The SDK-side factories (`NodeTracerProvider`, `BatchSpanProcessor`,
|
|
27
|
+
* `OTLPTraceExporter`) aren't in @classytic/arc's devDeps and aren't
|
|
28
|
+
* type-importable without adding install weight, so those retain
|
|
29
|
+
* minimal constructor-style `unknown`-valued boxes. If a future refactor
|
|
30
|
+
* moves SDK types into devDependencies we can tighten further.
|
|
17
31
|
*/
|
|
18
32
|
const require = createRequire(import.meta.url);
|
|
19
33
|
let trace;
|
|
@@ -33,8 +47,8 @@ try {
|
|
|
33
47
|
NodeTracerProvider = sdkNode.NodeTracerProvider;
|
|
34
48
|
BatchSpanProcessor = sdkNode.BatchSpanProcessor;
|
|
35
49
|
OTLPTraceExporter = require("@opentelemetry/exporter-trace-otlp-http").OTLPTraceExporter;
|
|
36
|
-
require("@opentelemetry/instrumentation-http")
|
|
37
|
-
require("@opentelemetry/instrumentation-mongodb")
|
|
50
|
+
require("@opentelemetry/instrumentation-http");
|
|
51
|
+
require("@opentelemetry/instrumentation-mongodb");
|
|
38
52
|
getNodeAutoInstrumentations = require("@opentelemetry/auto-instrumentations-node").getNodeAutoInstrumentations;
|
|
39
53
|
isAvailable = true;
|
|
40
54
|
} catch (_e) {}
|
|
@@ -42,9 +56,9 @@ try {
|
|
|
42
56
|
* Create a tracer provider
|
|
43
57
|
*/
|
|
44
58
|
function createTracerProvider(options) {
|
|
45
|
-
if (!isAvailable) return null;
|
|
59
|
+
if (!isAvailable || !NodeTracerProvider || !BatchSpanProcessor || !OTLPTraceExporter) return null;
|
|
46
60
|
const { serviceName = "@classytic/arc", serviceVersion, exporterUrl = "http://localhost:4318/v1/traces" } = options;
|
|
47
|
-
const resolvedVersion = serviceVersion ?? "2.
|
|
61
|
+
const resolvedVersion = serviceVersion ?? "2.11.0";
|
|
48
62
|
const exporter = new OTLPTraceExporter({ url: exporterUrl });
|
|
49
63
|
const provider = new NodeTracerProvider({ resource: { attributes: {
|
|
50
64
|
"service.name": serviceName,
|
|
@@ -59,11 +73,14 @@ function createTracerProvider(options) {
|
|
|
59
73
|
*/
|
|
60
74
|
async function tracingPlugin(fastify, options = {}) {
|
|
61
75
|
const { serviceName = "@classytic/arc", autoInstrumentation = true, sampleRate = 1 } = options;
|
|
62
|
-
if (!isAvailable) {
|
|
76
|
+
if (!isAvailable || !trace || !context || !SpanStatusCode) {
|
|
63
77
|
fastify.log.warn("OpenTelemetry not installed. Tracing disabled.");
|
|
64
78
|
fastify.log.warn("Install: npm install @opentelemetry/api @opentelemetry/sdk-node");
|
|
65
79
|
return;
|
|
66
80
|
}
|
|
81
|
+
const otelTrace = trace;
|
|
82
|
+
const otelContext = context;
|
|
83
|
+
const otelStatus = SpanStatusCode;
|
|
67
84
|
if (!createTracerProvider(options)) return;
|
|
68
85
|
if (autoInstrumentation && getNodeAutoInstrumentations) {
|
|
69
86
|
const instrumentations = getNodeAutoInstrumentations({
|
|
@@ -73,7 +90,7 @@ async function tracingPlugin(fastify, options = {}) {
|
|
|
73
90
|
for (const instrumentation of instrumentations) instrumentation.enable();
|
|
74
91
|
fastify.log.debug("OpenTelemetry auto-instrumentation enabled");
|
|
75
92
|
}
|
|
76
|
-
const tracer =
|
|
93
|
+
const tracer = otelTrace.getTracer(serviceName);
|
|
77
94
|
fastify.decorateRequest("tracer", void 0);
|
|
78
95
|
fastify.addHook("onRequest", async (request, _reply) => {
|
|
79
96
|
if (Math.random() > sampleRate) return;
|
|
@@ -92,7 +109,7 @@ async function tracingPlugin(fastify, options = {}) {
|
|
|
92
109
|
tracer,
|
|
93
110
|
currentSpan: span
|
|
94
111
|
};
|
|
95
|
-
|
|
112
|
+
otelContext.with(otelTrace.setSpan(otelContext.active(), span), () => {});
|
|
96
113
|
});
|
|
97
114
|
fastify.addHook("onResponse", async (request, reply) => {
|
|
98
115
|
if (!request.tracer?.currentSpan) return;
|
|
@@ -101,11 +118,11 @@ async function tracingPlugin(fastify, options = {}) {
|
|
|
101
118
|
"http.status_code": reply.statusCode,
|
|
102
119
|
"http.response_content_length": reply.getHeader("content-length")
|
|
103
120
|
});
|
|
104
|
-
|
|
105
|
-
code:
|
|
121
|
+
const status = reply.statusCode >= 500 ? {
|
|
122
|
+
code: otelStatus.ERROR,
|
|
106
123
|
message: `HTTP ${reply.statusCode}`
|
|
107
|
-
}
|
|
108
|
-
|
|
124
|
+
} : { code: otelStatus.OK };
|
|
125
|
+
span.setStatus(status);
|
|
109
126
|
span.end();
|
|
110
127
|
});
|
|
111
128
|
fastify.addHook("onError", async (request, _reply, error) => {
|
|
@@ -113,7 +130,7 @@ async function tracingPlugin(fastify, options = {}) {
|
|
|
113
130
|
const span = request.tracer.currentSpan;
|
|
114
131
|
span.recordException(error);
|
|
115
132
|
span.setStatus({
|
|
116
|
-
code:
|
|
133
|
+
code: otelStatus.ERROR,
|
|
117
134
|
message: error.message
|
|
118
135
|
});
|
|
119
136
|
});
|
|
@@ -133,22 +150,23 @@ async function tracingPlugin(fastify, options = {}) {
|
|
|
133
150
|
* }
|
|
134
151
|
*/
|
|
135
152
|
function createSpan(request, name, fn, attributes) {
|
|
136
|
-
if (!request.tracer) return fn(null);
|
|
153
|
+
if (!request.tracer || !trace || !context || !SpanStatusCode) return fn(null);
|
|
154
|
+
const otelTrace = trace;
|
|
155
|
+
const otelContext = context;
|
|
156
|
+
const otelStatus = SpanStatusCode;
|
|
137
157
|
const { tracer, currentSpan } = request.tracer;
|
|
138
|
-
const span = tracer.startSpan(name, {
|
|
139
|
-
|
|
140
|
-
attributes: attributes || {}
|
|
141
|
-
}, trace.setSpan(context.active(), currentSpan));
|
|
142
|
-
return context.with(trace.setSpan(context.active(), span), async () => {
|
|
158
|
+
const span = tracer.startSpan(name, { attributes: attributes ?? {} }, otelTrace.setSpan(otelContext.active(), currentSpan));
|
|
159
|
+
return otelContext.with(otelTrace.setSpan(otelContext.active(), span), async () => {
|
|
143
160
|
try {
|
|
144
161
|
const result = await fn(span);
|
|
145
|
-
span.setStatus({ code:
|
|
162
|
+
span.setStatus({ code: otelStatus.OK });
|
|
146
163
|
return result;
|
|
147
164
|
} catch (error) {
|
|
148
|
-
|
|
165
|
+
const err = error;
|
|
166
|
+
span.recordException(err);
|
|
149
167
|
span.setStatus({
|
|
150
|
-
code:
|
|
151
|
-
message:
|
|
168
|
+
code: otelStatus.ERROR,
|
|
169
|
+
message: err.message
|
|
152
170
|
});
|
|
153
171
|
throw error;
|
|
154
172
|
} finally {
|
|
@@ -171,9 +189,9 @@ function traced(spanName) {
|
|
|
171
189
|
return (target, propertyKey, descriptor) => {
|
|
172
190
|
const originalMethod = descriptor.value;
|
|
173
191
|
descriptor.value = async function(...args) {
|
|
174
|
-
const request = args.find((arg) => arg
|
|
192
|
+
const request = args.find((arg) => !!(arg && typeof arg === "object" && "tracer" in arg && arg.tracer));
|
|
175
193
|
if (!request?.tracer) return originalMethod.apply(this, args);
|
|
176
|
-
return createSpan(request, spanName
|
|
194
|
+
return createSpan(request, spanName ?? `${target.constructor.name}.${propertyKey}`, async (span) => {
|
|
177
195
|
if (span) {
|
|
178
196
|
span.setAttribute("db.operation", propertyKey);
|
|
179
197
|
span.setAttribute("db.system", "mongodb");
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { a as StorageReadResult, i as StorageReadRange, n as StorageContext, o as StorageUploadInput, r as StorageFile, t as Storage } from "../storage-
|
|
1
|
+
import { nt as PresetResult } from "../index-Cm0vUrr_.mjs";
|
|
2
|
+
import { r as RequestScope } from "../types-tgR4Pt8F.mjs";
|
|
3
|
+
import { c as PermissionCheck } from "../fields-C8Y0XLAu.mjs";
|
|
4
|
+
import { a as StorageReadResult, i as StorageReadRange, n as StorageContext, o as StorageUploadInput, r as StorageFile, t as Storage } from "../storage-BwGQXUpd.mjs";
|
|
5
5
|
|
|
6
6
|
//#region src/presets/filesUpload.d.ts
|
|
7
7
|
interface FilesUploadPresetRoutes {
|
|
@@ -1,2 +1,256 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { o as getOrgId, p as getUserId } from "../types-AOD8fxIw.mjs";
|
|
2
|
+
import { C as requireAuth, y as allowPublic } from "../permissions-B4vU9L0Q.mjs";
|
|
3
|
+
import { i as NotFoundError, u as ValidationError } from "../errors-D5c-5BJL.mjs";
|
|
4
|
+
import { t as multipartBody } from "../multipartBody-CvTR1Un6.mjs";
|
|
5
|
+
//#region src/presets/filesUpload.ts
|
|
6
|
+
const DEFAULT_FIELD_NAME = "file";
|
|
7
|
+
const DEFAULT_MAX_FILE_SIZE = 10 * 1024 * 1024;
|
|
8
|
+
function defaultContextFrom(scope) {
|
|
9
|
+
if (!scope) return {};
|
|
10
|
+
const userId = getUserId(scope);
|
|
11
|
+
const organizationId = getOrgId(scope);
|
|
12
|
+
const ctx = {};
|
|
13
|
+
if (userId !== void 0) ctx.userId = userId;
|
|
14
|
+
if (organizationId !== void 0) ctx.organizationId = organizationId;
|
|
15
|
+
return ctx;
|
|
16
|
+
}
|
|
17
|
+
function buildStorageContext(request, contextFrom) {
|
|
18
|
+
const scope = request.scope;
|
|
19
|
+
return {
|
|
20
|
+
scope: contextFrom(scope),
|
|
21
|
+
requestId: request.id
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Parse a single-range `Range: bytes=start-end` header.
|
|
26
|
+
*
|
|
27
|
+
* Returns `undefined` when the header is missing or unparseable. Only
|
|
28
|
+
* satisfiable single ranges are supported — multi-range requests fall through
|
|
29
|
+
* to the full-object response (per RFC 7233 §4.1 a server MAY ignore ranges).
|
|
30
|
+
*/
|
|
31
|
+
function parseRangeHeader(header, totalSize) {
|
|
32
|
+
if (!header || !header.startsWith("bytes=")) return void 0;
|
|
33
|
+
const spec = header.slice(6).split(",")[0]?.trim();
|
|
34
|
+
if (!spec) return void 0;
|
|
35
|
+
const dashIndex = spec.indexOf("-");
|
|
36
|
+
if (dashIndex === -1) return void 0;
|
|
37
|
+
const startRaw = spec.slice(0, dashIndex);
|
|
38
|
+
const endRaw = spec.slice(dashIndex + 1);
|
|
39
|
+
if (startRaw === "") {
|
|
40
|
+
if (totalSize === void 0) return void 0;
|
|
41
|
+
const suffix = Number(endRaw);
|
|
42
|
+
if (!Number.isFinite(suffix) || suffix <= 0) return void 0;
|
|
43
|
+
return {
|
|
44
|
+
start: Math.max(0, totalSize - suffix),
|
|
45
|
+
end: totalSize - 1
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
const start = Number(startRaw);
|
|
49
|
+
if (!Number.isFinite(start) || start < 0) return void 0;
|
|
50
|
+
if (endRaw === "") {
|
|
51
|
+
if (totalSize === void 0) return void 0;
|
|
52
|
+
return {
|
|
53
|
+
start,
|
|
54
|
+
end: totalSize - 1
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
const end = Number(endRaw);
|
|
58
|
+
if (!Number.isFinite(end) || end < start) return void 0;
|
|
59
|
+
if (totalSize !== void 0 && end >= totalSize) return {
|
|
60
|
+
start,
|
|
61
|
+
end: totalSize - 1
|
|
62
|
+
};
|
|
63
|
+
return {
|
|
64
|
+
start,
|
|
65
|
+
end
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Strict policy — rejects filenames that could escape a storage root or
|
|
70
|
+
* confuse a filesystem. Safe default for disk/S3 adapters that compose
|
|
71
|
+
* `${prefix}/${filename}` or `path.join(root, filename)`.
|
|
72
|
+
*/
|
|
73
|
+
function strictFilenamePolicy(filename) {
|
|
74
|
+
if (filename.length === 0) throw new ValidationError("Upload filename is empty");
|
|
75
|
+
if (filename.length > 255) throw new ValidationError("Upload filename exceeds 255 characters");
|
|
76
|
+
if (filename.includes("\0")) throw new ValidationError("Upload filename contains a NUL byte");
|
|
77
|
+
if (filename.includes("/") || filename.includes("\\")) throw new ValidationError("Upload filename contains a path separator");
|
|
78
|
+
if (filename === "." || filename === "..") throw new ValidationError("Upload filename is a path traversal component");
|
|
79
|
+
return filename;
|
|
80
|
+
}
|
|
81
|
+
/** Resolve the user-supplied policy option into a concrete validator. */
|
|
82
|
+
function resolveFilenamePolicy(policy) {
|
|
83
|
+
if (policy === void 0 || policy === true) return strictFilenamePolicy;
|
|
84
|
+
if (policy === false || policy === "*") return (f) => f;
|
|
85
|
+
if (typeof policy === "function") return (filename) => {
|
|
86
|
+
const result = policy(filename);
|
|
87
|
+
if (result === false) throw new ValidationError(`Upload filename rejected: ${filename}`);
|
|
88
|
+
if (typeof result === "string") return result;
|
|
89
|
+
return filename;
|
|
90
|
+
};
|
|
91
|
+
return strictFilenamePolicy;
|
|
92
|
+
}
|
|
93
|
+
function makeUploadHandler(deps) {
|
|
94
|
+
return async function uploadHandler(request, reply) {
|
|
95
|
+
const file = (request.body?._files)?.[deps.fieldName];
|
|
96
|
+
if (!file) throw new ValidationError(`Missing file field '${deps.fieldName}' in multipart body`);
|
|
97
|
+
const filename = deps.applyFilenamePolicy(file.filename);
|
|
98
|
+
const ctx = buildStorageContext(request, deps.contextFrom);
|
|
99
|
+
const result = await deps.storage.upload({
|
|
100
|
+
buffer: file.buffer,
|
|
101
|
+
filename,
|
|
102
|
+
mimeType: file.mimetype,
|
|
103
|
+
size: file.size
|
|
104
|
+
}, ctx);
|
|
105
|
+
return reply.code(201).send({
|
|
106
|
+
success: true,
|
|
107
|
+
data: toResponseFile(result)
|
|
108
|
+
});
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
function toResponseFile(file) {
|
|
112
|
+
const payload = {
|
|
113
|
+
id: file.id,
|
|
114
|
+
url: file.url,
|
|
115
|
+
pathname: file.pathname,
|
|
116
|
+
contentType: file.contentType,
|
|
117
|
+
bytes: file.bytes
|
|
118
|
+
};
|
|
119
|
+
if (file.metadata !== void 0) payload.metadata = file.metadata;
|
|
120
|
+
return payload;
|
|
121
|
+
}
|
|
122
|
+
function makeReadHandler(deps) {
|
|
123
|
+
return async function readHandler(request, reply) {
|
|
124
|
+
const { id } = request.params;
|
|
125
|
+
const ctx = buildStorageContext(request, deps.contextFrom);
|
|
126
|
+
reply.header("Accept-Ranges", "bytes");
|
|
127
|
+
const rangeHeader = request.headers.range;
|
|
128
|
+
let result;
|
|
129
|
+
try {
|
|
130
|
+
const parsed = rangeHeader ? parseRangeHeader(rangeHeader, void 0) : void 0;
|
|
131
|
+
result = await deps.storage.read(id, ctx, parsed);
|
|
132
|
+
} catch (err) {
|
|
133
|
+
throw toNotFound(err, "File", id);
|
|
134
|
+
}
|
|
135
|
+
if (result.kind === "buffer") return sendBuffer(reply, result, rangeHeader);
|
|
136
|
+
return sendStream(reply, result, rangeHeader);
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
function sendBuffer(reply, result, rangeHeader) {
|
|
140
|
+
reply.type(result.contentType);
|
|
141
|
+
const total = result.totalBytes ?? result.buffer.length;
|
|
142
|
+
if (result.range) {
|
|
143
|
+
const { start, end } = result.range;
|
|
144
|
+
reply.code(206);
|
|
145
|
+
reply.header("Content-Range", `bytes ${start}-${end}/${total}`);
|
|
146
|
+
reply.header("Content-Length", String(result.buffer.length));
|
|
147
|
+
return reply.send(result.buffer);
|
|
148
|
+
}
|
|
149
|
+
if (rangeHeader) {
|
|
150
|
+
const parsed = parseRangeHeader(rangeHeader, total);
|
|
151
|
+
if (parsed) {
|
|
152
|
+
const slice = result.buffer.subarray(parsed.start, parsed.end + 1);
|
|
153
|
+
reply.code(206);
|
|
154
|
+
reply.header("Content-Range", `bytes ${parsed.start}-${parsed.end}/${total}`);
|
|
155
|
+
reply.header("Content-Length", String(slice.length));
|
|
156
|
+
return reply.send(slice);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
reply.header("Content-Length", String(result.buffer.length));
|
|
160
|
+
return reply.send(result.buffer);
|
|
161
|
+
}
|
|
162
|
+
function sendStream(reply, result, rangeHeader) {
|
|
163
|
+
reply.type(result.contentType);
|
|
164
|
+
if (result.range && result.bytes !== void 0) {
|
|
165
|
+
const { start, end } = result.range;
|
|
166
|
+
reply.code(206);
|
|
167
|
+
reply.header("Content-Range", `bytes ${start}-${end}/${result.bytes}`);
|
|
168
|
+
reply.header("Content-Length", String(end - start + 1));
|
|
169
|
+
} else if (result.bytes !== void 0) {
|
|
170
|
+
reply.header("Content-Length", String(result.bytes));
|
|
171
|
+
if (rangeHeader) reply.request.log.debug({ url: reply.request.url }, "filesUploadPreset: adapter returned unsliced stream for a range request — sending full object");
|
|
172
|
+
}
|
|
173
|
+
return reply.send(result.stream);
|
|
174
|
+
}
|
|
175
|
+
function makeDeleteHandler(deps) {
|
|
176
|
+
return async function deleteHandler(request, reply) {
|
|
177
|
+
const { id } = request.params;
|
|
178
|
+
const ctx = buildStorageContext(request, deps.contextFrom);
|
|
179
|
+
if (!await deps.storage.delete(id, ctx)) throw new NotFoundError("File", id);
|
|
180
|
+
return reply.code(204).send();
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
function toNotFound(err, resource, id) {
|
|
184
|
+
if (err instanceof NotFoundError) return err;
|
|
185
|
+
const maybe = err;
|
|
186
|
+
if (maybe?.statusCode === 404 || maybe?.code === "NOT_FOUND") return new NotFoundError(resource, id);
|
|
187
|
+
if (typeof maybe?.message === "string" && /not\s*found/i.test(maybe.message)) return new NotFoundError(resource, id);
|
|
188
|
+
return err;
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Create a files-upload preset bound to a `Storage` adapter.
|
|
192
|
+
*
|
|
193
|
+
* The preset uses `raw: true` routes so binary responses bypass arc's JSON
|
|
194
|
+
* envelope. Upload still returns the standard `{ success: true, data }`
|
|
195
|
+
* envelope manually because the response is structured metadata, not bytes.
|
|
196
|
+
*/
|
|
197
|
+
function filesUploadPreset(options) {
|
|
198
|
+
if (!options?.storage) throw new Error("filesUploadPreset: `storage` is required");
|
|
199
|
+
const deps = {
|
|
200
|
+
storage: options.storage,
|
|
201
|
+
fieldName: options.fieldName ?? DEFAULT_FIELD_NAME,
|
|
202
|
+
contextFrom: options.contextFrom ?? defaultContextFrom,
|
|
203
|
+
applyFilenamePolicy: resolveFilenamePolicy(options.sanitizeFilename)
|
|
204
|
+
};
|
|
205
|
+
const maxFileSize = options.maxFileSize ?? DEFAULT_MAX_FILE_SIZE;
|
|
206
|
+
const allowedMimeTypes = options.allowedMimeTypes;
|
|
207
|
+
const includeRoutes = {
|
|
208
|
+
upload: options.includeRoutes?.upload ?? true,
|
|
209
|
+
read: options.includeRoutes?.read ?? true,
|
|
210
|
+
delete: options.includeRoutes?.delete ?? true
|
|
211
|
+
};
|
|
212
|
+
return {
|
|
213
|
+
name: "filesUpload",
|
|
214
|
+
routes: (permissions) => {
|
|
215
|
+
const routes = [];
|
|
216
|
+
if (includeRoutes.upload) routes.push({
|
|
217
|
+
method: "POST",
|
|
218
|
+
path: "/upload",
|
|
219
|
+
operation: "filesUpload.upload",
|
|
220
|
+
summary: "Upload a file",
|
|
221
|
+
description: "Accepts a multipart/form-data request and persists the bytes via the configured Storage adapter.",
|
|
222
|
+
permissions: options.permissions?.upload ?? permissions.create ?? requireAuth(),
|
|
223
|
+
preHandler: [multipartBody({
|
|
224
|
+
maxFileSize,
|
|
225
|
+
allowedMimeTypes,
|
|
226
|
+
requiredFields: [deps.fieldName]
|
|
227
|
+
})],
|
|
228
|
+
raw: true,
|
|
229
|
+
handler: makeUploadHandler(deps)
|
|
230
|
+
});
|
|
231
|
+
if (includeRoutes.read) routes.push({
|
|
232
|
+
method: "GET",
|
|
233
|
+
path: "/:id",
|
|
234
|
+
operation: "filesUpload.read",
|
|
235
|
+
summary: "Download a file",
|
|
236
|
+
description: "Streams the stored bytes. Supports single-range `Range: bytes=start-end`.",
|
|
237
|
+
permissions: options.permissions?.read ?? permissions.get ?? allowPublic(),
|
|
238
|
+
raw: true,
|
|
239
|
+
handler: makeReadHandler(deps),
|
|
240
|
+
mcp: false
|
|
241
|
+
});
|
|
242
|
+
if (includeRoutes.delete) routes.push({
|
|
243
|
+
method: "DELETE",
|
|
244
|
+
path: "/:id",
|
|
245
|
+
operation: "filesUpload.delete",
|
|
246
|
+
summary: "Delete a file",
|
|
247
|
+
permissions: options.permissions?.delete ?? permissions.delete ?? requireAuth(),
|
|
248
|
+
raw: true,
|
|
249
|
+
handler: makeDeleteHandler(deps)
|
|
250
|
+
});
|
|
251
|
+
return routes;
|
|
252
|
+
}
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
//#endregion
|
|
2
256
|
export { filesUploadPreset };
|
package/dist/presets/index.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Pt as AnyRecord, _t as IControllerResponse, at as ResourceConfig, d as PaginationResult, nt as PresetResult, vt as IRequestContext } from "../index-Cm0vUrr_.mjs";
|
|
2
2
|
import { FilesUploadPresetOptions, FilesUploadPresetPermissions, FilesUploadPresetRoutes, filesUploadPreset } from "./filesUpload.mjs";
|
|
3
3
|
import { MultiTenantOptions, TenantFieldSpec, multiTenantPreset } from "./multiTenant.mjs";
|
|
4
4
|
import { SearchHandler, SearchPresetOptions, SearchRouteConfig, searchPreset } from "./search.mjs";
|
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-
|
|
3
|
-
import {
|
|
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-k604Lj99.mjs";
|
|
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 };
|