@classytic/arc 2.3.0 → 2.4.2
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 +187 -18
- package/bin/arc.js +11 -3
- package/dist/BaseController-CkM5dUh_.mjs +1031 -0
- package/dist/{EventTransport-BkUDYZEb.d.mts → EventTransport-wc5hSLik.d.mts} +1 -1
- package/dist/{HookSystem-BsGV-j2l.mjs → HookSystem-COkyWztM.mjs} +2 -3
- package/dist/{ResourceRegistry-7Ic20ZMw.mjs → ResourceRegistry-DeCIFlix.mjs} +8 -5
- package/dist/adapters/index.d.mts +3 -5
- package/dist/adapters/index.mjs +2 -3
- package/dist/{prisma-DJbMt3yf.mjs → adapters-DTC4Ug66.mjs} +45 -12
- package/dist/audit/index.d.mts +4 -7
- package/dist/audit/index.mjs +2 -29
- package/dist/audit/mongodb.d.mts +1 -4
- package/dist/audit/mongodb.mjs +2 -3
- package/dist/auth/index.d.mts +7 -9
- package/dist/auth/index.mjs +65 -63
- package/dist/auth/redis-session.d.mts +1 -1
- package/dist/auth/redis-session.mjs +1 -2
- package/dist/{betterAuthOpenApi-DjWDddNc.mjs → betterAuthOpenApi-lz0IRbXJ.mjs} +4 -6
- package/dist/cache/index.d.mts +23 -23
- package/dist/cache/index.mjs +4 -6
- package/dist/{caching-GSDJcA6-.mjs → caching-BSXB-Xr7.mjs} +2 -24
- package/dist/chunk-BpYLSNr0.mjs +14 -0
- package/dist/circuitBreaker-BOBOpN2w.mjs +284 -0
- package/dist/circuitBreaker-JP2GdJ4b.d.mts +206 -0
- package/dist/cli/commands/describe.mjs +24 -7
- package/dist/cli/commands/docs.mjs +6 -7
- package/dist/cli/commands/doctor.d.mts +10 -0
- package/dist/cli/commands/doctor.mjs +156 -0
- package/dist/cli/commands/generate.mjs +66 -17
- package/dist/cli/commands/init.mjs +315 -45
- package/dist/cli/commands/introspect.mjs +2 -4
- package/dist/cli/index.d.mts +1 -10
- package/dist/cli/index.mjs +4 -153
- package/dist/{constants-DdXFXQtN.mjs → constants-Cxde4rpC.mjs} +1 -2
- package/dist/core/index.d.mts +3 -5
- package/dist/core/index.mjs +5 -4
- package/dist/core-C1XCMtqM.mjs +185 -0
- package/dist/{createApp-CgKOPhA4.mjs → createApp-ByWNRsZj.mjs} +64 -35
- package/dist/{defineResource-DWbpJYtm.mjs → defineResource-D9aY5Cy6.mjs} +108 -1157
- package/dist/discovery/index.mjs +37 -5
- package/dist/docs/index.d.mts +6 -9
- package/dist/docs/index.mjs +3 -21
- package/dist/dynamic/index.d.mts +93 -0
- package/dist/dynamic/index.mjs +122 -0
- package/dist/{elevation-DSTbVvYj.mjs → elevation-BEdACOLB.mjs} +5 -36
- package/dist/{elevation-DGo5shaX.d.mts → elevation-Ca_yveIO.d.mts} +41 -7
- package/dist/{errorHandler-C3GY3_ow.mjs → errorHandler--zp54tGc.mjs} +3 -5
- package/dist/errorHandler-Do4vVQ1f.d.mts +139 -0
- package/dist/{errors-DBANPbGr.mjs → errors-rxhfP7Hf.mjs} +1 -2
- package/dist/{eventPlugin-BEOvaDqo.mjs → eventPlugin-Ba00swHF.mjs} +25 -27
- package/dist/{eventPlugin-H6wDDjGO.d.mts → eventPlugin-iGrSEmwJ.d.mts} +105 -5
- package/dist/events/index.d.mts +72 -7
- package/dist/events/index.mjs +216 -4
- package/dist/events/transports/redis-stream-entry.d.mts +1 -1
- package/dist/events/transports/redis-stream-entry.mjs +19 -7
- package/dist/events/transports/redis.d.mts +1 -1
- package/dist/events/transports/redis.mjs +3 -4
- package/dist/factory/index.d.mts +23 -9
- package/dist/factory/index.mjs +48 -3
- package/dist/{fields-Bi_AVKSo.d.mts → fields-DFwdaWCq.d.mts} +1 -1
- package/dist/{fields-CTd_CrKr.mjs → fields-ipsbIRPK.mjs} +1 -2
- package/dist/hooks/index.d.mts +1 -3
- package/dist/hooks/index.mjs +2 -3
- package/dist/idempotency/index.d.mts +5 -5
- package/dist/idempotency/index.mjs +3 -7
- package/dist/idempotency/mongodb.d.mts +1 -1
- package/dist/idempotency/mongodb.mjs +4 -5
- package/dist/idempotency/redis.d.mts +1 -1
- package/dist/idempotency/redis.mjs +2 -5
- package/dist/{fastifyAdapter-6b_eRDBw.d.mts → index-BL8CaQih.d.mts} +56 -57
- package/dist/index-Diqcm14c.d.mts +369 -0
- package/dist/{prisma-Dy5S5F5i.d.mts → index-yhxyjqNb.d.mts} +4 -5
- package/dist/index.d.mts +100 -105
- package/dist/index.mjs +85 -58
- package/dist/integrations/event-gateway.d.mts +1 -1
- package/dist/integrations/event-gateway.mjs +8 -4
- package/dist/integrations/index.d.mts +4 -2
- package/dist/integrations/index.mjs +1 -1
- package/dist/integrations/jobs.d.mts +2 -2
- package/dist/integrations/jobs.mjs +63 -14
- package/dist/integrations/mcp/index.d.mts +219 -0
- package/dist/integrations/mcp/index.mjs +572 -0
- package/dist/integrations/mcp/testing.d.mts +53 -0
- package/dist/integrations/mcp/testing.mjs +104 -0
- package/dist/integrations/streamline.mjs +39 -19
- package/dist/integrations/webhooks.d.mts +56 -0
- package/dist/integrations/webhooks.mjs +139 -0
- package/dist/integrations/websocket-redis.d.mts +46 -0
- package/dist/integrations/websocket-redis.mjs +50 -0
- package/dist/integrations/websocket.d.mts +68 -2
- package/dist/integrations/websocket.mjs +96 -13
- package/dist/{interface-CSNjltAc.d.mts → interface-B4awm1RJ.d.mts} +2 -2
- package/dist/interface-DGmPxakH.d.mts +2213 -0
- package/dist/{keys-DhqDRxv3.mjs → keys-qcD-TVJl.mjs} +3 -4
- package/dist/{logger-ByrvQWZO.mjs → logger-Dz3j1ItV.mjs} +2 -4
- package/dist/{memory-B2v7KrCB.mjs → memory-Cb_7iy9e.mjs} +2 -4
- package/dist/metrics-Csh4nsvv.mjs +224 -0
- package/dist/migrations/index.d.mts +113 -44
- package/dist/migrations/index.mjs +84 -102
- package/dist/{mongodb-DNKEExbf.mjs → mongodb-BuQ7fNTg.mjs} +1 -4
- package/dist/{mongodb-ClykrfGo.d.mts → mongodb-CUpYfxfD.d.mts} +2 -3
- package/dist/{mongodb-Dg8O_gvd.d.mts → mongodb-bga9AbkD.d.mts} +2 -2
- package/dist/{openapi-9nB_kiuR.mjs → openapi-CBmZ6EQN.mjs} +4 -21
- package/dist/org/index.d.mts +12 -14
- package/dist/org/index.mjs +92 -119
- package/dist/org/types.d.mts +2 -2
- package/dist/org/types.mjs +1 -1
- package/dist/permissions/index.d.mts +4 -278
- package/dist/permissions/index.mjs +4 -579
- package/dist/permissions-CA5zg0yK.mjs +751 -0
- package/dist/plugins/index.d.mts +104 -107
- package/dist/plugins/index.mjs +203 -313
- package/dist/plugins/response-cache.mjs +4 -69
- package/dist/plugins/tracing-entry.d.mts +1 -1
- package/dist/plugins/tracing-entry.mjs +24 -11
- package/dist/{pluralize-CM-jZg7p.mjs → pluralize-CcT6qF0a.mjs} +12 -13
- package/dist/policies/index.d.mts +2 -2
- package/dist/policies/index.mjs +80 -83
- package/dist/presets/index.d.mts +26 -19
- package/dist/presets/index.mjs +2 -142
- package/dist/presets/multiTenant.d.mts +1 -4
- package/dist/presets/multiTenant.mjs +4 -6
- package/dist/presets-C9QXJV1u.mjs +422 -0
- package/dist/{queryCachePlugin-B6R0d4av.mjs → queryCachePlugin-ClosZdNS.mjs} +6 -27
- package/dist/{queryCachePlugin-Q6SYuHZ6.d.mts → queryCachePlugin-DcmETvcB.d.mts} +3 -3
- package/dist/queryParser-CgCtsjti.mjs +352 -0
- package/dist/{redis-UwjEp8Ea.d.mts → redis-CQ5YxMC5.d.mts} +2 -2
- package/dist/{redis-stream-CBg0upHI.d.mts → redis-stream-BW9UKLZM.d.mts} +9 -2
- package/dist/registry/index.d.mts +1 -4
- package/dist/registry/index.mjs +3 -4
- package/dist/{introspectionPlugin-B3JkrjwU.mjs → registry-I-ogLgL9.mjs} +1 -8
- package/dist/{requestContext-xi6OKBL-.mjs → requestContext-DYtmNpm5.mjs} +1 -3
- package/dist/resourceToTools-PMFE8HIv.mjs +533 -0
- package/dist/rpc/index.d.mts +90 -0
- package/dist/rpc/index.mjs +248 -0
- package/dist/{schemaConverter-Dtg0Kt9T.mjs → schemaConverter-DjzHpFam.mjs} +1 -2
- package/dist/schemas/index.d.mts +30 -30
- package/dist/schemas/index.mjs +2 -4
- package/dist/scope/index.d.mts +13 -2
- package/dist/scope/index.mjs +18 -5
- package/dist/{sessionManager-D_iEHjQl.d.mts → sessionManager-wbkYj2HL.d.mts} +2 -2
- package/dist/{sse-DkqQ1uxb.mjs → sse-BkViJPlT.mjs} +4 -25
- package/dist/testing/index.d.mts +551 -567
- package/dist/testing/index.mjs +1744 -1799
- package/dist/{tracing-8CEbhF0w.d.mts → tracing-bz_U4EM1.d.mts} +6 -1
- package/dist/{typeGuards-DwxA1t_L.mjs → typeGuards-Cj5Rgvlg.mjs} +1 -2
- package/dist/types/index.d.mts +4 -946
- package/dist/types/index.mjs +2 -4
- package/dist/types-BJmgxNbF.d.mts +275 -0
- package/dist/{types-RLkFVgaw.d.mts → types-BNUccdcf.d.mts} +2 -2
- package/dist/{types-Beqn1Un7.mjs → types-C6TQjtdi.mjs} +30 -2
- package/dist/{types-tKwaViYB.d.mts → types-Dt0-AI6E.d.mts} +68 -27
- package/dist/{types-DelU6kln.mjs → types-ZUu_h0jp.mjs} +1 -2
- package/dist/utils/index.d.mts +254 -351
- package/dist/utils/index.mjs +7 -6
- package/dist/utils-Dc0WhlIl.mjs +594 -0
- package/dist/versioning-BzfeHmhj.mjs +37 -0
- package/package.json +44 -10
- package/skills/arc/SKILL.md +518 -0
- package/skills/arc/references/auth.md +250 -0
- package/skills/arc/references/events.md +272 -0
- package/skills/arc/references/integrations.md +385 -0
- package/skills/arc/references/mcp.md +431 -0
- package/skills/arc/references/production.md +610 -0
- package/skills/arc/references/testing.md +183 -0
- package/dist/audited-CGdLiSlE.mjs +0 -140
- package/dist/chunk-C7Uep-_p.mjs +0 -20
- package/dist/circuitBreaker-CSS2VvL6.mjs +0 -1109
- package/dist/errorHandler-CW3OOeYq.d.mts +0 -72
- package/dist/interface-BtdYtQUA.d.mts +0 -1114
- package/dist/presets-BTeYbw7h.d.mts +0 -57
- package/dist/presets-CeFtfDR8.mjs +0 -119
- /package/dist/{errors-DAWRdiYP.d.mts → errors-CPpvPHT0.d.mts} +0 -0
- /package/dist/{externalPaths-SyPF2tgK.d.mts → externalPaths-DpO-s7r8.d.mts} +0 -0
- /package/dist/{interface-DTbsvIWe.d.mts → interface-D_BWALyZ.d.mts} +0 -0
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
import { t as CircuitBreaker } from "../circuitBreaker-BOBOpN2w.mjs";
|
|
2
|
+
//#region src/rpc/serviceClient.ts
|
|
3
|
+
/**
|
|
4
|
+
* Service Client — Resource-Oriented RPC
|
|
5
|
+
*
|
|
6
|
+
* Typed HTTP client that speaks Arc's resource protocol.
|
|
7
|
+
* Built for microservice-to-microservice communication with:
|
|
8
|
+
* - correlationId propagation (distributed tracing)
|
|
9
|
+
* - Retry with exponential backoff (transient failure recovery)
|
|
10
|
+
* - Circuit breaker integration (cascading failure prevention)
|
|
11
|
+
* - Error normalization (consistent error handling)
|
|
12
|
+
* - Lifecycle hooks (observability)
|
|
13
|
+
*
|
|
14
|
+
* Zero external dependencies — uses native fetch + Arc's CircuitBreaker.
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```typescript
|
|
18
|
+
* import { createServiceClient } from '@classytic/arc/rpc';
|
|
19
|
+
*
|
|
20
|
+
* const catalog = createServiceClient({
|
|
21
|
+
* baseUrl: 'http://catalog-service:3000',
|
|
22
|
+
* token: () => getServiceToken(),
|
|
23
|
+
* correlationId: () => request.id, // propagate trace context
|
|
24
|
+
* organizationId: req.scope.organizationId,
|
|
25
|
+
* retry: { maxRetries: 2, backoffMs: 200 },
|
|
26
|
+
* circuitBreaker: { failureThreshold: 5, resetTimeout: 30000 },
|
|
27
|
+
* onResponse: ({ method, url, status, durationMs }) => {
|
|
28
|
+
* metrics.histogram('rpc_duration', durationMs, { method, url, status });
|
|
29
|
+
* },
|
|
30
|
+
* });
|
|
31
|
+
*
|
|
32
|
+
* const products = await catalog.resource('product').list({ filters: { active: true } });
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
35
|
+
const DEFAULT_RETRYABLE_STATUSES = [
|
|
36
|
+
502,
|
|
37
|
+
503,
|
|
38
|
+
504,
|
|
39
|
+
408,
|
|
40
|
+
429
|
|
41
|
+
];
|
|
42
|
+
function createServiceClient(options) {
|
|
43
|
+
const { baseUrl, token, organizationId, correlationId, schemaVersion, headers: extraHeaders = {}, timeout = 1e4, retry: retryConfig, circuitBreaker: cbOpts, healthPath = "/_health/live", onRequest, onResponse } = options;
|
|
44
|
+
const base = baseUrl.replace(/\/+$/, "");
|
|
45
|
+
let breaker;
|
|
46
|
+
if (cbOpts) breaker = new CircuitBreaker(singleFetch, {
|
|
47
|
+
name: `service-client:${base}`,
|
|
48
|
+
failureThreshold: cbOpts.failureThreshold ?? 5,
|
|
49
|
+
resetTimeout: cbOpts.resetTimeout ?? 6e4,
|
|
50
|
+
timeout: cbOpts.timeout ?? timeout,
|
|
51
|
+
successThreshold: cbOpts.successThreshold ?? 1
|
|
52
|
+
});
|
|
53
|
+
function buildHeaders(hasBody = false) {
|
|
54
|
+
const h = {
|
|
55
|
+
accept: "application/json",
|
|
56
|
+
...extraHeaders
|
|
57
|
+
};
|
|
58
|
+
if (hasBody) h["content-type"] = "application/json";
|
|
59
|
+
const resolvedToken = typeof token === "function" ? token() : token;
|
|
60
|
+
if (resolvedToken) h.authorization = `Bearer ${resolvedToken}`;
|
|
61
|
+
if (organizationId) h["x-organization-id"] = organizationId;
|
|
62
|
+
if (schemaVersion) h["x-arc-schema-version"] = schemaVersion;
|
|
63
|
+
const resolvedCorrelationId = typeof correlationId === "function" ? correlationId() : correlationId;
|
|
64
|
+
if (resolvedCorrelationId) h["x-request-id"] = resolvedCorrelationId;
|
|
65
|
+
return h;
|
|
66
|
+
}
|
|
67
|
+
async function singleFetch(url, init) {
|
|
68
|
+
const controller = new AbortController();
|
|
69
|
+
const timer = setTimeout(() => controller.abort(), timeout);
|
|
70
|
+
try {
|
|
71
|
+
const hasBody = !!init.body;
|
|
72
|
+
const response = await fetch(url, {
|
|
73
|
+
...init,
|
|
74
|
+
signal: controller.signal,
|
|
75
|
+
headers: {
|
|
76
|
+
...buildHeaders(hasBody),
|
|
77
|
+
...init.headers ?? {}
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
let body;
|
|
81
|
+
if ((response.headers.get("content-type") ?? "").includes("application/json")) body = await response.json();
|
|
82
|
+
else {
|
|
83
|
+
const text = await response.text();
|
|
84
|
+
body = {
|
|
85
|
+
success: false,
|
|
86
|
+
error: response.statusText || "Unknown error",
|
|
87
|
+
message: text.slice(0, 200),
|
|
88
|
+
status: response.status
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
if (body.status === void 0) body.status = response.status;
|
|
92
|
+
if (body.success === void 0) body.success = response.ok;
|
|
93
|
+
return {
|
|
94
|
+
response,
|
|
95
|
+
body
|
|
96
|
+
};
|
|
97
|
+
} finally {
|
|
98
|
+
clearTimeout(timer);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
async function execute(method, url, init) {
|
|
102
|
+
const startTime = performance.now();
|
|
103
|
+
let lastResponse;
|
|
104
|
+
let retries = 0;
|
|
105
|
+
const maxRetries = retryConfig?.maxRetries ?? 0;
|
|
106
|
+
const backoffMs = retryConfig?.backoffMs ?? 200;
|
|
107
|
+
const maxBackoffMs = retryConfig?.maxBackoffMs ?? 5e3;
|
|
108
|
+
const retryableStatuses = retryConfig?.retryableStatuses ?? DEFAULT_RETRYABLE_STATUSES;
|
|
109
|
+
onRequest?.({
|
|
110
|
+
method,
|
|
111
|
+
url
|
|
112
|
+
});
|
|
113
|
+
for (let attempt = 0; attempt <= maxRetries; attempt++) try {
|
|
114
|
+
let result;
|
|
115
|
+
if (breaker) result = await breaker.call(url, init);
|
|
116
|
+
else result = await singleFetch(url, init);
|
|
117
|
+
lastResponse = result.body;
|
|
118
|
+
if (result.response.ok || !retryableStatuses.includes(result.response.status)) {
|
|
119
|
+
onResponse?.({
|
|
120
|
+
method,
|
|
121
|
+
url,
|
|
122
|
+
status: result.response.status,
|
|
123
|
+
durationMs: performance.now() - startTime,
|
|
124
|
+
retries
|
|
125
|
+
});
|
|
126
|
+
return result.body;
|
|
127
|
+
}
|
|
128
|
+
if (attempt < maxRetries) {
|
|
129
|
+
retries++;
|
|
130
|
+
await sleep(Math.min(backoffMs * 2 ** attempt, maxBackoffMs));
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
133
|
+
onResponse?.({
|
|
134
|
+
method,
|
|
135
|
+
url,
|
|
136
|
+
status: result.response.status,
|
|
137
|
+
durationMs: performance.now() - startTime,
|
|
138
|
+
retries
|
|
139
|
+
});
|
|
140
|
+
return result.body;
|
|
141
|
+
} catch (err) {
|
|
142
|
+
if (attempt < maxRetries) {
|
|
143
|
+
retries++;
|
|
144
|
+
await sleep(Math.min(backoffMs * 2 ** attempt, maxBackoffMs));
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
147
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
148
|
+
lastResponse = {
|
|
149
|
+
success: false,
|
|
150
|
+
error: error.message,
|
|
151
|
+
status: 0
|
|
152
|
+
};
|
|
153
|
+
onResponse?.({
|
|
154
|
+
method,
|
|
155
|
+
url,
|
|
156
|
+
status: 0,
|
|
157
|
+
durationMs: performance.now() - startTime,
|
|
158
|
+
retries
|
|
159
|
+
});
|
|
160
|
+
if (maxRetries === 0) throw error;
|
|
161
|
+
return lastResponse;
|
|
162
|
+
}
|
|
163
|
+
return lastResponse ?? {
|
|
164
|
+
success: false,
|
|
165
|
+
error: "Unknown error",
|
|
166
|
+
status: 0
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
function toQueryString(query) {
|
|
170
|
+
if (!query || Object.keys(query).length === 0) return "";
|
|
171
|
+
const params = new URLSearchParams();
|
|
172
|
+
for (const [key, value] of Object.entries(query)) if (value !== void 0 && value !== null) if (typeof value === "object") {
|
|
173
|
+
for (const [k, v] of Object.entries(value)) if (v !== void 0 && v !== null) params.set(k, String(v));
|
|
174
|
+
} else params.set(key, String(value));
|
|
175
|
+
const qs = params.toString();
|
|
176
|
+
return qs ? `?${qs}` : "";
|
|
177
|
+
}
|
|
178
|
+
function plural(name) {
|
|
179
|
+
if (name.endsWith("s")) return name;
|
|
180
|
+
if (name.endsWith("y") && !name.endsWith("ay") && !name.endsWith("ey") && !name.endsWith("oy") && !name.endsWith("uy")) return `${name.slice(0, -1)}ies`;
|
|
181
|
+
return `${name}s`;
|
|
182
|
+
}
|
|
183
|
+
return {
|
|
184
|
+
resource(name) {
|
|
185
|
+
const prefix = `${base}/${plural(name)}`;
|
|
186
|
+
return {
|
|
187
|
+
async list(query) {
|
|
188
|
+
return execute("GET", `${prefix}${toQueryString(query?.filters ? query.filters : query)}`, { method: "GET" });
|
|
189
|
+
},
|
|
190
|
+
async get(id) {
|
|
191
|
+
return execute("GET", `${prefix}/${id}`, { method: "GET" });
|
|
192
|
+
},
|
|
193
|
+
async create(data) {
|
|
194
|
+
return execute("POST", prefix, {
|
|
195
|
+
method: "POST",
|
|
196
|
+
body: JSON.stringify(data)
|
|
197
|
+
});
|
|
198
|
+
},
|
|
199
|
+
async update(id, data) {
|
|
200
|
+
return execute("PATCH", `${prefix}/${id}`, {
|
|
201
|
+
method: "PATCH",
|
|
202
|
+
body: JSON.stringify(data)
|
|
203
|
+
});
|
|
204
|
+
},
|
|
205
|
+
async delete(id) {
|
|
206
|
+
return execute("DELETE", `${prefix}/${id}`, { method: "DELETE" });
|
|
207
|
+
},
|
|
208
|
+
async action(id, actionName, data) {
|
|
209
|
+
return execute("POST", `${prefix}/${id}/action`, {
|
|
210
|
+
method: "POST",
|
|
211
|
+
body: JSON.stringify({
|
|
212
|
+
action: actionName,
|
|
213
|
+
...data
|
|
214
|
+
})
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
};
|
|
218
|
+
},
|
|
219
|
+
async call(method, path, body) {
|
|
220
|
+
const url = `${base}${path}`;
|
|
221
|
+
const init = { method };
|
|
222
|
+
if (body !== void 0) init.body = JSON.stringify(body);
|
|
223
|
+
return execute(method, url, init);
|
|
224
|
+
},
|
|
225
|
+
async health() {
|
|
226
|
+
try {
|
|
227
|
+
const controller = new AbortController();
|
|
228
|
+
const timer = setTimeout(() => controller.abort(), timeout);
|
|
229
|
+
try {
|
|
230
|
+
return (await fetch(`${base}${healthPath}`, {
|
|
231
|
+
method: "GET",
|
|
232
|
+
signal: controller.signal,
|
|
233
|
+
headers: buildHeaders()
|
|
234
|
+
})).ok;
|
|
235
|
+
} finally {
|
|
236
|
+
clearTimeout(timer);
|
|
237
|
+
}
|
|
238
|
+
} catch {
|
|
239
|
+
return false;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
function sleep(ms) {
|
|
245
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
246
|
+
}
|
|
247
|
+
//#endregion
|
|
248
|
+
export { createServiceClient };
|
|
@@ -93,6 +93,5 @@ function convertRouteSchema(schema) {
|
|
|
93
93
|
}
|
|
94
94
|
return result;
|
|
95
95
|
}
|
|
96
|
-
|
|
97
96
|
//#endregion
|
|
98
|
-
export { toJsonSchema as a, isZodSchema as i, convertRouteSchema as n, isJsonSchema as r, convertOpenApiSchemas as t };
|
|
97
|
+
export { toJsonSchema as a, isZodSchema as i, convertRouteSchema as n, isJsonSchema as r, convertOpenApiSchemas as t };
|
package/dist/schemas/index.d.mts
CHANGED
|
@@ -1,63 +1,63 @@
|
|
|
1
|
-
import * as _sinclair_typebox0 from "@sinclair/typebox";
|
|
2
|
-
import { Static, TObject, TSchema, TSchema as TSchema$1, Type } from "@sinclair/typebox";
|
|
3
1
|
import { FastifyPluginAsyncTypebox, FastifyPluginCallbackTypebox, TypeBoxTypeProvider, TypeBoxValidatorCompiler } from "@fastify/type-provider-typebox";
|
|
2
|
+
import * as _$_sinclair_typebox0 from "@sinclair/typebox";
|
|
3
|
+
import { Static, TObject, TSchema, TSchema as TSchema$1, Type } from "@sinclair/typebox";
|
|
4
4
|
|
|
5
5
|
//#region src/schemas/index.d.ts
|
|
6
6
|
/**
|
|
7
7
|
* Paginated list response — matches Arc's runtime format:
|
|
8
8
|
* `{ success, docs: [...], page, limit, total, pages, hasNext, hasPrev }`
|
|
9
9
|
*/
|
|
10
|
-
declare function ArcListResponse<T extends TSchema$1>(itemSchema: T): _sinclair_typebox0.TObject<{
|
|
11
|
-
success: _sinclair_typebox0.TBoolean;
|
|
12
|
-
docs: _sinclair_typebox0.TArray<T>;
|
|
13
|
-
page: _sinclair_typebox0.TInteger;
|
|
14
|
-
limit: _sinclair_typebox0.TInteger;
|
|
15
|
-
total: _sinclair_typebox0.TInteger;
|
|
16
|
-
pages: _sinclair_typebox0.TInteger;
|
|
17
|
-
hasNext: _sinclair_typebox0.TBoolean;
|
|
18
|
-
hasPrev: _sinclair_typebox0.TBoolean;
|
|
10
|
+
declare function ArcListResponse<T extends TSchema$1>(itemSchema: T): _$_sinclair_typebox0.TObject<{
|
|
11
|
+
success: _$_sinclair_typebox0.TBoolean;
|
|
12
|
+
docs: _$_sinclair_typebox0.TArray<T>;
|
|
13
|
+
page: _$_sinclair_typebox0.TInteger;
|
|
14
|
+
limit: _$_sinclair_typebox0.TInteger;
|
|
15
|
+
total: _$_sinclair_typebox0.TInteger;
|
|
16
|
+
pages: _$_sinclair_typebox0.TInteger;
|
|
17
|
+
hasNext: _$_sinclair_typebox0.TBoolean;
|
|
18
|
+
hasPrev: _$_sinclair_typebox0.TBoolean;
|
|
19
19
|
}>;
|
|
20
20
|
/**
|
|
21
21
|
* Single item response — `{ success, data: {...} }`
|
|
22
22
|
*/
|
|
23
|
-
declare function ArcItemResponse<T extends TSchema$1>(itemSchema: T): _sinclair_typebox0.TObject<{
|
|
24
|
-
success: _sinclair_typebox0.TBoolean;
|
|
23
|
+
declare function ArcItemResponse<T extends TSchema$1>(itemSchema: T): _$_sinclair_typebox0.TObject<{
|
|
24
|
+
success: _$_sinclair_typebox0.TBoolean;
|
|
25
25
|
data: T;
|
|
26
26
|
}>;
|
|
27
27
|
/**
|
|
28
28
|
* Mutation (create/update) response — `{ success, data: {...}, message? }`
|
|
29
29
|
*/
|
|
30
|
-
declare function ArcMutationResponse<T extends TSchema$1>(itemSchema: T): _sinclair_typebox0.TObject<{
|
|
31
|
-
success: _sinclair_typebox0.TBoolean;
|
|
30
|
+
declare function ArcMutationResponse<T extends TSchema$1>(itemSchema: T): _$_sinclair_typebox0.TObject<{
|
|
31
|
+
success: _$_sinclair_typebox0.TBoolean;
|
|
32
32
|
data: T;
|
|
33
|
-
message: _sinclair_typebox0.TOptional<_sinclair_typebox0.TString>;
|
|
33
|
+
message: _$_sinclair_typebox0.TOptional<_$_sinclair_typebox0.TString>;
|
|
34
34
|
}>;
|
|
35
35
|
/**
|
|
36
36
|
* Delete response — `{ success, message }`
|
|
37
37
|
*/
|
|
38
|
-
declare function ArcDeleteResponse(): _sinclair_typebox0.TObject<{
|
|
39
|
-
success: _sinclair_typebox0.TBoolean;
|
|
40
|
-
message: _sinclair_typebox0.TOptional<_sinclair_typebox0.TString>;
|
|
38
|
+
declare function ArcDeleteResponse(): _$_sinclair_typebox0.TObject<{
|
|
39
|
+
success: _$_sinclair_typebox0.TBoolean;
|
|
40
|
+
message: _$_sinclair_typebox0.TOptional<_$_sinclair_typebox0.TString>;
|
|
41
41
|
}>;
|
|
42
42
|
/**
|
|
43
43
|
* Error response schema
|
|
44
44
|
*/
|
|
45
|
-
declare function ArcErrorResponse(): _sinclair_typebox0.TObject<{
|
|
46
|
-
success: _sinclair_typebox0.TLiteral<false>;
|
|
47
|
-
error: _sinclair_typebox0.TString;
|
|
48
|
-
code: _sinclair_typebox0.TOptional<_sinclair_typebox0.TString>;
|
|
49
|
-
message: _sinclair_typebox0.TOptional<_sinclair_typebox0.TString>;
|
|
45
|
+
declare function ArcErrorResponse(): _$_sinclair_typebox0.TObject<{
|
|
46
|
+
success: _$_sinclair_typebox0.TLiteral<false>;
|
|
47
|
+
error: _$_sinclair_typebox0.TString;
|
|
48
|
+
code: _$_sinclair_typebox0.TOptional<_$_sinclair_typebox0.TString>;
|
|
49
|
+
message: _$_sinclair_typebox0.TOptional<_$_sinclair_typebox0.TString>;
|
|
50
50
|
}>;
|
|
51
51
|
/**
|
|
52
52
|
* Standard pagination + sorting + filtering query parameters.
|
|
53
53
|
* Matches Arc's list endpoint conventions.
|
|
54
54
|
*/
|
|
55
|
-
declare function ArcPaginationQuery(): _sinclair_typebox0.TObject<{
|
|
56
|
-
page: _sinclair_typebox0.TOptional<_sinclair_typebox0.TInteger>;
|
|
57
|
-
limit: _sinclair_typebox0.TOptional<_sinclair_typebox0.TInteger>;
|
|
58
|
-
sort: _sinclair_typebox0.TOptional<_sinclair_typebox0.TString>;
|
|
59
|
-
select: _sinclair_typebox0.TOptional<_sinclair_typebox0.TString>;
|
|
60
|
-
populate: _sinclair_typebox0.TOptional<_sinclair_typebox0.TAny>;
|
|
55
|
+
declare function ArcPaginationQuery(): _$_sinclair_typebox0.TObject<{
|
|
56
|
+
page: _$_sinclair_typebox0.TOptional<_$_sinclair_typebox0.TInteger>;
|
|
57
|
+
limit: _$_sinclair_typebox0.TOptional<_$_sinclair_typebox0.TInteger>;
|
|
58
|
+
sort: _$_sinclair_typebox0.TOptional<_$_sinclair_typebox0.TString>;
|
|
59
|
+
select: _$_sinclair_typebox0.TOptional<_$_sinclair_typebox0.TString>;
|
|
60
|
+
populate: _$_sinclair_typebox0.TOptional<_$_sinclair_typebox0.TAny>;
|
|
61
61
|
}>;
|
|
62
62
|
//#endregion
|
|
63
63
|
export { ArcDeleteResponse, ArcErrorResponse, ArcItemResponse, ArcListResponse, ArcMutationResponse, ArcPaginationQuery, type FastifyPluginAsyncTypebox, type FastifyPluginCallbackTypebox, type Static, type TObject, type TSchema, Type, type TypeBoxTypeProvider, TypeBoxValidatorCompiler };
|
package/dist/schemas/index.mjs
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import { Type, Type as Type$1 } from "@sinclair/typebox";
|
|
2
1
|
import { TypeBoxValidatorCompiler } from "@fastify/type-provider-typebox";
|
|
3
|
-
|
|
2
|
+
import { Type, Type as Type$1 } from "@sinclair/typebox";
|
|
4
3
|
//#region src/schemas/index.ts
|
|
5
4
|
/**
|
|
6
5
|
* Paginated list response — matches Arc's runtime format:
|
|
@@ -77,6 +76,5 @@ function ArcPaginationQuery() {
|
|
|
77
76
|
populate: Type$1.Optional(Type$1.Any())
|
|
78
77
|
}, { additionalProperties: true });
|
|
79
78
|
}
|
|
80
|
-
|
|
81
79
|
//#endregion
|
|
82
|
-
export { ArcDeleteResponse, ArcErrorResponse, ArcItemResponse, ArcListResponse, ArcMutationResponse, ArcPaginationQuery, Type, TypeBoxValidatorCompiler };
|
|
80
|
+
export { ArcDeleteResponse, ArcErrorResponse, ArcItemResponse, ArcListResponse, ArcMutationResponse, ArcPaginationQuery, Type, TypeBoxValidatorCompiler };
|
package/dist/scope/index.d.mts
CHANGED
|
@@ -1,6 +1,17 @@
|
|
|
1
|
-
import { a as AUTHENTICATED_SCOPE, c as getOrgId, d as
|
|
1
|
+
import { a as AUTHENTICATED_SCOPE, c as getOrgId, d as getUserId, f as getUserRoles, g as isMember, h as isElevated, i as elevationPlugin, l as getOrgRoles, m as isAuthenticated, n as ElevationOptions, o as PUBLIC_SCOPE, p as hasOrgAccess, r as _default, s as RequestScope, t as ElevationEvent, u as getTeamId } from "../elevation-Ca_yveIO.mjs";
|
|
2
2
|
import { FastifyReply, FastifyRequest } from "fastify";
|
|
3
3
|
|
|
4
|
+
//#region src/scope/rateLimitKey.d.ts
|
|
5
|
+
interface RateLimitKeyContext {
|
|
6
|
+
ip: string;
|
|
7
|
+
scope?: RequestScope;
|
|
8
|
+
}
|
|
9
|
+
interface TenantKeyGeneratorOptions {
|
|
10
|
+
/** Custom strategy — overrides default scope-based logic */
|
|
11
|
+
strategy?: (ctx: RateLimitKeyContext) => string;
|
|
12
|
+
}
|
|
13
|
+
declare function createTenantKeyGenerator(opts?: TenantKeyGeneratorOptions): (ctx: RateLimitKeyContext) => string;
|
|
14
|
+
//#endregion
|
|
4
15
|
//#region src/scope/resolveOrgFromHeader.d.ts
|
|
5
16
|
interface ResolveOrgFromHeaderOptions {
|
|
6
17
|
/** Header name (default: 'x-organization-id') */
|
|
@@ -18,4 +29,4 @@ interface ResolveOrgFromHeaderOptions {
|
|
|
18
29
|
*/
|
|
19
30
|
declare function resolveOrgFromHeader(options: ResolveOrgFromHeaderOptions): (request: FastifyRequest, reply: FastifyReply) => Promise<void>;
|
|
20
31
|
//#endregion
|
|
21
|
-
export { AUTHENTICATED_SCOPE, type ElevationEvent, type ElevationOptions, PUBLIC_SCOPE, type RequestScope, type ResolveOrgFromHeaderOptions, _default as elevationPlugin, elevationPlugin as elevationPluginFn, getOrgId, getOrgRoles, getTeamId, hasOrgAccess, isAuthenticated, isElevated, isMember, resolveOrgFromHeader };
|
|
32
|
+
export { AUTHENTICATED_SCOPE, type ElevationEvent, type ElevationOptions, PUBLIC_SCOPE, type RateLimitKeyContext, type RequestScope, type ResolveOrgFromHeaderOptions, type TenantKeyGeneratorOptions, createTenantKeyGenerator, _default as elevationPlugin, elevationPlugin as elevationPluginFn, getOrgId, getOrgRoles, getTeamId, getUserId, getUserRoles, hasOrgAccess, isAuthenticated, isElevated, isMember, resolveOrgFromHeader };
|
package/dist/scope/index.mjs
CHANGED
|
@@ -1,6 +1,18 @@
|
|
|
1
|
-
import { a as getTeamId, c as
|
|
2
|
-
import { n as
|
|
3
|
-
|
|
1
|
+
import { a as getTeamId, c as hasOrgAccess, d as isMember, i as getOrgRoles, l as isAuthenticated, n as PUBLIC_SCOPE, o as getUserId, r as getOrgId, s as getUserRoles, t as AUTHENTICATED_SCOPE, u as isElevated } from "../types-C6TQjtdi.mjs";
|
|
2
|
+
import { n as normalizeRoles } from "../types-ZUu_h0jp.mjs";
|
|
3
|
+
import { n as elevation_default, t as elevationPlugin } from "../elevation-BEdACOLB.mjs";
|
|
4
|
+
//#region src/scope/rateLimitKey.ts
|
|
5
|
+
function createTenantKeyGenerator(opts) {
|
|
6
|
+
if (opts?.strategy) return opts.strategy;
|
|
7
|
+
return (ctx) => {
|
|
8
|
+
const scope = ctx.scope;
|
|
9
|
+
if (!scope || scope.kind === "public") return ctx.ip;
|
|
10
|
+
if (scope.kind === "member") return scope.organizationId;
|
|
11
|
+
if (scope.kind === "elevated") return scope.organizationId ?? scope.userId ?? ctx.ip;
|
|
12
|
+
return scope.userId ?? ctx.ip;
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
//#endregion
|
|
4
16
|
//#region src/scope/resolveOrgFromHeader.ts
|
|
5
17
|
/**
|
|
6
18
|
* Create a preHandler hook that resolves org scope from a header.
|
|
@@ -55,11 +67,12 @@ function resolveOrgFromHeader(options) {
|
|
|
55
67
|
}
|
|
56
68
|
request.scope = {
|
|
57
69
|
kind: "member",
|
|
70
|
+
userId: userId || void 0,
|
|
71
|
+
userRoles: normalizeRoles(user.role),
|
|
58
72
|
organizationId: orgId,
|
|
59
73
|
orgRoles: membership.roles
|
|
60
74
|
};
|
|
61
75
|
};
|
|
62
76
|
}
|
|
63
|
-
|
|
64
77
|
//#endregion
|
|
65
|
-
export { AUTHENTICATED_SCOPE, PUBLIC_SCOPE, elevation_default as elevationPlugin, elevationPlugin as elevationPluginFn, getOrgId, getOrgRoles, getTeamId, hasOrgAccess, isAuthenticated, isElevated, isMember, resolveOrgFromHeader };
|
|
78
|
+
export { AUTHENTICATED_SCOPE, PUBLIC_SCOPE, createTenantKeyGenerator, elevation_default as elevationPlugin, elevationPlugin as elevationPluginFn, getOrgId, getOrgRoles, getTeamId, getUserId, getUserRoles, hasOrgAccess, isAuthenticated, isElevated, isMember, resolveOrgFromHeader };
|
|
@@ -41,7 +41,7 @@ interface SessionCookieOptions {
|
|
|
41
41
|
/** Prevent client-side JavaScript access (default: true) */
|
|
42
42
|
httpOnly?: boolean;
|
|
43
43
|
/** SameSite attribute (default: 'lax') */
|
|
44
|
-
sameSite?:
|
|
44
|
+
sameSite?: "strict" | "lax" | "none";
|
|
45
45
|
/** Cookie path (default: '/') */
|
|
46
46
|
path?: string;
|
|
47
47
|
/** Cookie domain */
|
|
@@ -75,7 +75,7 @@ interface SessionManagerResult {
|
|
|
75
75
|
/** PreHandler that rejects requests without a fresh session */
|
|
76
76
|
requireFresh: (request: FastifyRequest, reply: FastifyReply) => Promise<void>;
|
|
77
77
|
}
|
|
78
|
-
declare module
|
|
78
|
+
declare module "fastify" {
|
|
79
79
|
interface FastifyInstance {
|
|
80
80
|
/** Authenticate middleware — validates session and sets request.user */
|
|
81
81
|
authenticate: (request: FastifyRequest, reply: FastifyReply) => Promise<void>;
|
|
@@ -1,28 +1,8 @@
|
|
|
1
|
-
import { t as __exportAll } from "./chunk-
|
|
2
|
-
import { n as PUBLIC_SCOPE, r as getOrgId } from "./types-
|
|
3
|
-
import { t as arcLog } from "./logger-
|
|
1
|
+
import { t as __exportAll } from "./chunk-BpYLSNr0.mjs";
|
|
2
|
+
import { n as PUBLIC_SCOPE, r as getOrgId } from "./types-C6TQjtdi.mjs";
|
|
3
|
+
import { t as arcLog } from "./logger-Dz3j1ItV.mjs";
|
|
4
4
|
import fp from "fastify-plugin";
|
|
5
|
-
|
|
6
5
|
//#region src/plugins/sse.ts
|
|
7
|
-
/**
|
|
8
|
-
* SSE Plugin (Server-Sent Events)
|
|
9
|
-
*
|
|
10
|
-
* Streams domain events to clients over HTTP using Server-Sent Events.
|
|
11
|
-
* Requires the events plugin (`arc-events`) to be registered first.
|
|
12
|
-
*
|
|
13
|
-
* @example
|
|
14
|
-
* import { ssePlugin } from '@classytic/arc/plugins';
|
|
15
|
-
*
|
|
16
|
-
* // Basic — stream all events at /events/stream
|
|
17
|
-
* await fastify.register(ssePlugin);
|
|
18
|
-
*
|
|
19
|
-
* // Filtered + org-scoped
|
|
20
|
-
* await fastify.register(ssePlugin, {
|
|
21
|
-
* path: '/api/events',
|
|
22
|
-
* patterns: ['order.*', 'product.*'],
|
|
23
|
-
* orgScoped: true,
|
|
24
|
-
* });
|
|
25
|
-
*/
|
|
26
6
|
var sse_exports = /* @__PURE__ */ __exportAll({
|
|
27
7
|
default: () => sse_default,
|
|
28
8
|
ssePlugin: () => ssePlugin
|
|
@@ -118,6 +98,5 @@ var sse_default = fp(ssePlugin, {
|
|
|
118
98
|
fastify: "5.x",
|
|
119
99
|
dependencies: ["arc-events"]
|
|
120
100
|
});
|
|
121
|
-
|
|
122
101
|
//#endregion
|
|
123
|
-
export { sse_default as n, sse_exports as r, ssePlugin as t };
|
|
102
|
+
export { sse_default as n, sse_exports as r, ssePlugin as t };
|