@classytic/arc 2.30.0 → 2.31.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/LICENSE +1 -1
- package/README.md +7 -0
- package/TRADEMARK.md +22 -0
- package/dist/{BaseController-B5DwufbB.mjs → BaseController-De7GqHck.mjs} +16 -17
- package/dist/{EventTransport-QmY7A258.d.mts → EventTransport-2uVV34WU.d.mts} +1 -0
- package/dist/{EventTransport-C6YA8xY9.mjs → EventTransport-9mlTgqzE.mjs} +1 -0
- package/dist/{HookSystem-WkIougo6.mjs → HookSystem-BcmPyq5I.mjs} +2 -1
- package/dist/{QueryCache-DOMT_iq7.d.mts → QueryCache-DFA-QrU9.d.mts} +2 -1
- package/dist/{ResourceRegistry-y9Hnypus.mjs → ResourceRegistry-qq0mn2s9.mjs} +3 -2
- package/dist/{actionPermissions-CAphcmUG.mjs → actionPermissions-BE5NwaQk.mjs} +1 -0
- package/dist/audit/index.d.mts +3 -2
- package/dist/audit/index.mjs +2 -1
- package/dist/auth/audit.d.mts +1 -0
- package/dist/auth/audit.mjs +1 -0
- package/dist/auth/index.d.mts +5 -4
- package/dist/auth/index.mjs +6 -5
- package/dist/auth/redis-session.d.mts +2 -1
- package/dist/auth/redis-session.mjs +1 -0
- package/dist/{applyPermissionResult-Cf6zUCvb.mjs → authorizationDecision-ChvUQGG1.mjs} +19 -10
- package/dist/{buildHandler-BYS3Vkfz.mjs → buildHandler-CDrCJzmB.mjs} +4 -3
- package/dist/cache/index.d.mts +4 -3
- package/dist/cache/index.mjs +4 -3
- package/dist/{caching-_NJeDd1_.mjs → caching-BFHA6Scf.mjs} +2 -1
- package/dist/cleanup/index.d.mts +4 -3
- package/dist/cleanup/index.mjs +9 -13
- package/dist/cli/commands/describe.d.mts +2 -1
- package/dist/cli/commands/describe.mjs +3 -2
- package/dist/cli/commands/docs.d.mts +1 -0
- package/dist/cli/commands/docs.mjs +3 -2
- package/dist/cli/commands/doctor.d.mts +1 -0
- package/dist/cli/commands/doctor.mjs +1 -0
- package/dist/cli/commands/generate.d.mts +2 -1
- package/dist/cli/commands/generate.mjs +2 -1
- package/dist/cli/commands/init.d.mts +2 -1
- package/dist/cli/commands/init.mjs +2 -1
- package/dist/cli/commands/introspect.d.mts +1 -0
- package/dist/cli/commands/introspect.mjs +2 -1
- package/dist/cli/index.d.mts +3 -2
- package/dist/cli/index.mjs +3 -2
- package/dist/{constants-DNre0wRg.mjs → constants-D3Dv4LAV.mjs} +1 -0
- package/dist/context/index.d.mts +1 -0
- package/dist/context/index.mjs +3 -2
- package/dist/core/index.d.mts +3 -2
- package/dist/core/index.mjs +7 -6
- package/dist/{core-poalJLvp.mjs → core-BGpIxINN.mjs} +3 -2
- package/dist/{createActionRouter-BxuywVRB.mjs → createActionRouter-CDXEdasl.mjs} +8 -7
- package/dist/{createAggregationRouter-BiY-lGdY.mjs → createAggregationRouter-KrQDzx-k.mjs} +7 -6
- package/dist/{createApp-CXrKFciH.mjs → createApp-Df89BEni.mjs} +484 -209
- package/dist/{defineAggregation-ufWjUwml.mjs → defineAggregation-DbPPS8RI.mjs} +1 -0
- package/dist/{defineResource-CxWqDCAy.mjs → defineResource-Bv6EtN1x.mjs} +63 -38
- package/dist/discovery/index.d.mts +1 -0
- package/dist/discovery/index.mjs +1 -0
- package/dist/docs/index.d.mts +3 -2
- package/dist/docs/index.mjs +3 -2
- package/dist/{elevation-C9s5hFP_.d.mts → elevation-BXCfVcNG.d.mts} +1 -0
- package/dist/{elevation-CIz1GjGm.mjs → elevation-D2l1mh4g.mjs} +4 -3
- package/dist/encryption/index.d.mts +2 -1
- package/dist/encryption/index.mjs +3 -2
- package/dist/{errorHandler-Bq2OeEQL.mjs → errorHandler-C5NeD6bT.mjs} +3 -2
- package/dist/{errorHandler-C6yv4ZTW.d.mts → errorHandler-CpYUJmPA.d.mts} +1 -0
- package/dist/{errors-DHcHVAgk.d.mts → errors-BGdVwP25.d.mts} +1 -0
- package/dist/{errors-DkO8Nk6-.mjs → errors-Cj42AntO.mjs} +1 -0
- package/dist/{eventPlugin-D2NNR4py.mjs → eventPlugin-CkffEihm.mjs} +5 -4
- package/dist/{eventPlugin-Bg3cP2_u.d.mts → eventPlugin-GF5nnBIq.d.mts} +2 -1
- package/dist/events/index.d.mts +4 -3
- package/dist/events/index.mjs +16 -4
- package/dist/events/transports/redis-stream-entry.d.mts +2 -1
- package/dist/events/transports/redis-stream-entry.mjs +1 -0
- package/dist/events/transports/redis.d.mts +2 -1
- package/dist/events/transports/redis.mjs +1 -0
- package/dist/{externalPaths-BE6wFpYv.d.mts → externalPaths-CmGOxOtD.d.mts} +1 -0
- package/dist/factory/index.d.mts +9 -5
- package/dist/factory/index.mjs +7 -6
- package/dist/{fastify-augmentation-0WhPnUH7.d.mts → fastify-augmentation-zh0_AYHa.d.mts} +13 -4
- package/dist/{fields-CQ_7KQWw.mjs → fields-CB5Cuzk9.mjs} +1 -0
- package/dist/{fields-D3lhgzhD.d.mts → fields-I_EeRWND.d.mts} +1 -0
- package/dist/{generate-BnsL9GBm.d.mts → generate-DCGuEbqI.d.mts} +1 -0
- package/dist/{generate-D0wZupBg.mjs → generate-EcA6ui4i.mjs} +2 -1
- package/dist/{headers-BvlQSDMB.mjs → headers-3zvE0rM3.mjs} +2 -1
- package/dist/{health-CB_m4S32.mjs → health-DjZYjM7H.mjs} +1 -0
- package/dist/hooks/index.d.mts +2 -1
- package/dist/hooks/index.mjs +2 -1
- package/dist/idempotency/index.d.mts +3 -2
- package/dist/idempotency/index.mjs +4 -3
- package/dist/idempotency/redis.d.mts +2 -1
- package/dist/idempotency/redis.mjs +1 -0
- package/dist/{index-Cx7bro4R.d.mts → index-Bc2ox8_U.d.mts} +5 -4
- package/dist/{index-CG4yqMDB.d.mts → index-C-481Zf2.d.mts} +74 -18
- package/dist/{index-Ch16aAah.d.mts → index-CEJNZFtX.d.mts} +53 -15
- package/dist/index-CFThn1K9.d.mts +224 -0
- package/dist/{index-Q5gzEIWY.d.mts → index-CpzxqePp.d.mts} +107 -32
- package/dist/index.d.mts +9 -8
- package/dist/index.mjs +11 -10
- package/dist/{init-tlRSt667.mjs → init-BEiNZBsJ.mjs} +19 -23
- package/dist/{init-BPEu8cs-.d.mts → init-Cscs4lab.d.mts} +1 -0
- package/dist/integrations/event-gateway.d.mts +3 -2
- package/dist/integrations/event-gateway.mjs +2 -1
- package/dist/integrations/index.d.mts +4 -3
- package/dist/integrations/index.mjs +1 -0
- package/dist/integrations/jobs/index.d.mts +2 -1
- package/dist/integrations/jobs/index.mjs +108 -32
- package/dist/integrations/mcp/index.d.mts +6 -6
- package/dist/integrations/mcp/index.mjs +2 -1
- package/dist/integrations/mcp/testing.d.mts +3 -2
- package/dist/integrations/mcp/testing.mjs +2 -2
- package/dist/integrations/streamline.d.mts +1 -0
- package/dist/integrations/streamline.mjs +3 -2
- package/dist/integrations/webhooks.d.mts +1 -0
- package/dist/integrations/webhooks.mjs +1 -0
- package/dist/integrations/websocket-pushref-redis.d.mts +2 -1
- package/dist/integrations/websocket-pushref-redis.mjs +1 -0
- package/dist/integrations/websocket-redis.d.mts +2 -1
- package/dist/integrations/websocket-redis.mjs +1 -0
- package/dist/integrations/websocket.d.mts +3 -2
- package/dist/integrations/websocket.mjs +1 -0
- package/dist/{interface-BGZCzOGL.d.mts → interface-BOk3YnTz.d.mts} +1 -0
- package/dist/{interface-Uk8ONJv7.d.mts → interface-DEWNOMza.d.mts} +1 -0
- package/dist/{interface-n5hpKg1d.mjs → interface-DYzM0q-u.mjs} +1 -0
- package/dist/{jsonBody-BlzAoGCe.mjs → jsonBody-BYbLDi5D.mjs} +2 -1
- package/dist/{keys-Cd316u_y.mjs → keys-C0_pa1tY.mjs} +1 -0
- package/dist/{loadResources-DpU8pfjV.mjs → loadResources-yawi9tAE.mjs} +2 -1
- package/dist/{loadResourcesFromEntry-kvGT6EcO.mjs → loadResourcesFromEntry-BAjYZonx.mjs} +2 -1
- package/dist/logger/index.d.mts +1 -0
- package/dist/logger/index.mjs +1 -0
- package/dist/{mcpPlugin-nRzm5g2s.mjs → mcpPlugin-B_es0hiF.mjs} +22 -23
- package/dist/{memory-CmnzXE5Z.mjs → memory-DR6wpa9F.mjs} +2 -1
- package/dist/{metrics-fdxI5mn_.mjs → metrics-CUdw_wLB.mjs} +2 -1
- package/dist/middleware/index.d.mts +2 -1
- package/dist/middleware/index.mjs +3 -2
- package/dist/migrations/index.d.mts +1 -0
- package/dist/migrations/index.mjs +2 -1
- package/dist/module-0pFJA6YW.mjs +78 -0
- package/dist/{multipartBody-D0eBvkeq.mjs → multipartBody-wOgj_fnj.mjs} +1 -0
- package/dist/{openapi-B8Vkbxbn.mjs → openapi-B2L4js1v.mjs} +6 -5
- package/dist/permissions/index.d.mts +5 -4
- package/dist/permissions/index.mjs +6 -5
- package/dist/{permissions-Bfu_8y1U.mjs → permissions-L--QDAir.mjs} +214 -41
- package/dist/{pipe-BOLmGIxa.mjs → pipe-DRDNsI2T.mjs} +2 -1
- package/dist/pipeline/index.d.mts +2 -1
- package/dist/pipeline/index.mjs +2 -1
- package/dist/{pipeline-BJnpkY9i.mjs → pipeline-UPQFatit.mjs} +6 -5
- package/dist/plugins/index.d.mts +8 -7
- package/dist/plugins/index.mjs +16 -15
- package/dist/plugins/response-cache.d.mts +1 -0
- package/dist/plugins/response-cache.mjs +2 -1
- package/dist/plugins/tracing-entry.d.mts +2 -1
- package/dist/plugins/tracing-entry.mjs +2 -1
- package/dist/{pluralize-BhrgwZb9.mjs → pluralize-O0FZgESl.mjs} +1 -0
- package/dist/{preloadResources-DCfa2egz.d.mts → preloadResources-CyoJMSiO.d.mts} +117 -12
- package/dist/{preloadResources-DSY5HGpS.mjs → preloadResources-Dz6BjzM6.mjs} +1 -0
- package/dist/presets/filesUpload.d.mts +6 -5
- package/dist/presets/filesUpload.mjs +9 -11
- package/dist/presets/index.d.mts +3 -2
- package/dist/presets/index.mjs +2 -1
- package/dist/presets/multiTenant.d.mts +2 -1
- package/dist/presets/multiTenant.mjs +3 -2
- package/dist/presets/search.d.mts +3 -2
- package/dist/presets/search.mjs +2 -1
- package/dist/{presets-CJOEXLuk.mjs → presets-C4-g4_4P.mjs} +4 -3
- package/dist/{projection-UBDgfMwu.mjs → projection-BHw6xlOQ.mjs} +2 -1
- package/dist/{pushref-registry-BbcnpNTm.d.mts → pushref-registry-Dov9l4qE.d.mts} +1 -0
- package/dist/{queryCachePlugin-DSGuSVLW.mjs → queryCachePlugin-BZr8v46E.mjs} +5 -4
- package/dist/{queryCachePlugin-br4otiqF.d.mts → queryCachePlugin-BzqxconG.d.mts} +3 -2
- package/dist/{rateLimit-DrED-C4v.mjs → rateLimit-CMPY5Qms.mjs} +7 -3
- package/dist/{rateLimitKey-BoWRH3e7.mjs → rateLimitKey-Dn5JNmkj.mjs} +1 -0
- package/dist/{realtime-B9nmIY5V.mjs → realtime-C5NoxWFY.mjs} +20 -9
- package/dist/{redis-UDtLaNj6.d.mts → redis-CduhKsJi.d.mts} +2 -1
- package/dist/{redis-stream-uIaauNpZ.d.mts → redis-stream-qN6A-RFv.d.mts} +2 -1
- package/dist/{registerRoute-JkxIEBJq.mjs → registerRoute-CBN84fc7.mjs} +4 -3
- package/dist/registry/index.d.mts +3 -2
- package/dist/registry/index.mjs +3 -2
- package/dist/{registry-BYXUUg21.mjs → registry-4213U4zb.mjs} +2 -1
- package/dist/{renewingLease-3cuQH7h8.mjs → renewingLease-Dll8mGgc.mjs} +1 -0
- package/dist/{replyHelpers-67tRawQZ.mjs → replyHelpers-DIiZKGUt.mjs} +2 -1
- package/dist/{repositoryFilter-Dz8k3S4P.mjs → repositoryFilter-f9eAfq_l.mjs} +1 -0
- package/dist/{requestContext-MrlJwECf.mjs → requestContext-2TxXOJ7T.mjs} +1 -0
- package/dist/{rolldown-runtime-8H4AJuhK.mjs → rolldown-runtime-CUsjNX9R.mjs} +1 -0
- package/dist/{schedules-CjOd2MG9.mjs → schedules-DdtlICTf.mjs} +3 -2
- package/dist/{schemaConverter-DJvodiHj.mjs → schemaConverter-D--wdJM-.mjs} +70 -17
- package/dist/{schemaIR-Ca7ySNPx.mjs → schemaIR-Bm2enbnh.mjs} +2 -1
- package/dist/schemas/index.d.mts +1 -0
- package/dist/schemas/index.mjs +1 -0
- package/dist/scim/index.d.mts +1 -0
- package/dist/scim/index.mjs +2 -1
- package/dist/scope/index.d.mts +3 -2
- package/dist/scope/index.mjs +5 -4
- package/dist/{scope-DfaPntpo.mjs → scope-DHx5Fous.mjs} +4 -3
- package/dist/{sessionManager-BHOM3N_0.d.mts → sessionManager-qimlKV0P.d.mts} +1 -0
- package/dist/{simpleEqualityMatcher-Cg05HPsn.mjs → simpleEqualityMatcher-DjQJ7FKA.mjs} +1 -0
- package/dist/{sse-BQ1w0dfl.mjs → sse-CUTo9y0X.mjs} +5 -4
- package/dist/{sseStream-BQ7Qaynp.mjs → sseStream-B_5XcVVd.mjs} +2 -1
- package/dist/{storage-DLUZ04kK.d.mts → storage-Cx9Sz3Ou.d.mts} +1 -0
- package/dist/{store-helpers-DY6xq4sM.mjs → store-helpers-D1MP_gRG.mjs} +1 -0
- package/dist/{streaming-BQcW6_Kq.mjs → streaming-PSZwi8JV.mjs} +1 -0
- package/dist/{subscribe-helpers-DfzsivM6.mjs → subscribe-helpers-C0Tb2aUp.mjs} +2 -1
- package/dist/sync/changelog.d.mts +1 -0
- package/dist/sync/changelog.mjs +1 -0
- package/dist/testing/cleanupStoreContract.d.mts +2 -1
- package/dist/testing/cleanupStoreContract.mjs +1 -0
- package/dist/testing/index.d.mts +139 -12
- package/dist/testing/index.mjs +230 -16
- package/dist/testing/outboxStoreContract.d.mts +1 -0
- package/dist/testing/outboxStoreContract.mjs +1 -0
- package/dist/testing/storageContract.d.mts +2 -1
- package/dist/testing/storageContract.mjs +1 -0
- package/dist/{tracing-CsGKQJGg.d.mts → tracing-Dro5eH6j.d.mts} +1 -0
- package/dist/{transactionContext-PNdoyLLm.mjs → transactionContext-dAfBVUrk.mjs} +1 -0
- package/dist/{typeGuards-C9KXUly6.mjs → typeGuards-B0ryPo3I.mjs} +1 -0
- package/dist/types/index.d.mts +6 -5
- package/dist/types/index.mjs +1 -0
- package/dist/types/storage.d.mts +2 -1
- package/dist/types/storage.mjs +1 -0
- package/dist/{types-ChvvVNP8.d.mts → types-B7XeLEgK.d.mts} +34 -2
- package/dist/{types-Cc91nXuA.d.mts → types-ByHn9rEO.d.mts} +1 -0
- package/dist/{types-C_ZZRcD6.d.mts → types-C4QE8qnm.d.mts} +1 -0
- package/dist/{types-D_n8L6VY.d.mts → types-CBgvkp8O.d.mts} +51 -12
- package/dist/{types-DjC-KUCz.d.mts → types-ChuShpCP.d.mts} +1 -0
- package/dist/{types-Bmmn-fO0.d.mts → types-CwcUQ610.d.mts} +2 -6
- package/dist/{types-CSyCxl2V.mjs → types-Cys0ko9Z.mjs} +4 -3
- package/dist/usage/index.d.mts +3 -2
- package/dist/usage/index.mjs +0 -0
- package/dist/{userHelpers-Cg_y3fBz.mjs → userHelpers-ChYJ6CdK.mjs} +1 -0
- package/dist/utils/index.d.mts +16 -7
- package/dist/utils/index.mjs +10 -9
- package/dist/{utils-taUVD4p3.mjs → utils-vL8BrDE2.mjs} +3 -2
- package/dist/{validate-Bk44F2yT.mjs → validate-_KbifpHF.mjs} +2 -1
- package/dist/{versioning-gKWyryT3.mjs → versioning-CHDTor3P.mjs} +3 -2
- package/dist/{versioning-DXgY5BUm.d.mts → versioning-D_rhHfZm.d.mts} +5 -2
- package/dist/{websocket-_Pd0CgR0.d.mts → websocket-BOXVggMf.d.mts} +2 -1
- package/package.json +547 -539
- package/skills/arc/SKILL.md +22 -8
- package/skills/arc/references/api-reference.md +4 -0
- package/skills/arc/references/auth.md +3 -0
- package/skills/arc/references/integrations.md +33 -0
- package/skills/arc/references/mcp.md +6 -7
- package/skills/arc/references/production.md +24 -25
- package/skills/arc-code-review/references/anti-patterns.md +3 -3
- package/dist/index-CCzVR-N3.d.mts +0 -107
- package/dist/module-_-M7C5ES.mjs +0 -29
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/*! @classytic/arc v2.31.0 | MIT | https://classytic.com */
|
|
1
2
|
import { arcLog } from "./logger/index.mjs";
|
|
2
3
|
//#region src/utils/schemaConverter.ts
|
|
3
4
|
/**
|
|
@@ -25,12 +26,53 @@ import { arcLog } from "./logger/index.mjs";
|
|
|
25
26
|
* Using `draft-7` fixes `.positive() / .negative() / .gt() / .lt()` out of the box.
|
|
26
27
|
* - **`openapi-3.0`** — for OpenAPI doc generation (arc emits OpenAPI 3.0.3). Keeps
|
|
27
28
|
* the boolean exclusive form that 3.0 tooling expects.
|
|
29
|
+
*
|
|
30
|
+
* ## Input vs output (`io`)
|
|
31
|
+
*
|
|
32
|
+
* A Zod schema has TWO shapes and `z.toJSONSchema()` emits one of them. Getting
|
|
33
|
+
* this wrong is not cosmetic — it rejects legal traffic:
|
|
34
|
+
*
|
|
35
|
+
* ```ts
|
|
36
|
+
* z.object({ title: z.string(), status: z.string().default("draft") })
|
|
37
|
+
* // io: "output" → required: ["title", "status"] ← a client omitting `status` is REJECTED
|
|
38
|
+
* // io: "input" → required: ["title"] ← correct: the default fills it in
|
|
39
|
+
* ```
|
|
40
|
+
*
|
|
41
|
+
* The same applies to `.transform()`: in output mode the node is unrepresentable
|
|
42
|
+
* and degrades to `{}` (no validation at all), while input mode emits the real
|
|
43
|
+
* input type. So arc converts by DIRECTION:
|
|
44
|
+
*
|
|
45
|
+
* - **request** (`body`, `querystring`, `params`, `headers`) → `io: "input"` —
|
|
46
|
+
* validates what the client SENDS, before defaults/transforms/coercion apply.
|
|
47
|
+
* - **response** (and the `entity` doc shape) → `io: "output"` — describes what
|
|
48
|
+
* the server RETURNS, after they apply.
|
|
49
|
+
*
|
|
50
|
+
* One deliberate consequence: a plain `z.object()` emits
|
|
51
|
+
* `additionalProperties: false` in output mode but NOT in input mode, because
|
|
52
|
+
* `z.object()` STRIPS unknown keys rather than rejecting them — so extra keys
|
|
53
|
+
* are legal input. Use `z.strictObject()` when the wire contract must reject
|
|
54
|
+
* them; it keeps `additionalProperties: false` in both modes.
|
|
28
55
|
*/
|
|
29
56
|
const log = arcLog("schema");
|
|
30
57
|
/** Default target for Fastify-consumed schemas (matches Fastify v5's default AJV draft). */
|
|
31
58
|
const DEFAULT_FASTIFY_TARGET = "draft-7";
|
|
32
59
|
/** Default target for OpenAPI document generation (matches arc's emitted OpenAPI version). */
|
|
33
60
|
const DEFAULT_OPENAPI_TARGET = "openapi-3.0";
|
|
61
|
+
/**
|
|
62
|
+
* Map arc's target onto one Zod actually branches on.
|
|
63
|
+
*
|
|
64
|
+
* Zod's `target` is typed `"draft-04" | "draft-07" | "draft-2020-12" |
|
|
65
|
+
* "openapi-3.0" | ({} & string)` — that last member accepts ANY string without
|
|
66
|
+
* narrowing, so an unrecognized value is not rejected, it just matches none of
|
|
67
|
+
* the internal `target === ...` branches and silently gets draft-2020-12
|
|
68
|
+
* behaviour. `"draft-7"` is safe (Zod normalizes the alias to `"draft-07"`),
|
|
69
|
+
* but `"openapi-3.1"` is neither official nor normalized. Mapping it explicitly
|
|
70
|
+
* keeps the fallback intentional rather than accidental — and it is the right
|
|
71
|
+
* dialect, since OpenAPI 3.1 aligned its Schema Object with JSON Schema 2020-12.
|
|
72
|
+
*/
|
|
73
|
+
function zodTarget(target) {
|
|
74
|
+
return target === "openapi-3.1" ? "draft-2020-12" : target;
|
|
75
|
+
}
|
|
34
76
|
let _toJSONSchema = null;
|
|
35
77
|
const zodReady = import("zod").then(({ z }) => {
|
|
36
78
|
if (typeof z?.toJSONSchema === "function") _toJSONSchema = (schema, opts) => z.toJSONSchema(schema, opts);
|
|
@@ -92,22 +134,33 @@ function unconvertible(mode, message, cause) {
|
|
|
92
134
|
* Pass `openapi-3.0`/`openapi-3.1` for OpenAPI document generation.
|
|
93
135
|
* @param mode Failure policy for unconvertible Zod schemas — see
|
|
94
136
|
* {@link UnconvertibleMode}. Defaults to `'warn'`.
|
|
137
|
+
* @param io Which shape to emit — see {@link SchemaIo} and the module
|
|
138
|
+
* docblock. Defaults to `'output'`, matching Zod's own default;
|
|
139
|
+
* arc's request-side call sites pass `'input'` explicitly.
|
|
95
140
|
*/
|
|
96
|
-
function toJsonSchema(input, target = DEFAULT_FASTIFY_TARGET, mode = "warn") {
|
|
141
|
+
function toJsonSchema(input, target = DEFAULT_FASTIFY_TARGET, mode = "warn", io = "output") {
|
|
97
142
|
if (input == null) return void 0;
|
|
98
143
|
if (typeof input !== "object") return void 0;
|
|
99
|
-
if (isJsonSchema(input))
|
|
144
|
+
if (isJsonSchema(input)) {
|
|
145
|
+
const plain = input;
|
|
146
|
+
if ("$schema" in plain) {
|
|
147
|
+
const { $schema: _dropped, ...rest } = plain;
|
|
148
|
+
return rest;
|
|
149
|
+
}
|
|
150
|
+
return plain;
|
|
151
|
+
}
|
|
100
152
|
if (isZodSchema(input)) {
|
|
101
153
|
if (!_toJSONSchema) return unconvertible(mode, "A Zod schema was passed but zod v4 is not installed (or not yet loaded). Install zod >=4 — arc converts via zod's native z.toJSONSchema().");
|
|
102
154
|
try {
|
|
103
155
|
const converted = _toJSONSchema(input, {
|
|
104
|
-
target,
|
|
156
|
+
target: zodTarget(target),
|
|
157
|
+
io,
|
|
105
158
|
unrepresentable: mode === "throw" ? "throw" : "any"
|
|
106
159
|
});
|
|
107
160
|
if ("$schema" in converted) delete converted.$schema;
|
|
108
161
|
return converted;
|
|
109
162
|
} catch (cause) {
|
|
110
|
-
return unconvertible(mode, `z.toJSONSchema() failed for a Zod schema (target: ${target}): ${cause instanceof Error ? cause.message : String(cause)}.
|
|
163
|
+
return unconvertible(mode, `z.toJSONSchema() failed for a Zod schema (target: ${target}, io: ${io}): ${cause instanceof Error ? cause.message : String(cause)}. Types with no JSON Schema equivalent cannot express wire validation. For \`z.date()\` / \`z.bigint()\` on a REQUEST schema, use the wire-shaped form (\`z.iso.datetime()\` / \`z.string()\`) and convert in a hook — JSON has no date or bigint. For \`.refine()\` / \`.transform()\` / custom checks, enforce them in the handler or a pipeline step.`, cause);
|
|
111
164
|
}
|
|
112
165
|
}
|
|
113
166
|
if (isZodV3Schema(input)) return unconvertible(mode, "A Zod v3 schema was passed (has `_def` but no v4 `_zod` marker). arc requires zod >=4 for schema conversion — upgrade zod, or pass plain JSON Schema.");
|
|
@@ -122,19 +175,19 @@ function toJsonSchema(input, target = DEFAULT_FASTIFY_TARGET, mode = "warn") {
|
|
|
122
175
|
*/
|
|
123
176
|
function convertOpenApiSchemas(schemas, target = DEFAULT_OPENAPI_TARGET) {
|
|
124
177
|
const result = {};
|
|
125
|
-
const schemaFields =
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
for (const field of schemaFields) {
|
|
178
|
+
const schemaFields = {
|
|
179
|
+
entity: "output",
|
|
180
|
+
createBody: "input",
|
|
181
|
+
updateBody: "input",
|
|
182
|
+
params: "input",
|
|
183
|
+
listQuery: "input",
|
|
184
|
+
response: "output"
|
|
185
|
+
};
|
|
186
|
+
for (const [field, io] of Object.entries(schemaFields)) {
|
|
134
187
|
const value = schemas[field];
|
|
135
|
-
if (value !== void 0) result[field] = toJsonSchema(value, target) ?? value;
|
|
188
|
+
if (value !== void 0) result[field] = toJsonSchema(value, target, "warn", io) ?? value;
|
|
136
189
|
}
|
|
137
|
-
for (const [key, value] of Object.entries(schemas)) if (!
|
|
190
|
+
for (const [key, value] of Object.entries(schemas)) if (!(key in schemaFields)) result[key] = value;
|
|
138
191
|
return result;
|
|
139
192
|
}
|
|
140
193
|
/**
|
|
@@ -158,11 +211,11 @@ function convertRouteSchema(schema, target = DEFAULT_FASTIFY_TARGET) {
|
|
|
158
211
|
"querystring",
|
|
159
212
|
"params",
|
|
160
213
|
"headers"
|
|
161
|
-
]) if (result[field] !== void 0) result[field] = toJsonSchema(result[field], target, mode) ?? result[field];
|
|
214
|
+
]) if (result[field] !== void 0) result[field] = toJsonSchema(result[field], target, mode, "input") ?? result[field];
|
|
162
215
|
if (result.response !== void 0 && typeof result.response === "object" && result.response !== null) {
|
|
163
216
|
const responseObj = result.response;
|
|
164
217
|
const convertedResponse = {};
|
|
165
|
-
for (const [statusCode, responseSchema] of Object.entries(responseObj)) convertedResponse[statusCode] = toJsonSchema(responseSchema, target, mode) ?? responseSchema;
|
|
218
|
+
for (const [statusCode, responseSchema] of Object.entries(responseObj)) convertedResponse[statusCode] = toJsonSchema(responseSchema, target, mode, "output") ?? responseSchema;
|
|
166
219
|
result.response = convertedResponse;
|
|
167
220
|
}
|
|
168
221
|
return result;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
|
|
1
|
+
/*! @classytic/arc v2.31.0 | MIT | https://classytic.com */
|
|
2
|
+
import { o as toJsonSchema } from "./schemaConverter-D--wdJM-.mjs";
|
|
2
3
|
//#region src/core/schemaIR.ts
|
|
3
4
|
/**
|
|
4
5
|
* Schema IR — one canonical representation, two adapters.
|
package/dist/schemas/index.d.mts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
|
|
1
2
|
import { ErrorContract, ErrorDetail } from "@classytic/repo-core/errors";
|
|
2
3
|
import { Static, TObject, TSchema, TSchema as TSchema$1, Type, Type as Type$1 } from "typebox";
|
|
3
4
|
import { FastifyPluginAsyncTypebox, FastifyPluginCallbackTypebox, TypeBoxTypeProvider } from "@fastify/type-provider-typebox";
|
package/dist/schemas/index.mjs
CHANGED
package/dist/scim/index.d.mts
CHANGED
package/dist/scim/index.mjs
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
|
|
1
|
+
/*! @classytic/arc v2.31.0 | MIT | https://classytic.com */
|
|
2
|
+
import { t as parseJsonBody } from "../jsonBody-BYbLDi5D.mjs";
|
|
2
3
|
import fp from "fastify-plugin";
|
|
3
4
|
//#region src/scim/errors.ts
|
|
4
5
|
var ScimError = class extends Error {
|
package/dist/scope/index.d.mts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
import { i as
|
|
1
|
+
|
|
2
|
+
import { A as requireOrgId, C as hasOrgAccess, D as isOrgInScope, E as isMember, M as requireUserId, N as scopeFirstCtx, O as isService, S as getUserRoles, T as isElevated, _ as getScopeContextMap, a as ScopeFirstCtx, b as getTenantFromRequest, c as getAncestorOrgIds, d as getMandate, f as getOrgContext, g as getScopeContext, h as getRequestScope, i as RequestScope, j as requireTeamId, k as requireClientId, l as getClientId, m as getOrgRoles, n as Mandate, o as ScopeFirstCtxOptions, p as getOrgId, r as PUBLIC_SCOPE, s as ScopeFirstCtxRequest, t as AUTHENTICATED_SCOPE, u as getDPoPJkt, v as getServiceScopes, w as isAuthenticated, x as getUserId, y as getTeamId } from "../types-ChuShpCP.mjs";
|
|
3
|
+
import { i as elevationPlugin, n as ElevationOptions, r as _default, t as ElevationEvent } from "../elevation-BXCfVcNG.mjs";
|
|
3
4
|
import { FastifyReply, FastifyRequest } from "fastify";
|
|
4
5
|
//#region src/scope/rateLimitKey.d.ts
|
|
5
6
|
interface RateLimitKeyContext {
|
package/dist/scope/index.mjs
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
import { n as
|
|
3
|
-
import { t as
|
|
4
|
-
import { t as
|
|
1
|
+
/*! @classytic/arc v2.31.0 | MIT | https://classytic.com */
|
|
2
|
+
import { C as isService, D as requireUserId, E as requireTeamId, O as scopeFirstCtx, S as isOrgInScope, T as requireOrgId, _ as getUserRoles, a as getDPoPJkt, b as isElevated, c as getOrgId, d as getScopeContext, f as getScopeContextMap, g as getUserId, h as getTenantFromRequest, i as getClientId, l as getOrgRoles, m as getTeamId, n as PUBLIC_SCOPE, o as getMandate, p as getServiceScopes, r as getAncestorOrgIds, s as getOrgContext, t as AUTHENTICATED_SCOPE, u as getRequestScope, v as hasOrgAccess, w as requireClientId, x as isMember, y as isAuthenticated } from "../types-Cys0ko9Z.mjs";
|
|
3
|
+
import { n as elevation_default, t as elevationPlugin } from "../elevation-D2l1mh4g.mjs";
|
|
4
|
+
import { t as createTenantKeyGenerator } from "../rateLimitKey-Dn5JNmkj.mjs";
|
|
5
|
+
import { t as resolveOrgFromHeader } from "../scope-DHx5Fous.mjs";
|
|
5
6
|
export { AUTHENTICATED_SCOPE, PUBLIC_SCOPE, createTenantKeyGenerator, elevation_default as elevationPlugin, elevationPlugin as elevationPluginFn, getAncestorOrgIds, getClientId, getDPoPJkt, getMandate, getOrgContext, getOrgId, getOrgRoles, getRequestScope, getScopeContext, getScopeContextMap, getServiceScopes, getTeamId, getTenantFromRequest, getUserId, getUserRoles, hasOrgAccess, isAuthenticated, isElevated, isMember, isOrgInScope, isService, requireClientId, requireOrgId, requireTeamId, requireUserId, resolveOrgFromHeader, scopeFirstCtx };
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
import {
|
|
3
|
-
import { n as
|
|
1
|
+
/*! @classytic/arc v2.31.0 | MIT | https://classytic.com */
|
|
2
|
+
import { a as OrgAccessDeniedError, l as UnauthorizedError } from "./errors-Cj42AntO.mjs";
|
|
3
|
+
import { n as requireSingleHeaderValue } from "./headers-3zvE0rM3.mjs";
|
|
4
|
+
import { n as getUserRoles } from "./userHelpers-ChYJ6CdK.mjs";
|
|
4
5
|
//#region src/scope/resolveOrgFromHeader.ts
|
|
5
6
|
/**
|
|
6
7
|
* Create a preHandler hook that resolves org scope from a header.
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
|
|
1
|
+
/*! @classytic/arc v2.31.0 | MIT | https://classytic.com */
|
|
2
|
+
import { t as __exportAll } from "./rolldown-runtime-CUsjNX9R.mjs";
|
|
2
3
|
import { arcLog } from "./logger/index.mjs";
|
|
3
|
-
import { c as getOrgId, n as PUBLIC_SCOPE } from "./types-
|
|
4
|
-
import { a as promoteStreamTokenToHeader } from "./streaming-
|
|
5
|
-
import { t as openSseStream } from "./sseStream-
|
|
4
|
+
import { c as getOrgId, n as PUBLIC_SCOPE } from "./types-Cys0ko9Z.mjs";
|
|
5
|
+
import { a as promoteStreamTokenToHeader } from "./streaming-PSZwi8JV.mjs";
|
|
6
|
+
import { t as openSseStream } from "./sseStream-B_5XcVVd.mjs";
|
|
6
7
|
import fp from "fastify-plugin";
|
|
7
8
|
//#region src/plugins/sse.ts
|
|
8
9
|
var sse_exports = /* @__PURE__ */ __exportAll({
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
|
|
1
|
+
/*! @classytic/arc v2.31.0 | MIT | https://classytic.com */
|
|
2
|
+
import { n as forwardedStreamHeaders } from "./streaming-PSZwi8JV.mjs";
|
|
2
3
|
//#region src/utils/sseStream.ts
|
|
3
4
|
/**
|
|
4
5
|
* Take over the reply socket and open an SSE stream. Call ONLY after every
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
|
|
1
|
+
/*! @classytic/arc v2.31.0 | MIT | https://classytic.com */
|
|
2
|
+
import { n as createEvent } from "./EventTransport-9mlTgqzE.mjs";
|
|
2
3
|
//#region src/events/defineEvent.ts
|
|
3
4
|
/**
|
|
4
5
|
* defineEvent — Typed Event Definitions with Optional Schema Validation
|
|
@@ -1,2 +1,3 @@
|
|
|
1
|
+
|
|
1
2
|
import { ChangeEntry, ChangeLogAppendOptions, ChangeLogStore, ChangeOp, ChangesPage, ChangesSinceOptions, CursorExpiredError, MemoryChangeLogStore, PushMutation, PushVerdict, PushVerdictStatus } from "@classytic/repo-core/sync";
|
|
2
3
|
export { type ChangeEntry, type ChangeLogAppendOptions, type ChangeLogStore, type ChangeOp, type ChangesPage, type ChangesSinceOptions, CursorExpiredError, MemoryChangeLogStore, type PushMutation, type PushVerdict, type PushVerdictStatus };
|
package/dist/sync/changelog.mjs
CHANGED
package/dist/testing/index.d.mts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import "../
|
|
5
|
-
import { n as
|
|
1
|
+
|
|
2
|
+
import { V as ResourceDefinition } from "../index-CpzxqePp.mjs";
|
|
3
|
+
import { i as RequestScope } from "../types-ChuShpCP.mjs";
|
|
4
|
+
import { l as AnyRecord, r as PermissionCheck } from "../types-B7XeLEgK.mjs";
|
|
5
|
+
import { n as ArcModuleInput } from "../types-CBgvkp8O.mjs";
|
|
6
|
+
import "../index-CFThn1K9.mjs";
|
|
7
|
+
import { n as preloadResourcesAsync, r as CreateAppOptions, t as preloadResources } from "../preloadResources-CyoJMSiO.mjs";
|
|
6
8
|
import { StorageContractSetup, StorageContractSetupResult, runStorageContract } from "./storageContract.mjs";
|
|
7
9
|
import { FastifyInstance, FastifyServerOptions } from "fastify";
|
|
8
10
|
import { Mock } from "vitest";
|
|
@@ -84,6 +86,13 @@ interface SurfaceObservation {
|
|
|
84
86
|
interface AuthorizationConformance {
|
|
85
87
|
/** `GET /confs` — the CRUD list surface. */
|
|
86
88
|
list: SurfaceObservation;
|
|
89
|
+
/**
|
|
90
|
+
* `GET /confs/:id` — the SINGLE-RECORD surface. Its `filter` is the compound
|
|
91
|
+
* `buildIdFilter` output (route id AND policy AND tenant), so a policy that
|
|
92
|
+
* tried to redirect/widen the target would show here — the surface the
|
|
93
|
+
* filter-overwrite class of bug lives on.
|
|
94
|
+
*/
|
|
95
|
+
get: SurfaceObservation;
|
|
87
96
|
/** `GET /confs/aggregations/tally` — the aggregation surface. */
|
|
88
97
|
aggregation: SurfaceObservation;
|
|
89
98
|
/** Close the throwaway app. Always call (e.g. in `finally`). */
|
|
@@ -327,6 +336,81 @@ declare function createBetterAuthTestHelpers(options?: BetterAuthTestHelpersOpti
|
|
|
327
336
|
*/
|
|
328
337
|
declare function setupBetterAuthTestApp(input: SetupBetterAuthTestAppInput): Promise<SetupBetterAuthTestAppResult>;
|
|
329
338
|
//#endregion
|
|
339
|
+
//#region src/testing/testActor.d.ts
|
|
340
|
+
/** The header the testing harness uses to carry a request's actor. */
|
|
341
|
+
declare const TEST_ACTOR_HEADER = "x-test-actor";
|
|
342
|
+
/**
|
|
343
|
+
* What a test wants to be. An `organizationId` produces a `member` scope (the
|
|
344
|
+
* org-scoped case); without one the actor is `authenticated`.
|
|
345
|
+
*/
|
|
346
|
+
interface TestActor {
|
|
347
|
+
/**
|
|
348
|
+
* Defaults to a synthetic id derived from the roles. Pass one explicitly when
|
|
349
|
+
* a test distinguishes two actors — ownership and audit assertions do, and
|
|
350
|
+
* two actors sharing roles would otherwise share an id.
|
|
351
|
+
*/
|
|
352
|
+
userId?: string;
|
|
353
|
+
/** Global roles — what `requireRoles` reads. */
|
|
354
|
+
roles?: string[];
|
|
355
|
+
/** Tenant. Present ⇒ a `member` scope. */
|
|
356
|
+
organizationId?: string;
|
|
357
|
+
/** Org-level roles from membership records. Defaults to `roles`. */
|
|
358
|
+
orgRoles?: string[];
|
|
359
|
+
teamId?: string;
|
|
360
|
+
/** App-defined dimensions (branch, project, region) read via `getScopeContext`. */
|
|
361
|
+
context?: Record<string, string>;
|
|
362
|
+
/**
|
|
363
|
+
* Elevated (platform-admin) scope. The value is the `elevatedBy` audit string
|
|
364
|
+
* arc requires — an elevated scope must record who granted it. `true` is
|
|
365
|
+
* shorthand for `'test'`.
|
|
366
|
+
*
|
|
367
|
+
* An elevated scope carries no roles: elevation is a different kind of actor,
|
|
368
|
+
* not a role. A test needing a role gate AND elevation wants `member`.
|
|
369
|
+
*/
|
|
370
|
+
elevated?: string | boolean;
|
|
371
|
+
}
|
|
372
|
+
/**
|
|
373
|
+
* Build the headers that make an injected request act as `actor`.
|
|
374
|
+
*
|
|
375
|
+
* A string is shorthand for a single role. `null` means "stay public", so a test
|
|
376
|
+
* can prove an endpoint is reachable anonymously.
|
|
377
|
+
*
|
|
378
|
+
* @example
|
|
379
|
+
* ```ts
|
|
380
|
+
* t.app.inject({ method: 'GET', url: '/pos/shifts/current',
|
|
381
|
+
* headers: testActorHeaders('manager', ORG) });
|
|
382
|
+
* ```
|
|
383
|
+
*/
|
|
384
|
+
declare function testActorHeaders(actor: TestActor | string | null, organizationId?: string): Record<string, string>;
|
|
385
|
+
/**
|
|
386
|
+
* Turn a spec into a `RequestScope`.
|
|
387
|
+
*
|
|
388
|
+
* `orgRoles` defaults to `roles`: a test that says "manager" means it at both
|
|
389
|
+
* levels, and making the caller repeat itself only produces tests that pass a
|
|
390
|
+
* global gate and mysteriously fail an org one.
|
|
391
|
+
*/
|
|
392
|
+
declare function scopeFromTestActor(spec: TestActor): RequestScope;
|
|
393
|
+
/**
|
|
394
|
+
* Build the `auth` option that makes the test actor arc's authenticator.
|
|
395
|
+
*
|
|
396
|
+
* It must be an authenticator, not a hook. Fastify encapsulates plugins and
|
|
397
|
+
* modules register DURING `createApp`, so a hook added to the root instance
|
|
398
|
+
* afterwards never runs for their routes — every request still resolves as
|
|
399
|
+
* public and every role gate still answers 401, which reads exactly like a
|
|
400
|
+
* broken authorization rule in the code under test. Registering as
|
|
401
|
+
* `auth: { type: 'authenticator' }` puts the scope where arc's own auth would,
|
|
402
|
+
* at the phase arc expects, so tenant filtering and permission checks behave
|
|
403
|
+
* identically to production.
|
|
404
|
+
*
|
|
405
|
+
* `defaultActor` applies to requests carrying no header. A malformed header
|
|
406
|
+
* REJECTS rather than falling back to public — a test that meant to be an admin
|
|
407
|
+
* and got quietly demoted is the most expensive way to fail.
|
|
408
|
+
*/
|
|
409
|
+
declare function testActorAuth(defaultActor?: TestActor): {
|
|
410
|
+
type: "authenticator";
|
|
411
|
+
authenticate: (request: unknown) => Promise<void>;
|
|
412
|
+
};
|
|
413
|
+
//#endregion
|
|
330
414
|
//#region src/testing/bootModuleApp.d.ts
|
|
331
415
|
/** Handed to a modules factory — the in-memory DB is live at call time. */
|
|
332
416
|
/**
|
|
@@ -350,8 +434,6 @@ type TestDatabaseFactory<TConn = unknown> = (opts: {
|
|
|
350
434
|
interface TestkitContext<TConn = unknown> {
|
|
351
435
|
/** The provisioned database's connection string. */
|
|
352
436
|
uri: string;
|
|
353
|
-
/** @deprecated Alias of `uri` from the Mongo-default era — prefer `uri`. */
|
|
354
|
-
mongoUri: string;
|
|
355
437
|
/**
|
|
356
438
|
* Driver connection handle (the default factory yields mongoose's
|
|
357
439
|
* DEFAULT connection). Generic so DB-typed wrappers assert the driver
|
|
@@ -377,13 +459,20 @@ interface BootModuleAppOptions<TConn = unknown> {
|
|
|
377
459
|
* boot when a module genuinely exercises one).
|
|
378
460
|
*/
|
|
379
461
|
app?: Partial<CreateAppOptions>;
|
|
462
|
+
/**
|
|
463
|
+
* Default actor for every injected request that carries no `x-test-actor` header.
|
|
464
|
+
*
|
|
465
|
+
* The harness boots with `auth: false`, so without this every request is
|
|
466
|
+
* `PUBLIC_SCOPE` and role-gated routes answer 401 — correct, but it left tests with
|
|
467
|
+
* no supported way to be somebody. See `testActor.ts` for why the seam lives in the
|
|
468
|
+
* testing entrypoint and can never be reached from production.
|
|
469
|
+
*/
|
|
470
|
+
actor?: TestActor;
|
|
380
471
|
}
|
|
381
472
|
interface ModuleTestApp<TConn = unknown> {
|
|
382
473
|
app: FastifyInstance;
|
|
383
474
|
/** The provisioned database's connection string. */
|
|
384
475
|
uri: string;
|
|
385
|
-
/** @deprecated Alias of `uri` from the Mongo-default era — prefer `uri`. */
|
|
386
|
-
mongoUri: string;
|
|
387
476
|
/** The mongoose `Connection` handed to the modules factory. */
|
|
388
477
|
connection: TConn;
|
|
389
478
|
/**
|
|
@@ -555,13 +644,26 @@ declare function createSpy<T extends (...args: unknown[]) => unknown>(_name?: st
|
|
|
555
644
|
getLastCall(): unknown[];
|
|
556
645
|
};
|
|
557
646
|
/**
|
|
558
|
-
* Wait
|
|
647
|
+
* Wait until `condition` holds — the standard way to assert on asynchronous
|
|
648
|
+
* effects in arc's suites.
|
|
559
649
|
*
|
|
560
|
-
*
|
|
650
|
+
* **Prefer this over `await sleep(n)` + assert.** A fixed delay encodes a guess
|
|
651
|
+
* about scheduling: too short and it flakes the moment the pool is busy, too
|
|
652
|
+
* long and every run pays for the worst case. Polling returns as soon as the
|
|
653
|
+
* effect lands, so the fast path stays fast and a loaded machine simply waits —
|
|
654
|
+
* and the failure mode becomes "this never happened", which is the thing worth
|
|
655
|
+
* asserting.
|
|
656
|
+
*
|
|
657
|
+
* Supply `label`: without it a timeout reports only its own duration, which is
|
|
658
|
+
* the least useful sentence a flaky test can print.
|
|
659
|
+
*
|
|
660
|
+
* @example
|
|
661
|
+
* await waitFor(() => runs >= 3, { label: "3 scheduler ticks" });
|
|
561
662
|
*/
|
|
562
663
|
declare function waitFor(condition: () => boolean | Promise<boolean>, options?: {
|
|
563
664
|
timeout?: number;
|
|
564
665
|
interval?: number;
|
|
666
|
+
label?: string;
|
|
565
667
|
}): Promise<void>;
|
|
566
668
|
/**
|
|
567
669
|
* Create a test timer that can be controlled
|
|
@@ -573,6 +675,31 @@ declare function createTestTimer(): {
|
|
|
573
675
|
reset: () => void;
|
|
574
676
|
};
|
|
575
677
|
//#endregion
|
|
678
|
+
//#region src/testing/scopeRoleGate.d.ts
|
|
679
|
+
/**
|
|
680
|
+
* Grants iff the caller is an org member holding one of `allowed` as an ORG role.
|
|
681
|
+
*
|
|
682
|
+
* This is `requireOrgRole` under a name package tests reach for — deliberately a
|
|
683
|
+
* delegation, not a reimplementation. It previously read `scope.userRoles` while
|
|
684
|
+
* requiring an organization, so it disagreed with production for any actor whose
|
|
685
|
+
* global and org roles differ: given
|
|
686
|
+
* `{ roles: ["employee"], orgRoles: ["manager"], organizationId: "org-1" }`,
|
|
687
|
+
* `requireOrgRole("manager")` allowed and this denied. The tests never caught it
|
|
688
|
+
* because `scopeFromTestActor` defaults `orgRoles` to `roles`, making the two
|
|
689
|
+
* dimensions identical for the common actor.
|
|
690
|
+
*
|
|
691
|
+
* Delegating also inherits the semantics a hand-rolled gate kept missing:
|
|
692
|
+
* elevated scopes pass, and a service (API-key) identity is refused with a
|
|
693
|
+
* message naming `requireServiceScope` instead of silently failing a role test.
|
|
694
|
+
*
|
|
695
|
+
* **Pair it with an org-bearing actor.** `testActorHeaders('manager', ORG)`
|
|
696
|
+
* yields a `member` scope and passes; `testActorHeaders('manager')` yields an
|
|
697
|
+
* `authenticated` scope with no tenant, which is refused — arc's generated CRUD
|
|
698
|
+
* filters by tenant, so a gate that waves a tenant-less request through is how a
|
|
699
|
+
* green test reads another org's rows.
|
|
700
|
+
*/
|
|
701
|
+
declare function scopeRoleGate(...allowed: string[]): PermissionCheck;
|
|
702
|
+
//#endregion
|
|
576
703
|
//#region src/testing/testApp.d.ts
|
|
577
704
|
type DbMode = "in-memory" | {
|
|
578
705
|
uri: string;
|
|
@@ -650,4 +777,4 @@ declare function createTestApp(options?: CreateTestAppOptions): Promise<TestAppC
|
|
|
650
777
|
*/
|
|
651
778
|
declare function createMinimalTestApp(options?: FastifyServerOptions): FastifyInstance;
|
|
652
779
|
//#endregion
|
|
653
|
-
export { type ArcAssertion, type ArcResponseLike, type AuthMode, type AuthResponse, type AuthorizationConformance, type BetterAuthTestHelpers, type BetterAuthTestHelpersOptions, type BetterAuthTestUser, type BootModuleAppOptions, type BootModulesInput, type CreateOrgInput, type CreateTestAppOptions, type DbMode, type FixtureDestroyer, type FixtureFactory, type FixtureRegistration, HttpTestHarness, type HttpTestHarnessOptions, type MockRepository, type ModuleTestApp, type OrgResponse, type RoleConfig, type SetupBetterAuthTestAppInput, type SetupBetterAuthTestAppResult, type SignInInput, type SignUpInput, type StorageContractSetup, type StorageContractSetupResult, type SurfaceObservation, type TestAppContext, type TestAuthProvider, type TestAuthSession, type TestDatabase, type TestDatabaseFactory, type TestFixtures, type TestkitContext, bootModuleApp, createBetterAuthProvider, createBetterAuthTestHelpers, createCustomAuthProvider, createDataFactory, createHttpTestHarness, createJwtAuthProvider, createMinimalTestApp, createMockController, createMockReply, createMockRepository, createMockRequest, createMockUser, createSpy, createTestApp, createTestFixtures, createTestTimer, expectArc, mongoMemoryDatabase, preloadResources, preloadResourcesAsync, runAuthorizationConformance, runStorageContract, safeParseBody, setupBetterAuthTestApp, waitFor };
|
|
780
|
+
export { type ArcAssertion, type ArcResponseLike, type AuthMode, type AuthResponse, type AuthorizationConformance, type BetterAuthTestHelpers, type BetterAuthTestHelpersOptions, type BetterAuthTestUser, type BootModuleAppOptions, type BootModulesInput, type CreateOrgInput, type CreateTestAppOptions, type DbMode, type FixtureDestroyer, type FixtureFactory, type FixtureRegistration, HttpTestHarness, type HttpTestHarnessOptions, type MockRepository, type ModuleTestApp, type OrgResponse, type RoleConfig, type SetupBetterAuthTestAppInput, type SetupBetterAuthTestAppResult, type SignInInput, type SignUpInput, type StorageContractSetup, type StorageContractSetupResult, type SurfaceObservation, TEST_ACTOR_HEADER, type TestActor, type TestAppContext, type TestAuthProvider, type TestAuthSession, type TestDatabase, type TestDatabaseFactory, type TestFixtures, type TestkitContext, bootModuleApp, createBetterAuthProvider, createBetterAuthTestHelpers, createCustomAuthProvider, createDataFactory, createHttpTestHarness, createJwtAuthProvider, createMinimalTestApp, createMockController, createMockReply, createMockRepository, createMockRequest, createMockUser, createSpy, createTestApp, createTestFixtures, createTestTimer, expectArc, mongoMemoryDatabase, preloadResources, preloadResourcesAsync, runAuthorizationConformance, runStorageContract, safeParseBody, scopeFromTestActor, scopeRoleGate, setupBetterAuthTestApp, testActorAuth, testActorHeaders, waitFor };
|