@classytic/arc 2.2.5 → 2.4.1
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-BKHSl2nT.mjs → createApp-ByWNRsZj.mjs} +65 -36
- package/dist/{defineResource-DO9ONe_D.mjs → defineResource-D9aY5Cy6.mjs} +154 -1165
- 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-CyAA2zlB.d.mts → index-BL8CaQih.d.mts} +56 -57
- package/dist/index-Diqcm14c.d.mts +369 -0
- package/dist/{prisma-xjhMEq_S.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.mjs +3 -7
- 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-B6ZN9Ing.mjs +489 -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 +4 -6
- 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-DMSBMkaZ.d.mts → types-Dt0-AI6E.d.mts} +85 -27
- package/dist/{types-DelU6kln.mjs → types-ZUu_h0jp.mjs} +1 -2
- package/dist/utils/index.d.mts +255 -352
- package/dist/utils/index.mjs +7 -6
- package/dist/utils-Dc0WhlIl.mjs +594 -0
- package/dist/versioning-BzfeHmhj.mjs +37 -0
- package/package.json +46 -12
- package/skills/arc/SKILL.md +506 -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 +386 -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-DYhWBW_D.mjs +0 -1096
- package/dist/errorHandler-CW3OOeYq.d.mts +0 -72
- package/dist/interface-DZYNK9bb.d.mts +0 -1112
- 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
|
@@ -29,14 +29,13 @@ function hashParams(params) {
|
|
|
29
29
|
function stableStringify(value) {
|
|
30
30
|
if (value === null || value === void 0) return "";
|
|
31
31
|
if (typeof value !== "object") return String(value);
|
|
32
|
-
if (Array.isArray(value)) return
|
|
33
|
-
return "{" + Object.keys(value).sort().map((k) => k
|
|
32
|
+
if (Array.isArray(value)) return `[${value.map(stableStringify).join(",")}]`;
|
|
33
|
+
return "{" + Object.keys(value).sort().map((k) => `${k}:${stableStringify(value[k])}`).join(",") + "}";
|
|
34
34
|
}
|
|
35
35
|
function djb2(str) {
|
|
36
36
|
let hash = 5381;
|
|
37
37
|
for (let i = 0; i < str.length; i++) hash = (hash << 5) + hash + str.charCodeAt(i) >>> 0;
|
|
38
38
|
return hash;
|
|
39
39
|
}
|
|
40
|
-
|
|
41
40
|
//#endregion
|
|
42
|
-
export { versionKey as i, hashParams as n, tagVersionKey as r, buildQueryKey as t };
|
|
41
|
+
export { versionKey as i, hashParams as n, tagVersionKey as r, buildQueryKey as t };
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import { t as __exportAll } from "./chunk-
|
|
2
|
-
|
|
1
|
+
import { t as __exportAll } from "./chunk-BpYLSNr0.mjs";
|
|
3
2
|
//#region src/logger/index.ts
|
|
4
3
|
var logger_exports = /* @__PURE__ */ __exportAll({
|
|
5
4
|
arcLog: () => arcLog,
|
|
@@ -73,6 +72,5 @@ function isSuppressed() {
|
|
|
73
72
|
const env = typeof process !== "undefined" ? process.env?.ARC_SUPPRESS_WARNINGS : void 0;
|
|
74
73
|
return env === "1" || env === "true";
|
|
75
74
|
}
|
|
76
|
-
|
|
77
75
|
//#endregion
|
|
78
|
-
export { configureArcLogger as n, logger_exports as r, arcLog as t };
|
|
76
|
+
export { configureArcLogger as n, logger_exports as r, arcLog as t };
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import { t as __exportAll } from "./chunk-
|
|
2
|
-
|
|
1
|
+
import { t as __exportAll } from "./chunk-BpYLSNr0.mjs";
|
|
3
2
|
//#region src/cache/memory.ts
|
|
4
3
|
var memory_exports = /* @__PURE__ */ __exportAll({
|
|
5
4
|
MemoryCacheStore: () => MemoryCacheStore,
|
|
@@ -138,6 +137,5 @@ var MemoryCacheStore = class {
|
|
|
138
137
|
function clamp(value, min, max) {
|
|
139
138
|
return Math.min(max, Math.max(min, value));
|
|
140
139
|
}
|
|
141
|
-
|
|
142
140
|
//#endregion
|
|
143
|
-
export { memory_exports as n, MemoryCacheStore as t };
|
|
141
|
+
export { memory_exports as n, MemoryCacheStore as t };
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
import { t as __exportAll } from "./chunk-BpYLSNr0.mjs";
|
|
2
|
+
import fp from "fastify-plugin";
|
|
3
|
+
//#region src/plugins/metrics.ts
|
|
4
|
+
var metrics_exports = /* @__PURE__ */ __exportAll({
|
|
5
|
+
default: () => metrics_default,
|
|
6
|
+
metricsPlugin: () => metricsPlugin
|
|
7
|
+
});
|
|
8
|
+
var Counter = class {
|
|
9
|
+
data = /* @__PURE__ */ new Map();
|
|
10
|
+
inc(labels, value = 1) {
|
|
11
|
+
const key = labelKey(labels);
|
|
12
|
+
const existing = this.data.get(key);
|
|
13
|
+
if (existing) existing.value += value;
|
|
14
|
+
else this.data.set(key, {
|
|
15
|
+
labels: { ...labels },
|
|
16
|
+
value
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
values() {
|
|
20
|
+
return [...this.data.values()];
|
|
21
|
+
}
|
|
22
|
+
reset() {
|
|
23
|
+
this.data.clear();
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
var Histogram = class {
|
|
27
|
+
data = /* @__PURE__ */ new Map();
|
|
28
|
+
observe(labels, value) {
|
|
29
|
+
const key = labelKey(labels);
|
|
30
|
+
const existing = this.data.get(key);
|
|
31
|
+
if (existing) {
|
|
32
|
+
existing.sum += value;
|
|
33
|
+
existing.count += 1;
|
|
34
|
+
} else this.data.set(key, {
|
|
35
|
+
labels: { ...labels },
|
|
36
|
+
sum: value,
|
|
37
|
+
count: 1
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
values() {
|
|
41
|
+
return [...this.data.values()].flatMap(({ labels, sum, count }) => [{
|
|
42
|
+
labels: {
|
|
43
|
+
...labels,
|
|
44
|
+
le: "sum"
|
|
45
|
+
},
|
|
46
|
+
value: sum
|
|
47
|
+
}, {
|
|
48
|
+
labels: {
|
|
49
|
+
...labels,
|
|
50
|
+
le: "count"
|
|
51
|
+
},
|
|
52
|
+
value: count
|
|
53
|
+
}]);
|
|
54
|
+
}
|
|
55
|
+
reset() {
|
|
56
|
+
this.data.clear();
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
var Gauge = class {
|
|
60
|
+
data = /* @__PURE__ */ new Map();
|
|
61
|
+
set(labels, value) {
|
|
62
|
+
const key = labelKey(labels);
|
|
63
|
+
this.data.set(key, {
|
|
64
|
+
labels: { ...labels },
|
|
65
|
+
value
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
values() {
|
|
69
|
+
return [...this.data.values()];
|
|
70
|
+
}
|
|
71
|
+
reset() {
|
|
72
|
+
this.data.clear();
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
function labelKey(labels) {
|
|
76
|
+
return Object.entries(labels).sort(([a], [b]) => a.localeCompare(b)).map(([k, v]) => `${k}=${v}`).join(",");
|
|
77
|
+
}
|
|
78
|
+
function toPrometheusText(metrics) {
|
|
79
|
+
const lines = [];
|
|
80
|
+
for (const m of metrics) {
|
|
81
|
+
lines.push(`# HELP ${m.name} ${m.help}`);
|
|
82
|
+
lines.push(`# TYPE ${m.name} ${m.type}`);
|
|
83
|
+
for (const v of m.values) {
|
|
84
|
+
const labelStr = Object.entries(v.labels).map(([k, val]) => `${k}="${val}"`).join(",");
|
|
85
|
+
lines.push(`${m.name}{${labelStr}} ${v.value}`);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return `${lines.join("\n")}\n`;
|
|
89
|
+
}
|
|
90
|
+
const metricsPlugin = async (fastify, opts = {}) => {
|
|
91
|
+
const path = opts.path ?? "/_metrics";
|
|
92
|
+
const prefix = opts.prefix ?? "arc";
|
|
93
|
+
const httpRequestsTotal = new Counter();
|
|
94
|
+
const crudOpsTotal = new Counter();
|
|
95
|
+
const cacheHitsTotal = new Counter();
|
|
96
|
+
const cacheMissesTotal = new Counter();
|
|
97
|
+
const eventsPublishedTotal = new Counter();
|
|
98
|
+
const eventsConsumedTotal = new Counter();
|
|
99
|
+
const httpDuration = new Histogram();
|
|
100
|
+
const crudDuration = new Histogram();
|
|
101
|
+
const circuitBreakerState = new Gauge();
|
|
102
|
+
const collector = {
|
|
103
|
+
collect() {
|
|
104
|
+
const metrics = [
|
|
105
|
+
{
|
|
106
|
+
name: `${prefix}_http_requests_total`,
|
|
107
|
+
type: "counter",
|
|
108
|
+
help: "Total HTTP requests",
|
|
109
|
+
values: httpRequestsTotal.values()
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
name: `${prefix}_http_request_duration_seconds`,
|
|
113
|
+
type: "histogram",
|
|
114
|
+
help: "HTTP request duration in seconds",
|
|
115
|
+
values: httpDuration.values()
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
name: `${prefix}_crud_operations_total`,
|
|
119
|
+
type: "counter",
|
|
120
|
+
help: "Total CRUD operations by resource",
|
|
121
|
+
values: crudOpsTotal.values()
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
name: `${prefix}_crud_operation_duration_seconds`,
|
|
125
|
+
type: "histogram",
|
|
126
|
+
help: "CRUD operation duration in seconds",
|
|
127
|
+
values: crudDuration.values()
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
name: `${prefix}_cache_hits_total`,
|
|
131
|
+
type: "counter",
|
|
132
|
+
help: "Total cache hits by resource",
|
|
133
|
+
values: cacheHitsTotal.values()
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
name: `${prefix}_cache_misses_total`,
|
|
137
|
+
type: "counter",
|
|
138
|
+
help: "Total cache misses by resource",
|
|
139
|
+
values: cacheMissesTotal.values()
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
name: `${prefix}_events_published_total`,
|
|
143
|
+
type: "counter",
|
|
144
|
+
help: "Total events published",
|
|
145
|
+
values: eventsPublishedTotal.values()
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
name: `${prefix}_events_consumed_total`,
|
|
149
|
+
type: "counter",
|
|
150
|
+
help: "Total events consumed",
|
|
151
|
+
values: eventsConsumedTotal.values()
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
name: `${prefix}_circuit_breaker_state`,
|
|
155
|
+
type: "gauge",
|
|
156
|
+
help: "Circuit breaker state (0=closed, 1=open, 2=half-open)",
|
|
157
|
+
values: circuitBreakerState.values()
|
|
158
|
+
}
|
|
159
|
+
];
|
|
160
|
+
opts.onCollect?.(metrics);
|
|
161
|
+
return metrics;
|
|
162
|
+
},
|
|
163
|
+
reset() {
|
|
164
|
+
httpRequestsTotal.reset();
|
|
165
|
+
httpDuration.reset();
|
|
166
|
+
crudOpsTotal.reset();
|
|
167
|
+
crudDuration.reset();
|
|
168
|
+
cacheHitsTotal.reset();
|
|
169
|
+
cacheMissesTotal.reset();
|
|
170
|
+
eventsPublishedTotal.reset();
|
|
171
|
+
eventsConsumedTotal.reset();
|
|
172
|
+
circuitBreakerState.reset();
|
|
173
|
+
},
|
|
174
|
+
recordOperation(resource, operation, status, durationMs) {
|
|
175
|
+
crudOpsTotal.inc({
|
|
176
|
+
resource,
|
|
177
|
+
operation,
|
|
178
|
+
status: String(status)
|
|
179
|
+
});
|
|
180
|
+
crudDuration.observe({
|
|
181
|
+
resource,
|
|
182
|
+
operation
|
|
183
|
+
}, durationMs / 1e3);
|
|
184
|
+
},
|
|
185
|
+
recordCacheHit(resource) {
|
|
186
|
+
cacheHitsTotal.inc({ resource });
|
|
187
|
+
},
|
|
188
|
+
recordCacheMiss(resource) {
|
|
189
|
+
cacheMissesTotal.inc({ resource });
|
|
190
|
+
},
|
|
191
|
+
recordEventPublish(eventType) {
|
|
192
|
+
eventsPublishedTotal.inc({ event_type: eventType });
|
|
193
|
+
},
|
|
194
|
+
recordEventConsume(eventType) {
|
|
195
|
+
eventsConsumedTotal.inc({ event_type: eventType });
|
|
196
|
+
},
|
|
197
|
+
recordCircuitBreakerState(service, state) {
|
|
198
|
+
const stateValue = state === "open" ? 1 : state === "half-open" ? 2 : 0;
|
|
199
|
+
circuitBreakerState.set({ service }, stateValue);
|
|
200
|
+
}
|
|
201
|
+
};
|
|
202
|
+
fastify.decorate("metrics", collector);
|
|
203
|
+
fastify.addHook("onResponse", async (request, reply) => {
|
|
204
|
+
if (request.url === path) return;
|
|
205
|
+
const duration = reply.elapsedTime / 1e3;
|
|
206
|
+
const labels = {
|
|
207
|
+
method: request.method,
|
|
208
|
+
route: request.routeOptions?.url ?? request.url,
|
|
209
|
+
status: String(reply.statusCode)
|
|
210
|
+
};
|
|
211
|
+
httpRequestsTotal.inc(labels);
|
|
212
|
+
httpDuration.observe(labels, duration);
|
|
213
|
+
});
|
|
214
|
+
fastify.get(path, async (_request, reply) => {
|
|
215
|
+
const text = toPrometheusText(collector.collect());
|
|
216
|
+
return reply.type("text/plain; charset=utf-8").send(text);
|
|
217
|
+
});
|
|
218
|
+
};
|
|
219
|
+
var metrics_default = fp(metricsPlugin, {
|
|
220
|
+
name: "arc-metrics",
|
|
221
|
+
fastify: "5.x"
|
|
222
|
+
});
|
|
223
|
+
//#endregion
|
|
224
|
+
export { metrics_default as n, metrics_exports as r, metricsPlugin as t };
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import mongoose from "mongoose";
|
|
2
|
-
|
|
3
1
|
//#region src/migrations/index.ts
|
|
4
2
|
/**
|
|
5
3
|
* Define a migration
|
|
@@ -95,8 +93,7 @@ var MigrationRunner = class {
|
|
|
95
93
|
*/
|
|
96
94
|
async runMigration(migration, direction, isRollback = false) {
|
|
97
95
|
const start = Date.now();
|
|
98
|
-
|
|
99
|
-
console.log(`${action} ${migration.resource} v${migration.version}${migration.description ? `: ${migration.description}` : ""}...`);
|
|
96
|
+
console.log(`${direction === "up" ? "Applying" : "Rolling back"} ${migration.resource} v${migration.version}${migration.description ? `: ${migration.description}` : ""}...`);
|
|
100
97
|
try {
|
|
101
98
|
if (direction === "up") {
|
|
102
99
|
await migration.up(this.db);
|
|
@@ -238,7 +235,7 @@ const migrationHelpers = {
|
|
|
238
235
|
up: async (db) => {
|
|
239
236
|
await db.collection(collection).updateMany({}, { $unset: { [fieldName]: "" } });
|
|
240
237
|
},
|
|
241
|
-
down: async (
|
|
238
|
+
down: async (_db) => {
|
|
242
239
|
console.warn(`Cannot restore ${fieldName} field - data was deleted`);
|
|
243
240
|
}
|
|
244
241
|
}),
|
|
@@ -255,6 +252,5 @@ const migrationHelpers = {
|
|
|
255
252
|
}
|
|
256
253
|
})
|
|
257
254
|
};
|
|
258
|
-
|
|
259
255
|
//#endregion
|
|
260
|
-
export { MigrationRegistry, MigrationRunner, defineMigration, migrationHelpers, migrationRegistry, withSchemaVersion };
|
|
256
|
+
export { MigrationRegistry, MigrationRunner, defineMigration, migrationHelpers, migrationRegistry, withSchemaVersion };
|
|
@@ -4,9 +4,7 @@ var MongoAuditStore = class {
|
|
|
4
4
|
collection;
|
|
5
5
|
initialized = false;
|
|
6
6
|
ttlDays;
|
|
7
|
-
options;
|
|
8
7
|
constructor(options) {
|
|
9
|
-
this.options = options;
|
|
10
8
|
const collectionName = options.collection ?? "audit_logs";
|
|
11
9
|
this.collection = options.connection.collection(collectionName);
|
|
12
10
|
this.ttlDays = options.ttlDays ?? 90;
|
|
@@ -88,6 +86,5 @@ var MongoAuditStore = class {
|
|
|
88
86
|
}));
|
|
89
87
|
}
|
|
90
88
|
};
|
|
91
|
-
|
|
92
89
|
//#endregion
|
|
93
|
-
export { MongoAuditStore as t };
|
|
90
|
+
export { MongoAuditStore as t };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { i as UserBase } from "./types-
|
|
1
|
+
import { i as UserBase } from "./types-BNUccdcf.mjs";
|
|
2
2
|
|
|
3
3
|
//#region src/audit/stores/interface.d.ts
|
|
4
|
-
type AuditAction =
|
|
4
|
+
type AuditAction = "create" | "update" | "delete" | "restore" | "custom";
|
|
5
5
|
interface AuditEntry {
|
|
6
6
|
/** Unique audit log ID */
|
|
7
7
|
id: string;
|
|
@@ -108,7 +108,6 @@ declare class MongoAuditStore implements AuditStore {
|
|
|
108
108
|
private collection;
|
|
109
109
|
private initialized;
|
|
110
110
|
private ttlDays;
|
|
111
|
-
private options;
|
|
112
111
|
constructor(options: MongoAuditStoreOptions);
|
|
113
112
|
private ensureIndexes;
|
|
114
113
|
log(entry: AuditEntry): Promise<void>;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { n as IdempotencyResult, r as IdempotencyStore } from "./interface-
|
|
1
|
+
import { n as IdempotencyResult, r as IdempotencyStore } from "./interface-B4awm1RJ.mjs";
|
|
2
2
|
|
|
3
3
|
//#region src/idempotency/stores/mongodb.d.ts
|
|
4
4
|
interface MongoConnection {
|
|
@@ -58,7 +58,7 @@ declare class MongoIdempotencyStore implements IdempotencyStore {
|
|
|
58
58
|
private get collection();
|
|
59
59
|
private ensureIndex;
|
|
60
60
|
get(key: string): Promise<IdempotencyResult | undefined>;
|
|
61
|
-
set(key: string, result: Omit<IdempotencyResult,
|
|
61
|
+
set(key: string, result: Omit<IdempotencyResult, "key">): Promise<void>;
|
|
62
62
|
tryLock(key: string, requestId: string, ttlMs: number): Promise<boolean>;
|
|
63
63
|
unlock(key: string, requestId: string): Promise<void>;
|
|
64
64
|
isLocked(key: string): Promise<boolean>;
|
|
@@ -1,23 +1,7 @@
|
|
|
1
|
-
import { t as getUserRoles } from "./types-
|
|
2
|
-
import { n as convertRouteSchema } from "./schemaConverter-
|
|
1
|
+
import { t as getUserRoles } from "./types-ZUu_h0jp.mjs";
|
|
2
|
+
import { n as convertRouteSchema } from "./schemaConverter-DjzHpFam.mjs";
|
|
3
3
|
import fp from "fastify-plugin";
|
|
4
|
-
|
|
5
4
|
//#region src/docs/openapi.ts
|
|
6
|
-
/**
|
|
7
|
-
* OpenAPI Spec Generator
|
|
8
|
-
*
|
|
9
|
-
* Auto-generates OpenAPI 3.0 specification from Arc resource registry.
|
|
10
|
-
*
|
|
11
|
-
* @example
|
|
12
|
-
* import { openApiPlugin } from '@classytic/arc/docs';
|
|
13
|
-
*
|
|
14
|
-
* await fastify.register(openApiPlugin, {
|
|
15
|
-
* title: 'My API',
|
|
16
|
-
* version: '1.0.0',
|
|
17
|
-
* });
|
|
18
|
-
*
|
|
19
|
-
* // Spec available at /_docs/openapi.json
|
|
20
|
-
*/
|
|
21
5
|
const openApiPlugin = async (fastify, opts = {}) => {
|
|
22
6
|
const { title = "Arc API", version = "1.0.0", description, serverUrl, prefix = "/_docs", apiPrefix = "", authRoles = [] } = opts;
|
|
23
7
|
const buildSpec = () => {
|
|
@@ -481,7 +465,7 @@ function generateSchemas(resources) {
|
|
|
481
465
|
if (fieldPerms && schemas[resource.name]?.properties) {
|
|
482
466
|
const props = schemas[resource.name].properties;
|
|
483
467
|
for (const [field, perm] of Object.entries(fieldPerms)) if (props[field]) {
|
|
484
|
-
const desc = props[field]
|
|
468
|
+
const desc = props[field]?.description ?? "";
|
|
485
469
|
const permDesc = formatFieldPermDescription(perm);
|
|
486
470
|
props[field].description = desc ? `${desc} (${permDesc})` : permDesc;
|
|
487
471
|
} else if (perm.type === "hidden") {}
|
|
@@ -520,6 +504,5 @@ var openapi_default = fp(openApiPlugin, {
|
|
|
520
504
|
name: "arc-openapi",
|
|
521
505
|
fastify: "5.x"
|
|
522
506
|
});
|
|
523
|
-
|
|
524
507
|
//#endregion
|
|
525
|
-
export { openApiPlugin as n, openapi_default as r, buildOpenApiSpec as t };
|
|
508
|
+
export { openApiPlugin as n, openapi_default as r, buildOpenApiSpec as t };
|
package/dist/org/index.d.mts
CHANGED
|
@@ -1,10 +1,18 @@
|
|
|
1
|
-
import "../
|
|
2
|
-
import {
|
|
3
|
-
import { i as UserBase } from "../types-RLkFVgaw.mjs";
|
|
4
|
-
import "../types/index.mjs";
|
|
1
|
+
import { Pt as RouteHandler } from "../interface-DGmPxakH.mjs";
|
|
2
|
+
import { i as UserBase } from "../types-BNUccdcf.mjs";
|
|
5
3
|
import { InvitationAdapter, InvitationDoc, MemberDoc, OrgAdapter, OrgDoc, OrgPermissionStatement, OrgRole, OrganizationPluginOptions } from "./types.mjs";
|
|
6
4
|
import { FastifyPluginAsync, RouteHandlerMethod } from "fastify";
|
|
7
5
|
|
|
6
|
+
//#region src/org/organizationPlugin.d.ts
|
|
7
|
+
declare module "fastify" {
|
|
8
|
+
interface FastifyInstance {
|
|
9
|
+
/** Middleware: require the caller to hold one of the listed org roles */
|
|
10
|
+
requireOrgRole: (roles: string[]) => RouteHandlerMethod;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
declare const organizationPlugin: FastifyPluginAsync<OrganizationPluginOptions>;
|
|
14
|
+
declare const _default: FastifyPluginAsync<OrganizationPluginOptions>;
|
|
15
|
+
//#endregion
|
|
8
16
|
//#region src/org/orgGuard.d.ts
|
|
9
17
|
interface OrgGuardOptions {
|
|
10
18
|
/** Require organization context (default: true) */
|
|
@@ -55,14 +63,4 @@ declare function getUserOrgRoles(user: UserBase | undefined | null, orgId: strin
|
|
|
55
63
|
*/
|
|
56
64
|
declare function hasOrgRole(user: UserBase | undefined | null, orgId: string | undefined | null, roles: string | string[], options?: OrgRolesOptions): boolean;
|
|
57
65
|
//#endregion
|
|
58
|
-
//#region src/org/organizationPlugin.d.ts
|
|
59
|
-
declare module 'fastify' {
|
|
60
|
-
interface FastifyInstance {
|
|
61
|
-
/** Middleware: require the caller to hold one of the listed org roles */
|
|
62
|
-
requireOrgRole: (roles: string[]) => RouteHandlerMethod;
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
declare const organizationPlugin: FastifyPluginAsync<OrganizationPluginOptions>;
|
|
66
|
-
declare const _default: FastifyPluginAsync<OrganizationPluginOptions>;
|
|
67
|
-
//#endregion
|
|
68
66
|
export { type InvitationAdapter, type InvitationDoc, type MemberDoc, type OrgAdapter, type OrgDoc, type OrgGuardOptions, type OrgMembershipOptions, type OrgPermissionStatement, type OrgRole, type OrgRolesOptions, type OrganizationPluginOptions, getUserOrgRoles, hasOrgRole, orgGuard, orgMembershipCheck, _default as organizationPlugin, organizationPlugin as organizationPluginFn, requireOrg, requireOrgRole };
|
package/dist/org/index.mjs
CHANGED
|
@@ -1,122 +1,6 @@
|
|
|
1
|
-
import { c as
|
|
1
|
+
import { c as hasOrgAccess, d as isMember, i as getOrgRoles, n as PUBLIC_SCOPE, u as isElevated } from "../types-C6TQjtdi.mjs";
|
|
2
2
|
import fp from "fastify-plugin";
|
|
3
|
-
|
|
4
|
-
//#region src/org/orgGuard.ts
|
|
5
|
-
/**
|
|
6
|
-
* Create org guard middleware.
|
|
7
|
-
* Reads `request.scope` for org context and roles.
|
|
8
|
-
* Elevated scope always passes.
|
|
9
|
-
*/
|
|
10
|
-
function orgGuard(options = {}) {
|
|
11
|
-
const { requireOrgContext = true, roles = [] } = options;
|
|
12
|
-
return async function orgGuardMiddleware(request, reply) {
|
|
13
|
-
const scope = request.scope ?? PUBLIC_SCOPE;
|
|
14
|
-
if (isElevated(scope)) return;
|
|
15
|
-
if (requireOrgContext && !hasOrgAccess(scope)) {
|
|
16
|
-
reply.code(403).send({
|
|
17
|
-
success: false,
|
|
18
|
-
error: "Organization context required",
|
|
19
|
-
code: "ORG_CONTEXT_REQUIRED",
|
|
20
|
-
message: "This endpoint requires an organization context. Please specify organization via x-organization-id header."
|
|
21
|
-
});
|
|
22
|
-
return;
|
|
23
|
-
}
|
|
24
|
-
if (roles.length > 0 && isMember(scope)) {
|
|
25
|
-
const userOrgRoles = getOrgRoles(scope);
|
|
26
|
-
if (!roles.some((role) => userOrgRoles.includes(role))) {
|
|
27
|
-
reply.code(403).send({
|
|
28
|
-
success: false,
|
|
29
|
-
error: "Insufficient organization permissions",
|
|
30
|
-
code: "ORG_ROLE_REQUIRED",
|
|
31
|
-
message: `This action requires one of these organization roles: ${roles.join(", ")}`,
|
|
32
|
-
required: roles,
|
|
33
|
-
current: userOrgRoles
|
|
34
|
-
});
|
|
35
|
-
return;
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
};
|
|
39
|
-
}
|
|
40
|
-
/**
|
|
41
|
-
* Shorthand for requiring org context
|
|
42
|
-
*/
|
|
43
|
-
function requireOrg() {
|
|
44
|
-
return orgGuard({ requireOrgContext: true });
|
|
45
|
-
}
|
|
46
|
-
/**
|
|
47
|
-
* Require org context with specific roles
|
|
48
|
-
*/
|
|
49
|
-
function requireOrgRole(...roles) {
|
|
50
|
-
return orgGuard({
|
|
51
|
-
requireOrgContext: true,
|
|
52
|
-
roles
|
|
53
|
-
});
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
//#endregion
|
|
57
|
-
//#region src/org/orgMembership.ts
|
|
58
|
-
/**
|
|
59
|
-
* Check if user is member of organization.
|
|
60
|
-
* This is a low-level utility for checking membership from user object data.
|
|
61
|
-
* For request-level checks, use `request.scope` (isMember/isElevated guards).
|
|
62
|
-
*/
|
|
63
|
-
async function orgMembershipCheck(user, orgId, options = {}) {
|
|
64
|
-
const { userOrgsPath = "organizations", validateFromDb } = options;
|
|
65
|
-
if (!user || !orgId) return false;
|
|
66
|
-
if ((user[userOrgsPath] ?? []).some((o) => {
|
|
67
|
-
return (o.organizationId?.toString() ?? String(o)) === orgId.toString();
|
|
68
|
-
})) return true;
|
|
69
|
-
if (validateFromDb) {
|
|
70
|
-
const userId = (user._id ?? user.id)?.toString();
|
|
71
|
-
if (userId) return validateFromDb(userId, orgId);
|
|
72
|
-
}
|
|
73
|
-
return false;
|
|
74
|
-
}
|
|
75
|
-
/**
|
|
76
|
-
* Get user's role in organization from user object data.
|
|
77
|
-
* For request-level role checks, use `request.scope.orgRoles` (when scope is 'member').
|
|
78
|
-
*/
|
|
79
|
-
function getUserOrgRoles(user, orgId, options = {}) {
|
|
80
|
-
const { userOrgsPath = "organizations" } = options;
|
|
81
|
-
if (!user || !orgId) return [];
|
|
82
|
-
return (user[userOrgsPath] ?? []).find((o) => {
|
|
83
|
-
return (o.organizationId?.toString() ?? String(o)) === orgId.toString();
|
|
84
|
-
})?.roles ?? [];
|
|
85
|
-
}
|
|
86
|
-
/**
|
|
87
|
-
* Check if user has specific role in organization from user object data.
|
|
88
|
-
* For request-level role checks, use `requireOrgRole()` permission or `request.scope`.
|
|
89
|
-
*/
|
|
90
|
-
function hasOrgRole(user, orgId, roles, options = {}) {
|
|
91
|
-
const userOrgRoles = getUserOrgRoles(user, orgId, options);
|
|
92
|
-
return (Array.isArray(roles) ? roles : [roles]).some((role) => userOrgRoles.includes(role));
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
//#endregion
|
|
96
3
|
//#region src/org/organizationPlugin.ts
|
|
97
|
-
/**
|
|
98
|
-
* Organization Plugin -- Full org management with REST endpoints
|
|
99
|
-
*
|
|
100
|
-
* Creates these routes:
|
|
101
|
-
* - POST /api/organizations -- Create org
|
|
102
|
-
* - GET /api/organizations -- List user's orgs
|
|
103
|
-
* - GET /api/organizations/:orgId -- Get org
|
|
104
|
-
* - PATCH /api/organizations/:orgId -- Update org
|
|
105
|
-
* - DELETE /api/organizations/:orgId -- Delete org
|
|
106
|
-
* - GET /api/organizations/:orgId/members -- List members
|
|
107
|
-
* - POST /api/organizations/:orgId/members -- Add member
|
|
108
|
-
* - PATCH /api/organizations/:orgId/members/:userId -- Update role
|
|
109
|
-
* - DELETE /api/organizations/:orgId/members/:userId -- Remove member
|
|
110
|
-
*
|
|
111
|
-
* @example
|
|
112
|
-
* import { organizationPlugin } from '@classytic/arc/org';
|
|
113
|
-
*
|
|
114
|
-
* await fastify.register(organizationPlugin, {
|
|
115
|
-
* adapter: myMongooseOrgAdapter,
|
|
116
|
-
* basePath: '/api/organizations',
|
|
117
|
-
* enableInvitations: false,
|
|
118
|
-
* });
|
|
119
|
-
*/
|
|
120
4
|
const DEFAULT_ROLES = [
|
|
121
5
|
{
|
|
122
6
|
name: "owner",
|
|
@@ -508,6 +392,95 @@ var organizationPlugin_default = fp(organizationPlugin, {
|
|
|
508
392
|
name: "arc-organization",
|
|
509
393
|
fastify: "5.x"
|
|
510
394
|
});
|
|
511
|
-
|
|
512
395
|
//#endregion
|
|
513
|
-
|
|
396
|
+
//#region src/org/orgGuard.ts
|
|
397
|
+
/**
|
|
398
|
+
* Create org guard middleware.
|
|
399
|
+
* Reads `request.scope` for org context and roles.
|
|
400
|
+
* Elevated scope always passes.
|
|
401
|
+
*/
|
|
402
|
+
function orgGuard(options = {}) {
|
|
403
|
+
const { requireOrgContext = true, roles = [] } = options;
|
|
404
|
+
return async function orgGuardMiddleware(request, reply) {
|
|
405
|
+
const scope = request.scope ?? PUBLIC_SCOPE;
|
|
406
|
+
if (isElevated(scope)) return;
|
|
407
|
+
if (requireOrgContext && !hasOrgAccess(scope)) {
|
|
408
|
+
reply.code(403).send({
|
|
409
|
+
success: false,
|
|
410
|
+
error: "Organization context required",
|
|
411
|
+
code: "ORG_CONTEXT_REQUIRED",
|
|
412
|
+
message: "This endpoint requires an organization context. Please specify organization via x-organization-id header."
|
|
413
|
+
});
|
|
414
|
+
return;
|
|
415
|
+
}
|
|
416
|
+
if (roles.length > 0 && isMember(scope)) {
|
|
417
|
+
const userOrgRoles = getOrgRoles(scope);
|
|
418
|
+
if (!roles.some((role) => userOrgRoles.includes(role))) {
|
|
419
|
+
reply.code(403).send({
|
|
420
|
+
success: false,
|
|
421
|
+
error: "Insufficient organization permissions",
|
|
422
|
+
code: "ORG_ROLE_REQUIRED",
|
|
423
|
+
message: `This action requires one of these organization roles: ${roles.join(", ")}`,
|
|
424
|
+
required: roles,
|
|
425
|
+
current: userOrgRoles
|
|
426
|
+
});
|
|
427
|
+
return;
|
|
428
|
+
}
|
|
429
|
+
}
|
|
430
|
+
};
|
|
431
|
+
}
|
|
432
|
+
/**
|
|
433
|
+
* Shorthand for requiring org context
|
|
434
|
+
*/
|
|
435
|
+
function requireOrg() {
|
|
436
|
+
return orgGuard({ requireOrgContext: true });
|
|
437
|
+
}
|
|
438
|
+
/**
|
|
439
|
+
* Require org context with specific roles
|
|
440
|
+
*/
|
|
441
|
+
function requireOrgRole(...roles) {
|
|
442
|
+
return orgGuard({
|
|
443
|
+
requireOrgContext: true,
|
|
444
|
+
roles
|
|
445
|
+
});
|
|
446
|
+
}
|
|
447
|
+
//#endregion
|
|
448
|
+
//#region src/org/orgMembership.ts
|
|
449
|
+
/**
|
|
450
|
+
* Check if user is member of organization.
|
|
451
|
+
* This is a low-level utility for checking membership from user object data.
|
|
452
|
+
* For request-level checks, use `request.scope` (isMember/isElevated guards).
|
|
453
|
+
*/
|
|
454
|
+
async function orgMembershipCheck(user, orgId, options = {}) {
|
|
455
|
+
const { userOrgsPath = "organizations", validateFromDb } = options;
|
|
456
|
+
if (!user || !orgId) return false;
|
|
457
|
+
if ((user[userOrgsPath] ?? []).some((o) => {
|
|
458
|
+
return (o.organizationId?.toString() ?? String(o)) === orgId.toString();
|
|
459
|
+
})) return true;
|
|
460
|
+
if (validateFromDb) {
|
|
461
|
+
const userId = (user._id ?? user.id)?.toString();
|
|
462
|
+
if (userId) return validateFromDb(userId, orgId);
|
|
463
|
+
}
|
|
464
|
+
return false;
|
|
465
|
+
}
|
|
466
|
+
/**
|
|
467
|
+
* Get user's role in organization from user object data.
|
|
468
|
+
* For request-level role checks, use `request.scope.orgRoles` (when scope is 'member').
|
|
469
|
+
*/
|
|
470
|
+
function getUserOrgRoles(user, orgId, options = {}) {
|
|
471
|
+
const { userOrgsPath = "organizations" } = options;
|
|
472
|
+
if (!user || !orgId) return [];
|
|
473
|
+
return (user[userOrgsPath] ?? []).find((o) => {
|
|
474
|
+
return (o.organizationId?.toString() ?? String(o)) === orgId.toString();
|
|
475
|
+
})?.roles ?? [];
|
|
476
|
+
}
|
|
477
|
+
/**
|
|
478
|
+
* Check if user has specific role in organization from user object data.
|
|
479
|
+
* For request-level role checks, use `requireOrgRole()` permission or `request.scope`.
|
|
480
|
+
*/
|
|
481
|
+
function hasOrgRole(user, orgId, roles, options = {}) {
|
|
482
|
+
const userOrgRoles = getUserOrgRoles(user, orgId, options);
|
|
483
|
+
return (Array.isArray(roles) ? roles : [roles]).some((role) => userOrgRoles.includes(role));
|
|
484
|
+
}
|
|
485
|
+
//#endregion
|
|
486
|
+
export { getUserOrgRoles, hasOrgRole, orgGuard, orgMembershipCheck, organizationPlugin_default as organizationPlugin, organizationPlugin as organizationPluginFn, requireOrg, requireOrgRole };
|
package/dist/org/types.d.mts
CHANGED
|
@@ -28,7 +28,7 @@ interface InvitationDoc {
|
|
|
28
28
|
email: string;
|
|
29
29
|
role: string;
|
|
30
30
|
invitedBy: string;
|
|
31
|
-
status:
|
|
31
|
+
status: "pending" | "accepted" | "rejected" | "expired";
|
|
32
32
|
expiresAt: Date;
|
|
33
33
|
createdAt?: Date;
|
|
34
34
|
}
|
|
@@ -53,7 +53,7 @@ interface OrgAdapter {
|
|
|
53
53
|
invitations?: InvitationAdapter;
|
|
54
54
|
}
|
|
55
55
|
interface InvitationAdapter {
|
|
56
|
-
create(data: Omit<InvitationDoc,
|
|
56
|
+
create(data: Omit<InvitationDoc, "id" | "createdAt">): Promise<InvitationDoc>;
|
|
57
57
|
getByToken(token: string): Promise<InvitationDoc | null>;
|
|
58
58
|
accept(id: string): Promise<void>;
|
|
59
59
|
reject(id: string): Promise<void>;
|
package/dist/org/types.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export {};
|