@classytic/arc 2.11.4 → 2.14.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 +16 -12
- package/dist/{BaseController-swXruJ2_.mjs → BaseController-DX_T-bDB.mjs} +388 -423
- package/dist/EventTransport-CT_52aWU.d.mts +34 -0
- package/dist/EventTransport-DLWoUMHy.mjs +103 -0
- package/dist/{ResourceRegistry-DkAeAuTX.mjs → ResourceRegistry-CTERg_2x.mjs} +139 -66
- package/dist/audit/index.d.mts +2 -2
- package/dist/audit/index.mjs +1 -1
- package/dist/auth/audit.d.mts +199 -0
- package/dist/auth/audit.mjs +288 -0
- package/dist/auth/index.d.mts +3 -3
- package/dist/auth/index.mjs +117 -191
- package/dist/{betterAuthOpenApi-DwxtK3uG.mjs → betterAuthOpenApi--M_i87dQ.mjs} +1 -1
- package/dist/buildHandler-olo-gt94.mjs +610 -0
- package/dist/cache/index.mjs +3 -3
- package/dist/cli/commands/describe.d.mts +89 -13
- package/dist/cli/commands/describe.mjs +56 -2
- package/dist/cli/commands/docs.mjs +2 -2
- package/dist/cli/commands/generate.mjs +147 -48
- package/dist/cli/commands/init.d.mts +13 -0
- package/dist/cli/commands/init.mjs +130 -87
- package/dist/cli/commands/introspect.mjs +8 -1
- package/dist/context/index.mjs +1 -1
- package/dist/core/index.d.mts +3 -3
- package/dist/core/index.mjs +5 -5
- package/dist/core-DECn6zaU.mjs +1399 -0
- package/dist/{createActionRouter-CIKOcNA7.mjs → createActionRouter-CBxLLbn3.mjs} +7 -20
- package/dist/createAggregationRouter-CRIBv4sC.mjs +114 -0
- package/dist/{createApp-C9bRrqlX.mjs → createApp-XX2-N0Yd.mjs} +28 -22
- package/dist/{defineEvent-D1Ky9M1D.mjs → defineEvent-D5h7EvAx.mjs} +1 -1
- package/dist/docs/index.d.mts +24 -11
- package/dist/docs/index.mjs +2 -2
- package/dist/{elevation-DOFoxoDs.mjs → elevation-DgoeTyfX.mjs} +1 -1
- package/dist/errorHandler-Bk-AGhkU.mjs +174 -0
- package/dist/errorHandler-DFr45ZG4.d.mts +45 -0
- package/dist/errors-j4aJm1Wg.mjs +184 -0
- package/dist/{eventPlugin-Cts2-Tfj.mjs → eventPlugin-CaKTYkYM.mjs} +28 -4
- package/dist/{eventPlugin-DDJoNEPL.d.mts → eventPlugin-qXpqTebY.d.mts} +24 -1
- package/dist/events/index.d.mts +6 -6
- package/dist/events/index.mjs +11 -35
- 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 +2 -2
- package/dist/factory/index.mjs +2 -2
- package/dist/{fields-BRjxOAFp.d.mts → fields-COhcH3fk.d.mts} +23 -2
- package/dist/hooks/index.d.mts +1 -1
- package/dist/hooks/index.mjs +1 -1
- package/dist/idempotency/index.d.mts +1 -1
- package/dist/idempotency/index.mjs +1 -20
- package/dist/idempotency/redis.mjs +1 -1
- package/dist/{index-rHjXmJar.d.mts → index-BTqLEvhu.d.mts} +163 -3
- package/dist/{index-CXXRbnf8.d.mts → index-BtW7qYwa.d.mts} +660 -326
- package/dist/{index-m8mOOlFW.d.mts → index-Ds61mrJE.d.mts} +50 -4
- package/dist/{index-D9t1KNaB.d.mts → index-Dz5IKsrE.d.mts} +360 -219
- package/dist/index.d.mts +6 -7
- package/dist/index.mjs +9 -10
- package/dist/integrations/event-gateway.d.mts +1 -1
- package/dist/integrations/event-gateway.mjs +1 -1
- package/dist/integrations/index.d.mts +1 -1
- 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 +60 -11
- package/dist/integrations/streamline.mjs +75 -85
- package/dist/integrations/websocket.mjs +2 -8
- package/dist/middleware/index.d.mts +1 -1
- package/dist/middleware/index.mjs +2 -2
- package/dist/migrations/index.d.mts +23 -3
- package/dist/migrations/index.mjs +0 -7
- package/dist/{multipartBody-CvTR1Un6.mjs → multipartBody-BOvVSVCD.mjs} +11 -8
- package/dist/openapi-noXno2CV.mjs +968 -0
- package/dist/org/index.d.mts +2 -2
- package/dist/org/index.mjs +1 -1
- package/dist/permissions/index.d.mts +3 -3
- package/dist/permissions/index.mjs +3 -3
- package/dist/{permissions-gd_aUWrR.mjs → permissions-ohQyv50e.mjs} +404 -176
- package/dist/{pipe-DVoIheVC.mjs → pipe-Zr0KXjQe.mjs} +1 -1
- package/dist/pipeline/index.d.mts +1 -1
- package/dist/pipeline/index.mjs +1 -1
- package/dist/plugins/index.d.mts +16 -31
- package/dist/plugins/index.mjs +33 -13
- package/dist/plugins/response-cache.mjs +1 -1
- package/dist/plugins/tracing-entry.mjs +1 -1
- package/dist/presets/filesUpload.d.mts +4 -4
- package/dist/presets/filesUpload.mjs +6 -9
- package/dist/presets/index.d.mts +1 -1
- package/dist/presets/index.mjs +1 -1
- package/dist/presets/multiTenant.d.mts +1 -1
- package/dist/presets/multiTenant.mjs +2 -2
- package/dist/presets/search.d.mts +2 -2
- package/dist/presets/search.mjs +6 -8
- package/dist/{presets-Z7P5w4gF.mjs → presets-BbkjdPeH.mjs} +6 -28
- package/dist/{queryCachePlugin-Bq6bO6vc.mjs → queryCachePlugin-m1XsgAIJ.mjs} +3 -3
- package/dist/{redis-stream-xTGxB2bm.d.mts → redis-stream-D6HzR1Z_.d.mts} +1 -1
- package/dist/registry/index.d.mts +1 -1
- package/dist/registry/index.mjs +2 -2
- package/dist/{replyHelpers-ByllIXXV.mjs → replyHelpers-CK-FNO8E.mjs} +3 -21
- package/dist/{resourceToTools-CxNmI6xF.mjs → resourceToTools-DLL32us3.mjs} +224 -71
- package/dist/{routerShared-BqLRb5l7.mjs → routerShared-DrOa-26E.mjs} +41 -36
- package/dist/{schemaIR-Dy2p4MxS.mjs → schemaIR-lYhC2gE5.mjs} +1 -1
- package/dist/schemas/index.d.mts +100 -30
- package/dist/schemas/index.mjs +86 -29
- package/dist/scim/index.d.mts +264 -0
- package/dist/scim/index.mjs +963 -0
- package/dist/scope/index.d.mts +3 -3
- package/dist/scope/index.mjs +4 -4
- package/dist/{sse-V7aXc3bW.mjs → sse-Bz-5ZeTt.mjs} +1 -1
- package/dist/{store-helpers-Cp4uKC1U.mjs → store-helpers-BkIN9-vu.mjs} +1 -1
- package/dist/testing/index.d.mts +2 -8
- package/dist/testing/index.mjs +16 -24
- package/dist/types/index.d.mts +4 -4
- package/dist/{types-D7KpfiL1.d.mts → types-BvqwCCSx.d.mts} +73 -25
- package/dist/{types-DDyTPc6y.d.mts → types-CTYvcwHe.d.mts} +195 -1
- package/dist/{types-AOD8fxIw.mjs → types-C_s5moIu.mjs} +117 -1
- package/dist/{types-BQ9TJQNy.d.mts → types-DQHFc8PM.d.mts} +1 -1
- package/dist/utils/index.d.mts +2 -2
- package/dist/utils/index.mjs +5 -5
- package/dist/{utils-CcYTj09l.mjs → utils-_h9B3c57.mjs} +1269 -1334
- package/dist/{versioning-DsglKfM_.d.mts → versioning-DTTvc80y.d.mts} +1 -1
- package/package.json +24 -34
- package/skills/arc/SKILL.md +147 -51
- package/skills/arc/references/agent-auth.md +238 -0
- package/skills/arc/references/api-reference.md +187 -0
- package/skills/arc/references/auth.md +354 -7
- package/skills/arc/references/enterprise-auth.md +94 -0
- package/skills/arc/references/events.md +8 -6
- package/skills/arc/references/mcp.md +2 -2
- package/skills/arc/references/multi-tenancy.md +11 -2
- package/skills/arc/references/production.md +10 -9
- package/skills/arc/references/scim.md +247 -0
- package/skills/arc/references/testing.md +1 -1
- package/skills/arc-code-review/SKILL.md +141 -0
- package/skills/arc-code-review/references/anti-patterns.md +911 -0
- package/skills/arc-code-review/references/arc-cheatsheet.md +380 -0
- package/skills/arc-code-review/references/migration-recipes.md +700 -0
- package/skills/arc-code-review/references/mongokit-migration.md +386 -0
- package/skills/arc-code-review/references/scaffolding.md +230 -0
- package/skills/arc-code-review/references/severity.md +127 -0
- package/dist/EventTransport-BFQjw9pB.mjs +0 -133
- package/dist/EventTransport-CYNUXdCJ.d.mts +0 -293
- package/dist/adapters/index.d.mts +0 -3
- package/dist/adapters/index.mjs +0 -2
- package/dist/adapters-DUUiiimH.mjs +0 -964
- package/dist/auth/mongoose.d.mts +0 -191
- package/dist/auth/mongoose.mjs +0 -73
- package/dist/core-CbcQRIch.mjs +0 -1054
- package/dist/errorHandler-BQm8ZxTK.mjs +0 -173
- package/dist/errorHandler-DEWmGWPz.d.mts +0 -114
- package/dist/errors-D5c-5BJL.mjs +0 -232
- package/dist/index-Rg8axYPz.d.mts +0 -370
- package/dist/openapi-D7G1V7ex.mjs +0 -557
- /package/dist/{HookSystem-CGsMd6oK.mjs → HookSystem-Iiebom92.mjs} +0 -0
- /package/dist/{actionPermissions-sUUKDhtP.mjs → actionPermissions-CyUkQu6O.mjs} +0 -0
- /package/dist/{caching-CheW3m-S.mjs → caching-SM8gghN6.mjs} +0 -0
- /package/dist/{constants-BhY1OHoH.mjs → constants-Cxde4rpC.mjs} +0 -0
- /package/dist/{elevation-BQQXZ_VR.d.mts → elevation-BXOWoGCF.d.mts} +0 -0
- /package/dist/{keys-CARyUjiR.mjs → keys-CGcCbNyu.mjs} +0 -0
- /package/dist/{loadResources-CPpkyKfM.mjs → loadResources-DBMQg_Aj.mjs} +0 -0
- /package/dist/{memory-DikHSvWa.mjs → memory-UBydS5ku.mjs} +0 -0
- /package/dist/{metrics-Csh4nsvv.mjs → metrics-Qnvwc-LQ.mjs} +0 -0
- /package/dist/{pluralize-CWP6MB39.mjs → pluralize-DQgqgifU.mjs} +0 -0
- /package/dist/{registry-D63ee7fl.mjs → registry-I-ogLgL9.mjs} +0 -0
- /package/dist/{requestContext-C5XeK3VA.mjs → requestContext-SSaaTgW8.mjs} +0 -0
- /package/dist/{schemaConverter-B0oKLuqI.mjs → schemaConverter-De34B1ZG.mjs} +0 -0
- /package/dist/{typeGuards-CcFZXgU7.mjs → typeGuards-BzkXkvVv.mjs} +0 -0
- /package/dist/{types-DV9WDfeg.mjs → types-D57iXYb8.mjs} +0 -0
- /package/dist/{versioning-CGPjkqAg.mjs → versioning-BUrT5aP4.mjs} +0 -0
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
//#region src/utils/errors.ts
|
|
2
|
+
/**
|
|
3
|
+
* Base Arc Error. Implements the canonical `HttpError` contract — `status`
|
|
4
|
+
* mirrors `statusCode` and `meta` mirrors `details`, so consumers reading
|
|
5
|
+
* either name see the same value without adapter glue.
|
|
6
|
+
*/
|
|
7
|
+
var ArcError = class extends Error {
|
|
8
|
+
name = "ArcError";
|
|
9
|
+
code;
|
|
10
|
+
statusCode;
|
|
11
|
+
details;
|
|
12
|
+
cause;
|
|
13
|
+
constructor(message, options = {}) {
|
|
14
|
+
super(message, options.cause ? { cause: options.cause } : void 0);
|
|
15
|
+
this.code = options.code ?? "arc.error";
|
|
16
|
+
this.statusCode = options.statusCode ?? 500;
|
|
17
|
+
this.details = options.details;
|
|
18
|
+
this.cause = options.cause;
|
|
19
|
+
if (Error.captureStackTrace) Error.captureStackTrace(this, this.constructor);
|
|
20
|
+
}
|
|
21
|
+
/** `HttpError.status` mirror — repo-core's `toErrorContract` reads this. */
|
|
22
|
+
get status() {
|
|
23
|
+
return this.statusCode;
|
|
24
|
+
}
|
|
25
|
+
/** `HttpError.meta` mirror — `details` under the canonical name. */
|
|
26
|
+
get meta() {
|
|
27
|
+
return this.details;
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
var NotFoundError = class extends ArcError {
|
|
31
|
+
constructor(resource, identifier) {
|
|
32
|
+
const message = identifier ? `${resource} with identifier '${identifier}' not found` : `${resource} not found`;
|
|
33
|
+
super(message, {
|
|
34
|
+
code: "arc.not_found",
|
|
35
|
+
statusCode: 404,
|
|
36
|
+
details: {
|
|
37
|
+
resource,
|
|
38
|
+
...identifier ? { identifier } : {}
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
this.name = "NotFoundError";
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
var ValidationError = class extends ArcError {
|
|
45
|
+
errors;
|
|
46
|
+
constructor(message, errors = []) {
|
|
47
|
+
super(message, {
|
|
48
|
+
code: "arc.validation_error",
|
|
49
|
+
statusCode: 400,
|
|
50
|
+
details: { errors }
|
|
51
|
+
});
|
|
52
|
+
this.name = "ValidationError";
|
|
53
|
+
this.errors = errors;
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
var UnauthorizedError = class extends ArcError {
|
|
57
|
+
constructor(message = "Authentication required") {
|
|
58
|
+
super(message, {
|
|
59
|
+
code: "arc.unauthorized",
|
|
60
|
+
statusCode: 401
|
|
61
|
+
});
|
|
62
|
+
this.name = "UnauthorizedError";
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
var ForbiddenError = class extends ArcError {
|
|
66
|
+
constructor(message = "Access denied") {
|
|
67
|
+
super(message, {
|
|
68
|
+
code: "arc.forbidden",
|
|
69
|
+
statusCode: 403
|
|
70
|
+
});
|
|
71
|
+
this.name = "ForbiddenError";
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
var ConflictError = class extends ArcError {
|
|
75
|
+
constructor(message, field) {
|
|
76
|
+
super(message, {
|
|
77
|
+
code: "arc.conflict",
|
|
78
|
+
statusCode: 409,
|
|
79
|
+
...field ? { details: { field } } : {}
|
|
80
|
+
});
|
|
81
|
+
this.name = "ConflictError";
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
var OrgRequiredError = class extends ArcError {
|
|
85
|
+
organizations;
|
|
86
|
+
constructor(message, organizations) {
|
|
87
|
+
super(message, {
|
|
88
|
+
code: "arc.org.selection_required",
|
|
89
|
+
statusCode: 403,
|
|
90
|
+
...organizations ? { details: { organizations } } : {}
|
|
91
|
+
});
|
|
92
|
+
this.name = "OrgRequiredError";
|
|
93
|
+
this.organizations = organizations;
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
var OrgAccessDeniedError = class extends ArcError {
|
|
97
|
+
constructor(orgId) {
|
|
98
|
+
super("Organization access denied", {
|
|
99
|
+
code: "arc.org.access_denied",
|
|
100
|
+
statusCode: 403,
|
|
101
|
+
...orgId ? { details: { organizationId: orgId } } : {}
|
|
102
|
+
});
|
|
103
|
+
this.name = "OrgAccessDeniedError";
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
var RateLimitError = class extends ArcError {
|
|
107
|
+
retryAfter;
|
|
108
|
+
constructor(message = "Too many requests", retryAfter) {
|
|
109
|
+
super(message, {
|
|
110
|
+
code: "arc.rate_limited",
|
|
111
|
+
statusCode: 429,
|
|
112
|
+
...retryAfter ? { details: { retryAfter } } : {}
|
|
113
|
+
});
|
|
114
|
+
this.name = "RateLimitError";
|
|
115
|
+
this.retryAfter = retryAfter;
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
var ServiceUnavailableError = class extends ArcError {
|
|
119
|
+
constructor(message = "Service temporarily unavailable") {
|
|
120
|
+
super(message, {
|
|
121
|
+
code: "arc.service_unavailable",
|
|
122
|
+
statusCode: 503
|
|
123
|
+
});
|
|
124
|
+
this.name = "ServiceUnavailableError";
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
/**
|
|
128
|
+
* Status-code → canonical `arc.*` code mapping. Used by {@link createError}
|
|
129
|
+
* and the global error handler when no explicit code is supplied.
|
|
130
|
+
*/
|
|
131
|
+
const STATUS_CODE_MAP = {
|
|
132
|
+
400: "arc.bad_request",
|
|
133
|
+
401: "arc.unauthorized",
|
|
134
|
+
403: "arc.forbidden",
|
|
135
|
+
404: "arc.not_found",
|
|
136
|
+
409: "arc.conflict",
|
|
137
|
+
422: "arc.unprocessable_entity",
|
|
138
|
+
429: "arc.rate_limited",
|
|
139
|
+
500: "arc.internal_error",
|
|
140
|
+
502: "arc.bad_gateway",
|
|
141
|
+
503: "arc.service_unavailable",
|
|
142
|
+
504: "arc.gateway_timeout"
|
|
143
|
+
};
|
|
144
|
+
/** Status → canonical arc code, falling back to `arc.error`. */
|
|
145
|
+
function statusToArcCode(status) {
|
|
146
|
+
return STATUS_CODE_MAP[status] ?? "arc.error";
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Quick `ArcError` constructor when the bundled subclasses don't fit.
|
|
150
|
+
*
|
|
151
|
+
* If `details.code` is a string, it's lifted to the top-level `error.code`
|
|
152
|
+
* (the canonical wire slot for business signals — `'ORG_CONTEXT_REQUIRED'`,
|
|
153
|
+
* `'ALL_FIELDS_STRIPPED'`, etc). The same code stays in `details` so
|
|
154
|
+
* in-process consumers reading `error.details.code` still work. Without
|
|
155
|
+
* this lift, business codes get buried in `details` and the wire envelope
|
|
156
|
+
* carries only the status-derived `arc.forbidden` / `arc.bad_request`
|
|
157
|
+
* fallback — `repo-core`'s `toErrorContract` doesn't surface free-form
|
|
158
|
+
* `details` objects (its `details[]` array is reserved for validation /
|
|
159
|
+
* duplicate-key items).
|
|
160
|
+
*/
|
|
161
|
+
function createError(statusCode, message, details) {
|
|
162
|
+
return new ArcError(message, {
|
|
163
|
+
code: (typeof details?.code === "string" ? details.code : void 0) ?? statusToArcCode(statusCode),
|
|
164
|
+
statusCode,
|
|
165
|
+
...details ? { details } : {}
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Domain-error escape hatch. Use a hierarchical code that scopes the error
|
|
170
|
+
* to your package (`'commerce.cart.locked'`, `'payment.gateway.timeout'`).
|
|
171
|
+
*/
|
|
172
|
+
function createDomainError(code, message, statusCode = 400, details) {
|
|
173
|
+
return new ArcError(message, {
|
|
174
|
+
code,
|
|
175
|
+
statusCode,
|
|
176
|
+
...details ? { details } : {}
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
/** Type guard. */
|
|
180
|
+
function isArcError(error) {
|
|
181
|
+
return error instanceof ArcError;
|
|
182
|
+
}
|
|
183
|
+
//#endregion
|
|
184
|
+
export { OrgAccessDeniedError as a, ServiceUnavailableError as c, createDomainError as d, createError as f, NotFoundError as i, UnauthorizedError as l, statusToArcCode as m, ConflictError as n, OrgRequiredError as o, isArcError as p, ForbiddenError as r, RateLimitError as s, ArcError as t, ValidationError as u };
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { t as __exportAll } from "./chunk-BpYLSNr0.mjs";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { arcLog } from "./logger/index.mjs";
|
|
3
|
+
import { d as createDomainError } from "./errors-j4aJm1Wg.mjs";
|
|
4
|
+
import { t as requestContext } from "./requestContext-SSaaTgW8.mjs";
|
|
5
|
+
import { n as createEvent, t as MemoryEventTransport } from "./EventTransport-DLWoUMHy.mjs";
|
|
4
6
|
import fp from "fastify-plugin";
|
|
5
7
|
//#region src/events/retry.ts
|
|
6
8
|
/**
|
|
@@ -109,8 +111,18 @@ var eventPlugin_exports = /* @__PURE__ */ __exportAll({
|
|
|
109
111
|
eventPlugin: () => eventPlugin
|
|
110
112
|
});
|
|
111
113
|
const eventPlugin = async (fastify, opts = {}) => {
|
|
112
|
-
const { transport = new MemoryEventTransport(), logEvents = false, failOpen = true, retry: retryOpts, deadLetterQueue: dlqOpts, wal, onPublish, onPublishError, registry, validateMode: rawValidateMode } = opts;
|
|
114
|
+
const { transport = new MemoryEventTransport(), logEvents = false, failOpen = true, retry: retryOpts, deadLetterQueue: dlqOpts, wal, onPublish, onPublishError, registry, validateMode: rawValidateMode, warnOnDuplicate: rawWarnOnDuplicate } = opts;
|
|
113
115
|
const validateMode = rawValidateMode ?? (registry ? "warn" : "off");
|
|
116
|
+
const warnOnDuplicate = rawWarnOnDuplicate ?? process.env.NODE_ENV !== "production";
|
|
117
|
+
const DUP_WINDOW_MS = 5e3;
|
|
118
|
+
const recentPublishes = warnOnDuplicate ? /* @__PURE__ */ new Map() : /* @__PURE__ */ new Map();
|
|
119
|
+
const evictExpiredPublishes = (now) => {
|
|
120
|
+
if (recentPublishes.size === 0) return;
|
|
121
|
+
for (const [key, timestamp] of recentPublishes) {
|
|
122
|
+
if (now - timestamp <= DUP_WINDOW_MS) break;
|
|
123
|
+
recentPublishes.delete(key);
|
|
124
|
+
}
|
|
125
|
+
};
|
|
114
126
|
fastify.decorate("events", {
|
|
115
127
|
publish: async (type, payload, meta) => {
|
|
116
128
|
if (!type || typeof type !== "string") throw new Error("[Arc Events] Event type must be a non-empty string");
|
|
@@ -121,6 +133,15 @@ const eventPlugin = async (fastify, opts = {}) => {
|
|
|
121
133
|
...store?.requestId && !meta?.correlationId ? { correlationId: store.requestId } : {},
|
|
122
134
|
...meta
|
|
123
135
|
});
|
|
136
|
+
if (warnOnDuplicate && event.meta.correlationId) {
|
|
137
|
+
const now = Date.now();
|
|
138
|
+
evictExpiredPublishes(now);
|
|
139
|
+
const dupKey = `${type}::${event.meta.correlationId}`;
|
|
140
|
+
const previous = recentPublishes.get(dupKey);
|
|
141
|
+
if (previous !== void 0 && now - previous <= DUP_WINDOW_MS) arcLog("events").warn(`Duplicate publish detected: event type "${type}" published twice within ${DUP_WINDOW_MS}ms with correlationId "${event.meta.correlationId}". Subscribers will fire twice for the same logical event. Common cause: a domain service holds both a publisher and a notification helper that also publishes to the same bus — pick one. Set \`arcPlugins: { events: { warnOnDuplicate: false } }\` to silence.`);
|
|
142
|
+
recentPublishes.delete(dupKey);
|
|
143
|
+
recentPublishes.set(dupKey, now);
|
|
144
|
+
}
|
|
124
145
|
if (logEvents) fastify.log?.info?.({
|
|
125
146
|
eventType: type,
|
|
126
147
|
eventId: event.meta.id,
|
|
@@ -130,7 +151,10 @@ const eventPlugin = async (fastify, opts = {}) => {
|
|
|
130
151
|
const result = registry.validate(type, payload, event.meta.schemaVersion);
|
|
131
152
|
if (!result.valid) {
|
|
132
153
|
const msg = `[Arc Events] Event '${type}' payload validation failed: ${result.errors?.join("; ")}`;
|
|
133
|
-
if (validateMode === "reject") throw
|
|
154
|
+
if (validateMode === "reject") throw createDomainError("arc.event.validation_error", msg, 400, {
|
|
155
|
+
event: type,
|
|
156
|
+
errors: result.errors
|
|
157
|
+
});
|
|
134
158
|
fastify.log?.warn?.(msg);
|
|
135
159
|
}
|
|
136
160
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { a as EventTransport, i as EventLogger, n as DomainEvent, r as EventHandler } from "./EventTransport-CT_52aWU.mjs";
|
|
2
2
|
import { FastifyPluginAsync } from "fastify";
|
|
3
3
|
|
|
4
4
|
//#region src/events/defineEvent.d.ts
|
|
@@ -275,6 +275,29 @@ interface EventPluginOptions {
|
|
|
275
275
|
* - `'off'`: skip validation entirely (registry is only for introspection)
|
|
276
276
|
*/
|
|
277
277
|
validateMode?: "warn" | "reject" | "off";
|
|
278
|
+
/**
|
|
279
|
+
* Dev-mode duplicate-publish detector (v2.12).
|
|
280
|
+
*
|
|
281
|
+
* When enabled, arc keeps a 5-second LRU on `(eventType, correlationId)`
|
|
282
|
+
* and emits an `arcLog("events").warn(...)` the second time a request
|
|
283
|
+
* publishes the same event with the same correlation id within the
|
|
284
|
+
* window. Catches the dual-publish trap where a domain service holds
|
|
285
|
+
* BOTH a publisher AND a notification helper that internally publishes
|
|
286
|
+
* to the same bus — every subscriber fires twice for one logical event.
|
|
287
|
+
*
|
|
288
|
+
* Defaults:
|
|
289
|
+
* - `undefined` → enabled when `process.env.NODE_ENV !== 'production'`.
|
|
290
|
+
* - `true` → always enabled (catches duplicates in prod too — overhead
|
|
291
|
+
* is one Map lookup per publish).
|
|
292
|
+
* - `false` → always disabled.
|
|
293
|
+
*
|
|
294
|
+
* When a duplicate is detected, arc logs once and **still publishes** —
|
|
295
|
+
* the detector is observability, not enforcement. Pair with the outbox
|
|
296
|
+
* for at-most-once delivery.
|
|
297
|
+
*
|
|
298
|
+
* Documented in `wiki/gotchas.md` (#20).
|
|
299
|
+
*/
|
|
300
|
+
warnOnDuplicate?: boolean;
|
|
278
301
|
}
|
|
279
302
|
declare module "fastify" {
|
|
280
303
|
interface FastifyInstance {
|
package/dist/events/index.d.mts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { a as
|
|
3
|
-
import { a as withRetry, c as EventDefinitionOutput, d as EventSchema, f as ValidationResult, i as createDeadLetterPublisher, l as EventRegistry, m as defineEvent, n as eventPlugin, o as CustomValidator, p as createEventRegistry, r as RetryOptions, s as EventDefinitionInput, t as EventPluginOptions, u as EventRegistryOptions } from "../eventPlugin-DDJoNEPL.mjs";
|
|
1
|
+
import { a as EventTransport, i as EventLogger, n as DomainEvent, o as MemoryEventTransport, r as EventHandler, s as MemoryEventTransportOptions, t as DeadLetteredEvent } from "../EventTransport-CT_52aWU.mjs";
|
|
2
|
+
import { a as withRetry, c as EventDefinitionOutput, d as EventSchema, f as ValidationResult, i as createDeadLetterPublisher, l as EventRegistry, m as defineEvent, n as eventPlugin, o as CustomValidator, p as createEventRegistry, r as RetryOptions, s as EventDefinitionInput, t as EventPluginOptions, u as EventRegistryOptions } from "../eventPlugin-qXpqTebY.mjs";
|
|
4
3
|
import { RedisEventTransportOptions, RedisLike } from "./transports/redis.mjs";
|
|
5
|
-
import { r as RedisStreamTransportOptions, t as RedisStreamLike } from "../redis-stream-
|
|
4
|
+
import { r as RedisStreamTransportOptions, t as RedisStreamLike } from "../redis-stream-D6HzR1Z_.mjs";
|
|
5
|
+
import { RepositoryLike } from "@classytic/repo-core/adapter";
|
|
6
6
|
|
|
7
7
|
//#region src/events/eventTypes.d.ts
|
|
8
8
|
/**
|
|
@@ -620,7 +620,7 @@ declare function repositoryAsOutboxStore(repository: RepositoryLike): OutboxStor
|
|
|
620
620
|
* ```
|
|
621
621
|
*/
|
|
622
622
|
type PayloadOf<D> = D extends EventDefinitionOutput<infer T> ? T : never;
|
|
623
|
-
interface WrapWithSchemaOptions<
|
|
623
|
+
interface WrapWithSchemaOptions<_T> {
|
|
624
624
|
/**
|
|
625
625
|
* Custom validator. Overrides the built-in lookup. Use this to plug AJV /
|
|
626
626
|
* Zod / TypeBox in. Same shape as `EventRegistryOptions.validate`.
|
|
@@ -759,4 +759,4 @@ interface FastifyEventBus {
|
|
|
759
759
|
};
|
|
760
760
|
}
|
|
761
761
|
//#endregion
|
|
762
|
-
export { ARC_LIFECYCLE_EVENTS, type ArcLifecycleEvent, CACHE_EVENTS, CRUD_EVENT_SUFFIXES, type CacheEvent, type CrudEventSuffix, type CustomValidator, type
|
|
762
|
+
export { ARC_LIFECYCLE_EVENTS, type ArcLifecycleEvent, CACHE_EVENTS, CRUD_EVENT_SUFFIXES, type CacheEvent, type CrudEventSuffix, type CustomValidator, type EventDefinitionInput, type EventDefinitionOutput, EventOutbox, type EventOutboxOptions, type EventPluginOptions, type EventRegistry, type EventRegistryOptions, type EventSchema, type ExponentialBackoffOptions, InvalidOutboxEventError, MemoryEventTransport, type MemoryEventTransportOptions, MemoryOutboxStore, type OutboxAcknowledgeOptions, type OutboxClaimOptions, type OutboxErrorInfo, type OutboxFailOptions, type OutboxFailureContext, type OutboxFailureDecision, type OutboxFailurePolicy, OutboxOwnershipError, type OutboxRelayErrorHandler, type OutboxRelayErrorKind, type OutboxStore, type OutboxWriteOptions, type PayloadOf, type RedisEventTransportOptions, type RedisLike, type RedisStreamLike, type RedisStreamTransportOptions, type RelayResult, type RetryOptions, type ValidationResult, type WrapWithBoundaryOptions, type WrapWithSchemaOptions, createDeadLetterPublisher, createEventRegistry, crudEventType, defineEvent, eventPlugin, exponentialBackoff, repositoryAsOutboxStore, subscribeWithBoundary, subscribeWithSchema, withRetry, wrapWithBoundary, wrapWithSchema };
|
package/dist/events/index.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { n as defineEvent, t as createEventRegistry } from "../defineEvent-
|
|
3
|
-
import { i as withRetry, r as createDeadLetterPublisher, t as eventPlugin } from "../eventPlugin-
|
|
4
|
-
import { n as createSafeGetOne, t as createIsDuplicateKeyError } from "../store-helpers-
|
|
1
|
+
import { t as MemoryEventTransport } from "../EventTransport-DLWoUMHy.mjs";
|
|
2
|
+
import { n as defineEvent, t as createEventRegistry } from "../defineEvent-D5h7EvAx.mjs";
|
|
3
|
+
import { i as withRetry, r as createDeadLetterPublisher, t as eventPlugin } from "../eventPlugin-CaKTYkYM.mjs";
|
|
4
|
+
import { n as createSafeGetOne, t as createIsDuplicateKeyError } from "../store-helpers-BkIN9-vu.mjs";
|
|
5
5
|
import { and, anyOf, eq, lte, ne, or } from "@classytic/repo-core/filter";
|
|
6
6
|
import { update } from "@classytic/repo-core/update";
|
|
7
7
|
//#region src/events/eventTypes.ts
|
|
@@ -170,30 +170,6 @@ var MemoryOutboxStore = class {
|
|
|
170
170
|
};
|
|
171
171
|
//#endregion
|
|
172
172
|
//#region src/events/repository-outbox-adapter.ts
|
|
173
|
-
/**
|
|
174
|
-
* RepositoryLike → OutboxStore adapter.
|
|
175
|
-
*
|
|
176
|
-
* Maps the `OutboxStore` vocabulary (save / claimPending / acknowledge /
|
|
177
|
-
* fail / getDeadLettered / purge) onto arc's own `RepositoryLike` primitives
|
|
178
|
-
* (create / getOne / findAll / deleteMany / findOneAndUpdate). `EventOutbox`
|
|
179
|
-
* wraps a passed repository with this helper when you use the
|
|
180
|
-
* `{ repository }` option; the function is also re-exported from
|
|
181
|
-
* `@classytic/arc/events` so consumers can build and decorate the store
|
|
182
|
-
* manually (metrics, tracing, multi-transport fan-out).
|
|
183
|
-
*
|
|
184
|
-
* Portability: filters compose via `@classytic/repo-core/filter` and
|
|
185
|
-
* updates via `@classytic/repo-core/update`. The primary-key column name
|
|
186
|
-
* is read from `repository.idField` — mongokit defaults to `_id`,
|
|
187
|
-
* sqlitekit / pgkit / prismakit to the schema's declared PK. The adapter
|
|
188
|
-
* therefore runs on any kit that implements `StandardRepo.findOneAndUpdate`
|
|
189
|
-
* + `getOne` + `getAll` + `deleteMany` + `create`.
|
|
190
|
-
*
|
|
191
|
-
* `fail()` uses a lease-gated read-then-write pair to preserve
|
|
192
|
-
* `firstFailedAt` across retries without relying on Mongo's aggregation-
|
|
193
|
-
* pipeline `$ifNull`. Leases guarantee single-writer during the failure
|
|
194
|
-
* window (`claimPending` filters out non-owned rows), so the two calls are
|
|
195
|
-
* safe under concurrent relayers.
|
|
196
|
-
*/
|
|
197
173
|
const DEFAULT_LEASE_MS$1 = 3e4;
|
|
198
174
|
const DEFAULT_CLAIM_LIMIT = 100;
|
|
199
175
|
const DEFAULT_PURGE_BATCH = 500;
|
|
@@ -208,12 +184,12 @@ function repositoryAsOutboxStore(repository) {
|
|
|
208
184
|
const r = repository;
|
|
209
185
|
const idField = repository.idField ?? "_id";
|
|
210
186
|
/**
|
|
211
|
-
* Unwrap mongokit's pagination envelope ({
|
|
187
|
+
* Unwrap mongokit's pagination envelope ({ data, total, ... }) — some
|
|
212
188
|
* kits may return a bare array when pagination is disabled. Handle both.
|
|
213
189
|
*/
|
|
214
190
|
const unwrapDocs = (result) => {
|
|
215
191
|
if (Array.isArray(result)) return result;
|
|
216
|
-
return result?.
|
|
192
|
+
return result?.data ?? [];
|
|
217
193
|
};
|
|
218
194
|
const isDuplicateKeyError = createIsDuplicateKeyError(repository);
|
|
219
195
|
const safeGetOne = createSafeGetOne(repository);
|
|
@@ -532,7 +508,7 @@ var EventOutbox = class {
|
|
|
532
508
|
const valid = [];
|
|
533
509
|
let malformed = 0;
|
|
534
510
|
for (const event of pending) {
|
|
535
|
-
if (!event
|
|
511
|
+
if (!event?.type || !event.meta?.id) {
|
|
536
512
|
this._reportError("malformed_event", new InvalidOutboxEventError("store returned event missing type or meta.id — batch aborted"), event);
|
|
537
513
|
malformed++;
|
|
538
514
|
break;
|
|
@@ -551,7 +527,7 @@ var EventOutbox = class {
|
|
|
551
527
|
const canFail = typeof this._store.fail === "function";
|
|
552
528
|
let publishOutcomes;
|
|
553
529
|
if (canPublishMany && valid.length > 0) try {
|
|
554
|
-
const result = await this._transport.publishMany(valid);
|
|
530
|
+
const result = await this._transport.publishMany?.(valid);
|
|
555
531
|
publishOutcomes = new Map(result);
|
|
556
532
|
} catch (batchErr) {
|
|
557
533
|
publishOutcomes = /* @__PURE__ */ new Map();
|
|
@@ -596,7 +572,7 @@ var EventOutbox = class {
|
|
|
596
572
|
this._reportError("fail_failed", policyErr, event);
|
|
597
573
|
}
|
|
598
574
|
try {
|
|
599
|
-
await this._store.fail(event.meta.id, normalizeError(publishErr), failOpts);
|
|
575
|
+
await this._store.fail?.(event.meta.id, normalizeError(publishErr), failOpts);
|
|
600
576
|
if (failOpts.deadLetter) {
|
|
601
577
|
counts.deadLettered++;
|
|
602
578
|
this._attempts.delete(event.meta.id);
|
|
@@ -740,7 +716,7 @@ function wrapWithSchema(definition, handler, options = {}) {
|
|
|
740
716
|
if (validate && definition.schema) result = validate(definition.schema, event.payload);
|
|
741
717
|
else if (registry) result = registry.validate(definition.name, event.payload, eventVersion);
|
|
742
718
|
else if (definition.schema) {
|
|
743
|
-
const { createEventRegistry } = await import("../defineEvent-
|
|
719
|
+
const { createEventRegistry } = await import("../defineEvent-D5h7EvAx.mjs").then((n) => n.r);
|
|
744
720
|
const adhoc = createEventRegistry();
|
|
745
721
|
adhoc.register(definition);
|
|
746
722
|
result = adhoc.validate(definition.name, event.payload);
|
|
@@ -834,4 +810,4 @@ async function subscribeWithBoundary(fastify, pattern, handler, options) {
|
|
|
834
810
|
return fastify.events.subscribe(pattern, wrapWithBoundary(handler, options));
|
|
835
811
|
}
|
|
836
812
|
//#endregion
|
|
837
|
-
export { ARC_LIFECYCLE_EVENTS, CACHE_EVENTS, CRUD_EVENT_SUFFIXES, EventOutbox, InvalidOutboxEventError, MemoryEventTransport, MemoryOutboxStore, OutboxOwnershipError,
|
|
813
|
+
export { ARC_LIFECYCLE_EVENTS, CACHE_EVENTS, CRUD_EVENT_SUFFIXES, EventOutbox, InvalidOutboxEventError, MemoryEventTransport, MemoryOutboxStore, OutboxOwnershipError, createDeadLetterPublisher, createEventRegistry, crudEventType, defineEvent, eventPlugin, exponentialBackoff, repositoryAsOutboxStore, subscribeWithBoundary, subscribeWithSchema, withRetry, wrapWithBoundary, wrapWithSchema };
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { n as RedisStreamTransport, r as RedisStreamTransportOptions, t as RedisStreamLike } from "../../redis-stream-
|
|
1
|
+
import { n as RedisStreamTransport, r as RedisStreamTransportOptions, t as RedisStreamLike } from "../../redis-stream-D6HzR1Z_.mjs";
|
|
2
2
|
export { type RedisStreamLike, RedisStreamTransport, type RedisStreamTransportOptions };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { a as EventTransport, i as EventLogger, n as DomainEvent, r as EventHandler } from "../../EventTransport-CT_52aWU.mjs";
|
|
2
2
|
|
|
3
3
|
//#region src/events/transports/redis.d.ts
|
|
4
4
|
interface RedisLike {
|
package/dist/factory/index.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as CustomPluginAuthOption, c as RawBodyOptions, d as ResourceLike, f as ResourceModule, i as CustomAuthenticatorOption, l as UnderPressureOptions, n as BetterAuthOption, o as JwtAuthOption, p as loadResources, r as CreateAppOptions, s as MultipartOptions, t as AuthOption, u as LoadResourcesOptions } from "../types-
|
|
1
|
+
import { a as CustomPluginAuthOption, c as RawBodyOptions, d as ResourceLike, f as ResourceModule, i as CustomAuthenticatorOption, l as UnderPressureOptions, n as BetterAuthOption, o as JwtAuthOption, p as loadResources, r as CreateAppOptions, s as MultipartOptions, t as AuthOption, u as LoadResourcesOptions } from "../types-BvqwCCSx.mjs";
|
|
2
2
|
import { FastifyInstance } from "fastify";
|
|
3
3
|
|
|
4
4
|
//#region src/factory/createApp.d.ts
|
|
@@ -14,7 +14,7 @@ import { FastifyInstance } from "fastify";
|
|
|
14
14
|
* 4. Arc core (fastify.arc, events)
|
|
15
15
|
* 5. Arc plugins (requestId, health, caching, SSE, metrics, versioning)
|
|
16
16
|
* 6. Auth (scope decoration, auth strategy, elevation, error handler)
|
|
17
|
-
* 7. plugins() — user infra (DB,
|
|
17
|
+
* 7. plugins() — user infra (DB, data, webhooks)
|
|
18
18
|
* 8. bootstrap[] — domain init (singletons, event handlers)
|
|
19
19
|
* 9. resources[] — auto-discovered routes (prefix + skipGlobalPrefix)
|
|
20
20
|
* 10. afterResources() — post-registration wiring
|
package/dist/factory/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { a as edgePreset, c as testingPreset, i as developmentPreset, n as createApp, o as getPreset, s as productionPreset, t as ArcFactory } from "../createApp-
|
|
2
|
-
import { t as loadResources } from "../loadResources-
|
|
1
|
+
import { a as edgePreset, c as testingPreset, i as developmentPreset, n as createApp, o as getPreset, s as productionPreset, t as ArcFactory } from "../createApp-XX2-N0Yd.mjs";
|
|
2
|
+
import { t as loadResources } from "../loadResources-DBMQg_Aj.mjs";
|
|
3
3
|
//#region src/factory/edge.ts
|
|
4
4
|
/**
|
|
5
5
|
* Convert a Fastify app into a Web Standards fetch handler.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { i as RequestScope } from "./types-CTYvcwHe.mjs";
|
|
2
2
|
import { FastifyRequest } from "fastify";
|
|
3
3
|
|
|
4
4
|
//#region src/permissions/types.d.ts
|
|
@@ -134,7 +134,7 @@ interface PermissionResult {
|
|
|
134
134
|
type PermissionCheck<TDoc = Record<string, unknown>> = ((context: PermissionContext<TDoc>) => boolean | PermissionResult | Promise<boolean | PermissionResult>) & PermissionCheckMeta;
|
|
135
135
|
/**
|
|
136
136
|
* Optional metadata attached to permission check functions.
|
|
137
|
-
* Used for OpenAPI
|
|
137
|
+
* Used for OpenAPI data, introspection, and route-level auth decisions.
|
|
138
138
|
*
|
|
139
139
|
* Each helper from `permissions/index.ts` writes its own discriminating tag
|
|
140
140
|
* so downstream tooling (OpenAPI generator, MCP resource builder, route
|
|
@@ -173,6 +173,27 @@ interface PermissionCheckMeta {
|
|
|
173
173
|
* (e.g. from route params).
|
|
174
174
|
*/
|
|
175
175
|
_orgInScopeTarget?: string | ((ctx: PermissionContext) => string | undefined);
|
|
176
|
+
/**
|
|
177
|
+
* Set by requireDPoP() — the inbound credential must be sender-constrained
|
|
178
|
+
* via DPoP (RFC 9449), with `scope.dpopJkt` set by the authenticate
|
|
179
|
+
* function after a successful proof verification.
|
|
180
|
+
*/
|
|
181
|
+
_dpopRequired?: boolean;
|
|
182
|
+
/**
|
|
183
|
+
* Set by requireMandate() — the capability string the mandate on
|
|
184
|
+
* `scope.mandate` must authorize (e.g. `payment.charge`, `data.export`).
|
|
185
|
+
*/
|
|
186
|
+
_mandateCapability?: string;
|
|
187
|
+
/**
|
|
188
|
+
* Set by requireAgentScope() — composite descriptor for AI-agent flows.
|
|
189
|
+
* Tools (audit, OpenAPI, MCP) can render the full agent-auth requirement
|
|
190
|
+
* in one read instead of unpacking three separate metadata fields.
|
|
191
|
+
*/
|
|
192
|
+
_agentScope?: {
|
|
193
|
+
capability: string;
|
|
194
|
+
scopes?: readonly string[];
|
|
195
|
+
dpop: boolean;
|
|
196
|
+
};
|
|
176
197
|
}
|
|
177
198
|
//#endregion
|
|
178
199
|
//#region src/permissions/fields.d.ts
|
package/dist/hooks/index.d.mts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { An as afterUpdate, Cn as HookOperation, Dn as HookSystemOptions, En as HookSystem, Fn as defineHook, Mn as beforeDelete, Nn as beforeUpdate, On as afterCreate, Pn as createHookSystem, Sn as HookHandler, Tn as HookRegistration, bn as DefineHookOptions, jn as beforeCreate, kn as afterDelete, wn as HookPhase, xn as HookContext } from "../index-BtW7qYwa.mjs";
|
|
2
2
|
export { type DefineHookOptions, type HookContext, type HookHandler, type HookOperation, type HookPhase, type HookRegistration, HookSystem, type HookSystemOptions, afterCreate, afterDelete, afterUpdate, beforeCreate, beforeDelete, beforeUpdate, createHookSystem, defineHook };
|
package/dist/hooks/index.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as beforeCreate, c as createHookSystem, i as afterUpdate, l as defineHook, n as afterCreate, o as beforeDelete, r as afterDelete, s as beforeUpdate, t as HookSystem } from "../HookSystem-
|
|
1
|
+
import { a as beforeCreate, c as createHookSystem, i as afterUpdate, l as defineHook, n as afterCreate, o as beforeDelete, r as afterDelete, s as beforeUpdate, t as HookSystem } from "../HookSystem-Iiebom92.mjs";
|
|
2
2
|
export { HookSystem, afterCreate, afterDelete, afterUpdate, beforeCreate, beforeDelete, beforeUpdate, createHookSystem, defineHook };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { Mn as RepositoryLike } from "../index-CXXRbnf8.mjs";
|
|
2
1
|
import { i as createIdempotencyResult, n as IdempotencyResult, r as IdempotencyStore, t as IdempotencyLock } from "../interface-DfLGcus7.mjs";
|
|
3
2
|
import { i as RedisIdempotencyStoreOptions, n as RedisClient } from "../redis-DiMkdHEl.mjs";
|
|
4
3
|
import { FastifyPluginAsync } from "fastify";
|
|
4
|
+
import { RepositoryLike } from "@classytic/repo-core/adapter";
|
|
5
5
|
|
|
6
6
|
//#region src/idempotency/idempotencyPlugin.d.ts
|
|
7
7
|
interface IdempotencyPluginOptions {
|
|
@@ -1,28 +1,9 @@
|
|
|
1
|
-
import { n as createSafeGetOne, t as createIsDuplicateKeyError } from "../store-helpers-
|
|
1
|
+
import { n as createSafeGetOne, t as createIsDuplicateKeyError } from "../store-helpers-BkIN9-vu.mjs";
|
|
2
2
|
import { createHash } from "node:crypto";
|
|
3
3
|
import fp from "fastify-plugin";
|
|
4
4
|
import { and, eq, exists, gt, lt, or, startsWith } from "@classytic/repo-core/filter";
|
|
5
5
|
import { update } from "@classytic/repo-core/update";
|
|
6
6
|
//#region src/idempotency/repository-idempotency-adapter.ts
|
|
7
|
-
/**
|
|
8
|
-
* RepositoryLike → IdempotencyStore adapter.
|
|
9
|
-
*
|
|
10
|
-
* Maps the idempotency store's verbs (get / set / tryLock / unlock / delete /
|
|
11
|
-
* deleteByPrefix / findByPrefix) onto arc's canonical repository primitives
|
|
12
|
-
* (`getOne` / `deleteMany` / `findOneAndUpdate`). `idempotencyPlugin` wraps
|
|
13
|
-
* a passed repository with this helper when you use the `{ repository }`
|
|
14
|
-
* option; the function is also re-exported from `@classytic/arc/idempotency`
|
|
15
|
-
* so consumers can build and decorate the store (metrics, tracing, key
|
|
16
|
-
* namespacing) before passing it via `store:`.
|
|
17
|
-
*
|
|
18
|
-
* Portability: filters compose via `@classytic/repo-core/filter` builders
|
|
19
|
-
* (`and` / `or` / `eq` / `gt` / `lt` / `exists` / `startsWith`) and updates
|
|
20
|
-
* via `@classytic/repo-core/update` (`update({ set, unset, setOnInsert })`).
|
|
21
|
-
* Both IRs compile to Mongo operators on mongokit, SQL predicates on
|
|
22
|
-
* sqlitekit / pgkit, and `WhereInput` / `update` on prismakit. The store
|
|
23
|
-
* therefore runs identically on every backend that implements the
|
|
24
|
-
* `StandardRepo.findOneAndUpdate` + `getOne` + `deleteMany` surface.
|
|
25
|
-
*/
|
|
26
7
|
function repositoryAsIdempotencyStore(repository, defaultTtlMs) {
|
|
27
8
|
const missing = [];
|
|
28
9
|
if (typeof repository.getOne !== "function") missing.push("getOne");
|
|
@@ -218,7 +218,7 @@ function upstashAsIdempotencyClient(client) {
|
|
|
218
218
|
const keyCount = _numKeys;
|
|
219
219
|
const keys = args.slice(0, keyCount).map(String);
|
|
220
220
|
const rest = args.slice(keyCount);
|
|
221
|
-
return client.eval(script, keys, rest);
|
|
221
|
+
return client.eval?.(script, keys, rest);
|
|
222
222
|
} : void 0,
|
|
223
223
|
async quit() {
|
|
224
224
|
return "OK";
|