@classytic/arc 2.9.1 → 2.10.8
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 +20 -91
- package/dist/{BaseController-Vu2yc56T.mjs → BaseController-DVNKvoX4.mjs} +154 -170
- package/dist/{ResourceRegistry-Dq3_zBQP.mjs → ResourceRegistry-CcN2LVrc.mjs} +1 -1
- package/dist/actionPermissions-TUVR3uiZ.mjs +22 -0
- package/dist/adapters/index.d.mts +3 -3
- package/dist/adapters/index.mjs +2 -2
- package/dist/{adapters-BBqAVvPK.mjs → adapters-BXY4i-hw.mjs} +210 -41
- package/dist/audit/index.d.mts +38 -3
- package/dist/audit/index.mjs +54 -22
- package/dist/auth/index.d.mts +2 -2
- package/dist/auth/index.mjs +3 -3
- package/dist/cache/index.d.mts +17 -15
- package/dist/cache/index.mjs +16 -15
- package/dist/{caching-CjybdRwx.mjs → caching-3h93rkJM.mjs} +8 -3
- package/dist/cli/commands/describe.mjs +1 -1
- package/dist/cli/commands/docs.mjs +2 -2
- package/dist/cli/commands/init.mjs +1 -1
- package/dist/cli/commands/introspect.mjs +1 -1
- package/dist/context/index.d.mts +58 -0
- package/dist/context/index.mjs +2 -0
- package/dist/core/index.d.mts +2 -2
- package/dist/core/index.mjs +3 -4
- package/dist/{defineResource-C__jkwvs.mjs → core-3MWJosCH.mjs} +174 -94
- package/dist/{createActionRouter-DH1YFL9m.mjs → createActionRouter-C8UUB3Px.mjs} +1 -1
- package/dist/{createApp-CBJUJKGP.mjs → createApp-BwnEAO2h.mjs} +53 -19
- package/dist/docs/index.d.mts +1 -1
- package/dist/docs/index.mjs +2 -2
- package/dist/{elevation-DxQ6ACbt.mjs → elevation-Dci0AYLT.mjs} +2 -2
- package/dist/errorHandler-2ii4RIYr.d.mts +114 -0
- package/dist/{errorHandler-CZDW4EXS.mjs → errorHandler-CSxe7KIM.mjs} +1 -1
- package/dist/{eventPlugin-Dl7MoVWH.mjs → eventPlugin-ByU4Cv0e.mjs} +1 -1
- package/dist/{eventPlugin-BxvaCIZF.d.mts → eventPlugin-D1ThQ1Pp.d.mts} +1 -1
- package/dist/events/index.d.mts +8 -5
- package/dist/events/index.mjs +87 -52
- package/dist/events/transports/redis-stream-entry.d.mts +1 -1
- package/dist/events/transports/redis.d.mts +1 -1
- package/dist/factory/index.d.mts +1 -1
- package/dist/factory/index.mjs +1 -1
- package/dist/{types-DZi1aYhm.d.mts → fields-C8Y0XLAu.d.mts} +122 -2
- package/dist/hooks/index.d.mts +1 -1
- package/dist/idempotency/index.d.mts +5 -2
- package/dist/idempotency/index.mjs +46 -37
- package/dist/{interface-YrWsmKqE.d.mts → index-BGbpGVyM.d.mts} +2107 -2756
- package/dist/{index-CtGKT0lf.d.mts → index-BziRPS4H.d.mts} +81 -7
- package/dist/{index-C-xjcA6F.d.mts → index-C_Noptz-.d.mts} +284 -409
- package/dist/{index-Cibkchnx.d.mts → index-EqQN6p0W.d.mts} +3 -3
- package/dist/index.d.mts +6 -219
- package/dist/index.mjs +10 -131
- 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/interface-yhyb_pLY.d.mts +77 -0
- package/dist/logger/index.d.mts +81 -0
- package/dist/{logger-CDjpjySd.mjs → logger/index.mjs} +1 -6
- package/dist/{memory-BFAYkf8H.mjs → memory-DqI-449b.mjs} +23 -8
- package/dist/middleware/index.d.mts +109 -0
- package/dist/middleware/index.mjs +70 -0
- package/dist/multipartBody-CUQGVlM_.mjs +123 -0
- package/dist/{openapi-CXuTG1M9.mjs → openapi-DpNpqBmo.mjs} +9 -7
- package/dist/org/index.d.mts +2 -2
- package/dist/permissions/index.d.mts +3 -4
- package/dist/permissions/index.mjs +5 -5
- package/dist/{permissions-oNZawnkR.mjs → permissions-wkqRwicB.mjs} +315 -397
- package/dist/pipe-CGJxqDGx.mjs +62 -0
- package/dist/pipeline/index.d.mts +62 -0
- package/dist/pipeline/index.mjs +53 -0
- package/dist/plugins/index.d.mts +23 -3
- package/dist/plugins/index.mjs +9 -11
- package/dist/plugins/response-cache.mjs +1 -1
- package/dist/plugins/tracing-entry.mjs +1 -1
- package/dist/presets/filesUpload.d.mts +3 -3
- package/dist/presets/filesUpload.mjs +255 -1
- package/dist/presets/index.d.mts +1 -1
- package/dist/presets/index.mjs +2 -2
- package/dist/presets/multiTenant.d.mts +1 -1
- package/dist/presets/multiTenant.mjs +43 -9
- package/dist/presets/search.d.mts +91 -4
- package/dist/presets/search.mjs +1 -1
- package/dist/{presets-hM4WhNWY.mjs → presets-CrwOvuXI.mjs} +1 -1
- package/dist/{queryCachePlugin-DbUVroUG.mjs → queryCachePlugin-ChLNZvFT.mjs} +9 -9
- package/dist/{queryCachePlugin-CnTZZTC5.d.mts → queryCachePlugin-Dumka73q.d.mts} +1 -1
- package/dist/{queryParser-Cs-6SHQK.mjs → queryParser-NR__Qiju.mjs} +69 -2
- package/dist/{redis-stream-Bz-4q96t.d.mts → redis-stream-bkO88VHx.d.mts} +1 -1
- package/dist/registry/index.d.mts +1 -1
- package/dist/registry/index.mjs +1 -1
- package/dist/{requestContext-DYtmNpm5.mjs → requestContext-C38GskNt.mjs} +1 -1
- package/dist/{resourceToTools-C3cWymnW.mjs → resourceToTools-BhF3JV5p.mjs} +8 -3
- package/dist/scope/index.d.mts +2 -2
- package/dist/scope/index.mjs +2 -2
- package/dist/{sse-CJpt7LGI.mjs → sse-D8UeDwis.mjs} +1 -1
- package/dist/{store-helpers-DFiZl5TL.mjs → store-helpers-DYYUQbQN.mjs} +4 -0
- package/dist/testing/index.d.mts +6 -5
- package/dist/testing/index.mjs +17 -10
- package/dist/types/index.d.mts +5 -5
- package/dist/types/index.mjs +1 -31
- package/dist/types-CDnTEpga.mjs +27 -0
- package/dist/{types-CoSzA-s-.d.mts → types-CVKBssX5.d.mts} +1 -1
- package/dist/{types-CunEX4UX.d.mts → types-CVdgPXBW.d.mts} +20 -7
- package/dist/utils/index.d.mts +277 -3
- package/dist/utils/index.mjs +4 -5
- package/dist/{utils-B7FuRr9w.mjs → utils-LMwVidKy.mjs} +303 -2
- package/dist/{versioning-Cm8qoFDg.mjs → versioning-B6mimogM.mjs} +3 -5
- package/dist/versioning-CeUXHfjw.d.mts +117 -0
- package/package.json +31 -18
- package/skills/arc/SKILL.md +8 -12
- package/skills/arc/references/production.md +0 -41
- package/dist/circuitBreaker-CvXkjfrW.d.mts +0 -206
- package/dist/circuitBreaker-l18oRgL5.mjs +0 -284
- package/dist/core-DNncu0xF.mjs +0 -34
- package/dist/dynamic/index.d.mts +0 -93
- package/dist/dynamic/index.mjs +0 -122
- package/dist/errorHandler-DixGcttC.d.mts +0 -218
- package/dist/fields-BC7zcmI9.d.mts +0 -121
- package/dist/filesUpload-q8oHt--L.mjs +0 -377
- package/dist/interface-DplgQO2e.d.mts +0 -54
- package/dist/policies/index.d.mts +0 -425
- package/dist/policies/index.mjs +0 -318
- package/dist/rpc/index.d.mts +0 -90
- package/dist/rpc/index.mjs +0 -248
- /package/dist/{EventTransport-CqZ8FyM_.d.mts → EventTransport-CfVEGaEl.d.mts} +0 -0
- /package/dist/{applyPermissionResult-bqGpo9ML.mjs → applyPermissionResult-QhV1Pa-g.mjs} +0 -0
- /package/dist/{constants-Cxde4rpC.mjs → constants-BhY1OHoH.mjs} +0 -0
- /package/dist/{elevation-B6S5csVA.d.mts → elevation-s5ykdNHr.d.mts} +0 -0
- /package/dist/{errors-CqWnSqM-.mjs → errors-BqdUDja_.mjs} +0 -0
- /package/dist/{fields-CU6FlaDV.mjs → fields-CTMWOUDt.mjs} +0 -0
- /package/dist/{keys-qcD-TVJl.mjs → keys-nWQGUTu1.mjs} +0 -0
- /package/dist/{types-ZUu_h0jp.mjs → types-D57iXYb8.mjs} +0 -0
- /package/dist/{types-BD85MlEK.d.mts → types-tgR4Pt8F.d.mts} +0 -0
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { a as PrismaQueryParserOptions, c as MongooseAdapterOptions, i as PrismaQueryParser, l as createMongooseAdapter, n as PrismaAdapterOptions, o as createPrismaAdapter, r as PrismaQueryOptions, s as MongooseAdapter, t as PrismaAdapter } from "../index-
|
|
3
|
-
export { AdapterFactory, DataAdapter, FieldMetadata, MongooseAdapter, MongooseAdapterOptions, PrismaAdapter, PrismaAdapterOptions, PrismaQueryOptions, PrismaQueryParser, PrismaQueryParserOptions, RelationMetadata, RepositoryLike, SchemaMetadata, ValidationResult, createMongooseAdapter, createPrismaAdapter };
|
|
1
|
+
import { _n as RepositoryLike, fn as AdapterFactory, gn as RelationMetadata, hn as FieldMetadata, mn as DataAdapter, vn as SchemaMetadata, yn as ValidationResult } from "../index-BGbpGVyM.mjs";
|
|
2
|
+
import { a as PrismaQueryParserOptions, c as MongooseAdapterOptions, d as DrizzleAdapterOptions, f as createDrizzleAdapter, i as PrismaQueryParser, l as createMongooseAdapter, n as PrismaAdapterOptions, o as createPrismaAdapter, r as PrismaQueryOptions, s as MongooseAdapter, t as PrismaAdapter, u as DrizzleAdapter } from "../index-BziRPS4H.mjs";
|
|
3
|
+
export { AdapterFactory, DataAdapter, DrizzleAdapter, DrizzleAdapterOptions, FieldMetadata, MongooseAdapter, MongooseAdapterOptions, PrismaAdapter, PrismaAdapterOptions, PrismaQueryOptions, PrismaQueryParser, PrismaQueryParserOptions, RelationMetadata, RepositoryLike, SchemaMetadata, ValidationResult, createDrizzleAdapter, createMongooseAdapter, createPrismaAdapter };
|
package/dist/adapters/index.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as createMongooseAdapter, i as MongooseAdapter, n as PrismaQueryParser, r as createPrismaAdapter, t as PrismaAdapter } from "../adapters-
|
|
2
|
-
export { MongooseAdapter, PrismaAdapter, PrismaQueryParser, createMongooseAdapter, createPrismaAdapter };
|
|
1
|
+
import { a as createMongooseAdapter, i as MongooseAdapter, n as PrismaQueryParser, o as DrizzleAdapter, r as createPrismaAdapter, s as createDrizzleAdapter, t as PrismaAdapter } from "../adapters-BXY4i-hw.mjs";
|
|
2
|
+
export { DrizzleAdapter, MongooseAdapter, PrismaAdapter, PrismaQueryParser, createDrizzleAdapter, createMongooseAdapter, createPrismaAdapter };
|
|
@@ -1,4 +1,44 @@
|
|
|
1
|
-
import { h as SYSTEM_FIELDS, m as RESERVED_QUERY_PARAMS } from "./constants-
|
|
1
|
+
import { h as SYSTEM_FIELDS, m as RESERVED_QUERY_PARAMS } from "./constants-BhY1OHoH.mjs";
|
|
2
|
+
//#region src/adapters/field-rule-helpers.ts
|
|
3
|
+
/**
|
|
4
|
+
* Merge constraint-style `fieldRules` into an `OpenApiSchemas` bag in place.
|
|
5
|
+
*
|
|
6
|
+
* Operates on the three schema slots that carry property maps — `createBody`,
|
|
7
|
+
* `updateBody`, `response`. `listQuery` and `params` are skipped (their
|
|
8
|
+
* constraint vocabulary is owned by the kit's query parser).
|
|
9
|
+
*
|
|
10
|
+
* Existing constraints on a property always win — the merge only fills in
|
|
11
|
+
* gaps. Adapters that already walk `fieldRules` during base-schema assembly
|
|
12
|
+
* can call this helper for free (the checks are no-ops when constraints
|
|
13
|
+
* already exist).
|
|
14
|
+
*/
|
|
15
|
+
function mergeFieldRuleConstraints(schemas, schemaOptions) {
|
|
16
|
+
if (!schemas || typeof schemas !== "object") return;
|
|
17
|
+
const rules = schemaOptions?.fieldRules;
|
|
18
|
+
if (!rules || Object.keys(rules).length === 0) return;
|
|
19
|
+
for (const slot of [
|
|
20
|
+
"createBody",
|
|
21
|
+
"updateBody",
|
|
22
|
+
"response"
|
|
23
|
+
]) {
|
|
24
|
+
const slotSchema = schemas[slot];
|
|
25
|
+
if (!slotSchema || typeof slotSchema !== "object") continue;
|
|
26
|
+
const properties = slotSchema.properties;
|
|
27
|
+
if (!properties) continue;
|
|
28
|
+
for (const [field, rule] of Object.entries(rules)) {
|
|
29
|
+
const prop = properties[field];
|
|
30
|
+
if (!prop || typeof prop !== "object") continue;
|
|
31
|
+
if (rule.minLength != null && prop.minLength == null) prop.minLength = rule.minLength;
|
|
32
|
+
if (rule.maxLength != null && prop.maxLength == null) prop.maxLength = rule.maxLength;
|
|
33
|
+
if (rule.min != null && prop.minimum == null) prop.minimum = rule.min;
|
|
34
|
+
if (rule.max != null && prop.maximum == null) prop.maximum = rule.max;
|
|
35
|
+
if (rule.pattern != null && prop.pattern == null) prop.pattern = rule.pattern;
|
|
36
|
+
if (rule.enum != null && prop.enum == null) prop.enum = rule.enum;
|
|
37
|
+
if (rule.description != null && prop.description == null) prop.description = rule.description;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
//#endregion
|
|
2
42
|
//#region src/adapters/types.ts
|
|
3
43
|
/**
|
|
4
44
|
* Check if value is a Mongoose model
|
|
@@ -13,6 +53,168 @@ function isRepository(value) {
|
|
|
13
53
|
return typeof value === "object" && value !== null && "getAll" in value && "getById" in value && "create" in value && "update" in value && "delete" in value;
|
|
14
54
|
}
|
|
15
55
|
//#endregion
|
|
56
|
+
//#region src/adapters/drizzle.ts
|
|
57
|
+
const DRIZZLE_COLUMNS_SYMBOL = Symbol.for("drizzle:Columns");
|
|
58
|
+
function getColumns(table) {
|
|
59
|
+
const cols = table[DRIZZLE_COLUMNS_SYMBOL];
|
|
60
|
+
if (!cols || typeof cols !== "object") return {};
|
|
61
|
+
return cols;
|
|
62
|
+
}
|
|
63
|
+
function columnToJsonSchema(column) {
|
|
64
|
+
const { dataType, columnType, enumValues, length } = column;
|
|
65
|
+
if (dataType === "date") return {
|
|
66
|
+
type: "string",
|
|
67
|
+
format: "date-time"
|
|
68
|
+
};
|
|
69
|
+
if (dataType === "boolean") return { type: "boolean" };
|
|
70
|
+
if (dataType === "json") return {
|
|
71
|
+
type: "object",
|
|
72
|
+
additionalProperties: true
|
|
73
|
+
};
|
|
74
|
+
if (dataType === "buffer") return {
|
|
75
|
+
type: "string",
|
|
76
|
+
contentEncoding: "base64"
|
|
77
|
+
};
|
|
78
|
+
if (dataType === "number" || dataType === "bigint") return { type: columnType === "SQLiteInteger" ? "integer" : "number" };
|
|
79
|
+
if (dataType === "string") {
|
|
80
|
+
const result = { type: "string" };
|
|
81
|
+
if (Array.isArray(enumValues) && enumValues.length > 0) result.enum = [...enumValues];
|
|
82
|
+
if (typeof length === "number" && length > 0) result.maxLength = length;
|
|
83
|
+
return result;
|
|
84
|
+
}
|
|
85
|
+
return {};
|
|
86
|
+
}
|
|
87
|
+
function columnToFieldMetadata(column) {
|
|
88
|
+
const { dataType, enumValues } = column;
|
|
89
|
+
const meta = {
|
|
90
|
+
type: (dataType && {
|
|
91
|
+
number: "number",
|
|
92
|
+
bigint: "number",
|
|
93
|
+
string: "string",
|
|
94
|
+
date: "date",
|
|
95
|
+
boolean: "boolean",
|
|
96
|
+
json: "object",
|
|
97
|
+
buffer: "object"
|
|
98
|
+
}[dataType]) ?? (enumValues?.length ? "enum" : "object"),
|
|
99
|
+
required: !!column.notNull && !column.hasDefault
|
|
100
|
+
};
|
|
101
|
+
if (enumValues?.length) meta.enum = [...enumValues];
|
|
102
|
+
if (typeof column.length === "number") meta.maxLength = column.length;
|
|
103
|
+
return meta;
|
|
104
|
+
}
|
|
105
|
+
var DrizzleAdapter = class {
|
|
106
|
+
type = "drizzle";
|
|
107
|
+
name;
|
|
108
|
+
table;
|
|
109
|
+
repository;
|
|
110
|
+
schemaGenerator;
|
|
111
|
+
constructor(options) {
|
|
112
|
+
if (!options.table || typeof options.table !== "object") throw new TypeError("DrizzleAdapter: Invalid table. Expected a Drizzle table created with sqliteTable / pgTable / mysqlTable.");
|
|
113
|
+
if (!isRepository(options.repository)) throw new TypeError("DrizzleAdapter: Invalid repository. Expected an object implementing MinimalRepo (getAll / getById / create / update / delete).");
|
|
114
|
+
this.table = options.table;
|
|
115
|
+
this.repository = options.repository;
|
|
116
|
+
this.schemaGenerator = options.schemaGenerator;
|
|
117
|
+
this.name = options.name ?? "DrizzleAdapter";
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Introspect Drizzle columns into arc's schema metadata shape.
|
|
121
|
+
*/
|
|
122
|
+
getSchemaMetadata() {
|
|
123
|
+
const columns = getColumns(this.table);
|
|
124
|
+
const fields = {};
|
|
125
|
+
const indexes = [];
|
|
126
|
+
for (const [name, column] of Object.entries(columns)) {
|
|
127
|
+
fields[name] = columnToFieldMetadata(column);
|
|
128
|
+
if (column.primary) indexes.push({
|
|
129
|
+
fields: [name],
|
|
130
|
+
unique: true
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
return {
|
|
134
|
+
name: this.name,
|
|
135
|
+
fields,
|
|
136
|
+
...indexes.length > 0 ? { indexes } : {}
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Generate OpenAPI schemas. Delegates to the user-provided
|
|
141
|
+
* `schemaGenerator` when available (strongly recommended — that's where
|
|
142
|
+
* field rules, omit lists, and param-type narrowing live). The built-in
|
|
143
|
+
* fallback emits a permissive entity + CRUD body shape so routes still
|
|
144
|
+
* register when no generator is provided.
|
|
145
|
+
*
|
|
146
|
+
* After the kit generator runs, arc merges constraint-style field rules
|
|
147
|
+
* (`minLength`, `maxLength`, `min`, `max`, `pattern`, `enum`, `description`)
|
|
148
|
+
* into the resulting property schemas so sqlitekit / pgkit behave
|
|
149
|
+
* identically to mongoose here — rule-driven AJV constraints apply
|
|
150
|
+
* regardless of backend.
|
|
151
|
+
*/
|
|
152
|
+
generateSchemas(schemaOptions, context) {
|
|
153
|
+
try {
|
|
154
|
+
if (this.schemaGenerator) {
|
|
155
|
+
const generated = this.schemaGenerator(this.table, schemaOptions, context);
|
|
156
|
+
mergeFieldRuleConstraints(generated, schemaOptions);
|
|
157
|
+
return generated;
|
|
158
|
+
}
|
|
159
|
+
const columns = getColumns(this.table);
|
|
160
|
+
if (Object.keys(columns).length === 0) return null;
|
|
161
|
+
const entityProperties = {};
|
|
162
|
+
const inputProperties = {};
|
|
163
|
+
const inputRequired = [];
|
|
164
|
+
const updateProperties = {};
|
|
165
|
+
const fieldRules = schemaOptions?.fieldRules ?? {};
|
|
166
|
+
const readonlySet = new Set(schemaOptions?.readonlyFields ?? []);
|
|
167
|
+
const optionalSet = new Set(schemaOptions?.optionalFields ?? []);
|
|
168
|
+
const blocked = new Set([
|
|
169
|
+
...Object.entries(fieldRules).filter(([, rules]) => rules.systemManaged || rules.hidden).map(([field]) => field),
|
|
170
|
+
...schemaOptions?.excludeFields ?? [],
|
|
171
|
+
...schemaOptions?.hiddenFields ?? []
|
|
172
|
+
]);
|
|
173
|
+
for (const [fieldName, column] of Object.entries(columns)) {
|
|
174
|
+
const schema = columnToJsonSchema(column);
|
|
175
|
+
entityProperties[fieldName] = schema;
|
|
176
|
+
if (blocked.has(fieldName)) continue;
|
|
177
|
+
if (column.primary && column.columnType === "SQLiteInteger") continue;
|
|
178
|
+
if (!readonlySet.has(fieldName)) {
|
|
179
|
+
inputProperties[fieldName] = schema;
|
|
180
|
+
if (!!column.notNull && !column.hasDefault && !optionalSet.has(fieldName)) inputRequired.push(fieldName);
|
|
181
|
+
updateProperties[fieldName] = schema;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
return {
|
|
185
|
+
createBody: {
|
|
186
|
+
type: "object",
|
|
187
|
+
properties: inputProperties,
|
|
188
|
+
required: inputRequired.length > 0 ? inputRequired : void 0,
|
|
189
|
+
additionalProperties: true
|
|
190
|
+
},
|
|
191
|
+
updateBody: {
|
|
192
|
+
type: "object",
|
|
193
|
+
properties: updateProperties,
|
|
194
|
+
additionalProperties: true
|
|
195
|
+
},
|
|
196
|
+
response: {
|
|
197
|
+
type: "object",
|
|
198
|
+
properties: entityProperties,
|
|
199
|
+
additionalProperties: true
|
|
200
|
+
}
|
|
201
|
+
};
|
|
202
|
+
} catch {
|
|
203
|
+
return null;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
async healthCheck() {
|
|
207
|
+
return typeof this.repository.getAll === "function";
|
|
208
|
+
}
|
|
209
|
+
};
|
|
210
|
+
/**
|
|
211
|
+
* Factory — preferred construction style for symmetry with
|
|
212
|
+
* `createMongooseAdapter` / `createPrismaAdapter`.
|
|
213
|
+
*/
|
|
214
|
+
function createDrizzleAdapter(options) {
|
|
215
|
+
return new DrizzleAdapter(options);
|
|
216
|
+
}
|
|
217
|
+
//#endregion
|
|
16
218
|
//#region src/adapters/mongoose.ts
|
|
17
219
|
/**
|
|
18
220
|
* Mongoose data adapter with proper type safety
|
|
@@ -27,7 +229,7 @@ var MongooseAdapter = class {
|
|
|
27
229
|
schemaGenerator;
|
|
28
230
|
constructor(options) {
|
|
29
231
|
if (!isMongooseModel(options.model)) throw new TypeError("MongooseAdapter: Invalid model. Expected Mongoose Model instance.\nUsage: createMongooseAdapter({ model: YourModel, repository: yourRepo })");
|
|
30
|
-
if (!isRepository(options.repository)) throw new TypeError("MongooseAdapter: Invalid repository. Expected
|
|
232
|
+
if (!isRepository(options.repository)) throw new TypeError("MongooseAdapter: Invalid repository. Expected StandardRepo instance.\nUsage: createMongooseAdapter({ model: YourModel, repository: yourRepo })");
|
|
31
233
|
this.model = options.model;
|
|
32
234
|
this.repository = options.repository;
|
|
33
235
|
this.schemaGenerator = options.schemaGenerator;
|
|
@@ -73,7 +275,11 @@ var MongooseAdapter = class {
|
|
|
73
275
|
*/
|
|
74
276
|
generateSchemas(schemaOptions, context) {
|
|
75
277
|
try {
|
|
76
|
-
if (this.schemaGenerator)
|
|
278
|
+
if (this.schemaGenerator) {
|
|
279
|
+
const generated = this.schemaGenerator(this.model, schemaOptions, context);
|
|
280
|
+
mergeFieldRuleConstraints(generated, schemaOptions);
|
|
281
|
+
return generated;
|
|
282
|
+
}
|
|
77
283
|
const paths = this.model.schema.paths;
|
|
78
284
|
const properties = {};
|
|
79
285
|
const required = [];
|
|
@@ -242,43 +448,6 @@ function createMongooseAdapter(modelOrOptions, repository) {
|
|
|
242
448
|
//#endregion
|
|
243
449
|
//#region src/adapters/prisma.ts
|
|
244
450
|
/**
|
|
245
|
-
* Prisma Adapter - PostgreSQL/MySQL/SQLite Implementation
|
|
246
|
-
*
|
|
247
|
-
* @experimental This adapter is implemented but has no integration tests yet.
|
|
248
|
-
* Use in production at your own risk. The Mongoose adapter is the recommended
|
|
249
|
-
* and battle-tested path.
|
|
250
|
-
*
|
|
251
|
-
* Bridges Prisma Client with Arc's DataAdapter interface.
|
|
252
|
-
* Supports Prisma 5+ with all database providers.
|
|
253
|
-
*
|
|
254
|
-
* Implemented features:
|
|
255
|
-
* - Schema generation (OpenAPI docs from DMMF)
|
|
256
|
-
* - Health checks (database connectivity)
|
|
257
|
-
* - Query parsing (URL params → Prisma where/orderBy)
|
|
258
|
-
* - Policy filter translation
|
|
259
|
-
* - Soft delete preset support
|
|
260
|
-
*
|
|
261
|
-
* Known gaps:
|
|
262
|
-
* - No integration test coverage
|
|
263
|
-
* - Multi-tenant isolation relies on caller-provided policyFilters (no auto-enforcement)
|
|
264
|
-
*
|
|
265
|
-
* @example
|
|
266
|
-
* ```typescript
|
|
267
|
-
* import { PrismaClient, Prisma } from '@prisma/client';
|
|
268
|
-
* import { createPrismaAdapter, PrismaQueryParser } from '@classytic/arc/adapters';
|
|
269
|
-
*
|
|
270
|
-
* const prisma = new PrismaClient();
|
|
271
|
-
*
|
|
272
|
-
* const userAdapter = createPrismaAdapter({
|
|
273
|
-
* client: prisma,
|
|
274
|
-
* modelName: 'user',
|
|
275
|
-
* repository: new UserRepository(prisma),
|
|
276
|
-
* dmmf: Prisma.dmmf, // For schema generation
|
|
277
|
-
* queryParser: new PrismaQueryParser(), // Optional: custom parser
|
|
278
|
-
* });
|
|
279
|
-
* ```
|
|
280
|
-
*/
|
|
281
|
-
/**
|
|
282
451
|
* Prisma Query Parser - Converts URL parameters to Prisma query format
|
|
283
452
|
*
|
|
284
453
|
* Translates Arc's query format to Prisma's where/orderBy/take/skip structure.
|
|
@@ -723,4 +892,4 @@ function createPrismaAdapter(options) {
|
|
|
723
892
|
return new PrismaAdapter(options);
|
|
724
893
|
}
|
|
725
894
|
//#endregion
|
|
726
|
-
export { createMongooseAdapter as a, MongooseAdapter as i, PrismaQueryParser as n, createPrismaAdapter as r, PrismaAdapter as t };
|
|
895
|
+
export { createMongooseAdapter as a, MongooseAdapter as i, PrismaQueryParser as n, DrizzleAdapter as o, createPrismaAdapter as r, createDrizzleAdapter as s, PrismaAdapter as t };
|
package/dist/audit/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { _n as RepositoryLike } from "../index-BGbpGVyM.mjs";
|
|
2
|
+
import { d as UserBase } from "../fields-C8Y0XLAu.mjs";
|
|
3
3
|
import { FastifyPluginAsync } from "fastify";
|
|
4
4
|
|
|
5
5
|
//#region src/audit/stores/interface.d.ts
|
|
@@ -59,6 +59,15 @@ interface AuditStore {
|
|
|
59
59
|
log(entry: AuditEntry): Promise<void>;
|
|
60
60
|
/** Query audit logs (optional - not all stores support querying) */
|
|
61
61
|
query?(options: AuditQueryOptions): Promise<AuditEntry[]>;
|
|
62
|
+
/**
|
|
63
|
+
* Purge entries older than `cutoff`, return count deleted. Optional —
|
|
64
|
+
* stores that don't support deletion (append-only emitters like Kafka,
|
|
65
|
+
* S3 archivers) simply omit this method and are skipped by
|
|
66
|
+
* `fastify.audit.purge(...)`. Mongo-backed repositories can also rely
|
|
67
|
+
* on a server-side TTL index instead of calling this; the method is
|
|
68
|
+
* the DB-agnostic escape hatch.
|
|
69
|
+
*/
|
|
70
|
+
purgeOlderThan?(cutoff: Date): Promise<number>;
|
|
62
71
|
/** Close/cleanup (optional) */
|
|
63
72
|
close?(): Promise<void>;
|
|
64
73
|
}
|
|
@@ -104,6 +113,22 @@ interface AuditPluginOptions {
|
|
|
104
113
|
* to every store.
|
|
105
114
|
*/
|
|
106
115
|
customStores?: AuditStore[];
|
|
116
|
+
/**
|
|
117
|
+
* Retention policy — optional. Entries older than `maxAgeMs` are purged
|
|
118
|
+
* on a timer (`purgeIntervalMs`, default 24h). Stores that implement
|
|
119
|
+
* `purgeOlderThan` participate; append-only stores are skipped.
|
|
120
|
+
*
|
|
121
|
+
* Apps on MongoDB can instead declare a TTL index on the audit
|
|
122
|
+
* collection's `timestamp` field — server-side TTL is cheaper than a
|
|
123
|
+
* periodic delete. Both approaches coexist: `fastify.audit.purge(...)`
|
|
124
|
+
* is always available for manual / cron-driven purges.
|
|
125
|
+
*
|
|
126
|
+
* Set `purgeIntervalMs: 0` to skip the timer (manual purge only).
|
|
127
|
+
*/
|
|
128
|
+
retention?: {
|
|
129
|
+
/** Max entry age in ms. Entries with `timestamp < now - maxAgeMs` are purged. */maxAgeMs: number; /** Interval between purges in ms. Default 86_400_000 (24h). 0 disables the timer. */
|
|
130
|
+
purgeIntervalMs?: number;
|
|
131
|
+
};
|
|
107
132
|
/**
|
|
108
133
|
* Automatically audit CRUD operations via the hook system (default: true when enabled).
|
|
109
134
|
* When enabled, create/update/delete operations are auto-logged without manual calls.
|
|
@@ -167,10 +192,19 @@ interface AuditLogger {
|
|
|
167
192
|
custom: (resource: string, documentId: string, action: string, data?: Record<string, unknown>, context?: AuditContext) => Promise<void>;
|
|
168
193
|
/** Query audit logs (if stores support it) */
|
|
169
194
|
query: (options: AuditQueryOptions) => Promise<AuditEntry[]>;
|
|
195
|
+
/**
|
|
196
|
+
* Purge audit entries older than `cutoff` across every registered store.
|
|
197
|
+
* Returns the total number of entries deleted. Stores that don't support
|
|
198
|
+
* deletion (append-only emitters) are skipped silently.
|
|
199
|
+
*/
|
|
200
|
+
purge: (cutoff: Date) => Promise<number>;
|
|
170
201
|
}
|
|
171
202
|
declare const auditPlugin: FastifyPluginAsync<AuditPluginOptions>;
|
|
172
203
|
declare const _default: FastifyPluginAsync<AuditPluginOptions>;
|
|
173
204
|
//#endregion
|
|
205
|
+
//#region src/audit/repository-audit-adapter.d.ts
|
|
206
|
+
declare function repositoryAsAuditStore(repository: RepositoryLike): AuditStore;
|
|
207
|
+
//#endregion
|
|
174
208
|
//#region src/audit/stores/memory.d.ts
|
|
175
209
|
interface MemoryAuditStoreOptions {
|
|
176
210
|
/** Maximum entries to keep (default: 1000) */
|
|
@@ -183,6 +217,7 @@ declare class MemoryAuditStore implements AuditStore {
|
|
|
183
217
|
constructor(options?: MemoryAuditStoreOptions);
|
|
184
218
|
log(entry: AuditEntry): Promise<void>;
|
|
185
219
|
query(options?: AuditQueryOptions): Promise<AuditEntry[]>;
|
|
220
|
+
purgeOlderThan(cutoff: Date): Promise<number>;
|
|
186
221
|
close(): Promise<void>;
|
|
187
222
|
/** Get all entries (for testing) */
|
|
188
223
|
getAll(): AuditEntry[];
|
|
@@ -190,4 +225,4 @@ declare class MemoryAuditStore implements AuditStore {
|
|
|
190
225
|
clear(): void;
|
|
191
226
|
}
|
|
192
227
|
//#endregion
|
|
193
|
-
export { type AuditAction, type AuditContext, type AuditEntry, type AuditLogger, type AuditPluginOptions, type AuditQueryOptions, type AuditStore, type AuditStoreOptions, MemoryAuditStore, type MemoryAuditStoreOptions, _default as auditPlugin, auditPlugin as auditPluginFn, createAuditEntry };
|
|
228
|
+
export { type AuditAction, type AuditContext, type AuditEntry, type AuditLogger, type AuditPluginOptions, type AuditQueryOptions, type AuditStore, type AuditStoreOptions, MemoryAuditStore, type MemoryAuditStoreOptions, _default as auditPlugin, auditPlugin as auditPluginFn, createAuditEntry, repositoryAsAuditStore };
|
package/dist/audit/index.mjs
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import fp from "fastify-plugin";
|
|
2
|
+
import { and, anyOf, eq, gte, lt, lte } from "@classytic/repo-core/filter";
|
|
2
3
|
//#region src/audit/repository-audit-adapter.ts
|
|
3
4
|
function repositoryAsAuditStore(repository) {
|
|
5
|
+
const idField = repository.idField ?? "_id";
|
|
4
6
|
return {
|
|
5
7
|
name: "repository",
|
|
6
8
|
async log(entry) {
|
|
7
9
|
const doc = {
|
|
8
|
-
|
|
9
|
-
id: entry.id,
|
|
10
|
+
[idField]: entry.id,
|
|
10
11
|
resource: entry.resource,
|
|
11
12
|
documentId: entry.documentId,
|
|
12
13
|
action: entry.action,
|
|
@@ -23,29 +24,34 @@ function repositoryAsAuditStore(repository) {
|
|
|
23
24
|
};
|
|
24
25
|
await repository.create(doc);
|
|
25
26
|
},
|
|
27
|
+
async purgeOlderThan(cutoff) {
|
|
28
|
+
if (!repository.deleteMany) return 0;
|
|
29
|
+
return (await repository.deleteMany(lt("timestamp", cutoff))).deletedCount ?? 0;
|
|
30
|
+
},
|
|
26
31
|
async query(opts = {}) {
|
|
27
|
-
if (!repository.
|
|
28
|
-
const
|
|
29
|
-
if (opts.resource)
|
|
30
|
-
if (opts.documentId)
|
|
31
|
-
if (opts.userId)
|
|
32
|
-
if (opts.organizationId)
|
|
32
|
+
if (!repository.getAll) throw new Error("auditPlugin: repository.getAll is required for query(). It's on repo-core's MinimalRepo floor — every kit (mongokit, sqlitekit, custom) implements it.");
|
|
33
|
+
const clauses = [];
|
|
34
|
+
if (opts.resource) clauses.push(eq("resource", opts.resource));
|
|
35
|
+
if (opts.documentId) clauses.push(eq("documentId", opts.documentId));
|
|
36
|
+
if (opts.userId) clauses.push(eq("userId", opts.userId));
|
|
37
|
+
if (opts.organizationId) clauses.push(eq("organizationId", opts.organizationId));
|
|
33
38
|
if (opts.action) {
|
|
34
39
|
const actions = Array.isArray(opts.action) ? opts.action : [opts.action];
|
|
35
|
-
|
|
36
|
-
}
|
|
37
|
-
if (opts.from || opts.to) {
|
|
38
|
-
const range = {};
|
|
39
|
-
if (opts.from) range.$gte = opts.from;
|
|
40
|
-
if (opts.to) range.$lte = opts.to;
|
|
41
|
-
filter.timestamp = range;
|
|
40
|
+
clauses.push(actions.length === 1 ? eq("action", actions[0]) : anyOf("action", actions));
|
|
42
41
|
}
|
|
43
|
-
|
|
42
|
+
if (opts.from) clauses.push(gte("timestamp", opts.from));
|
|
43
|
+
if (opts.to) clauses.push(lte("timestamp", opts.to));
|
|
44
|
+
const filters = clauses.length > 0 ? and(...clauses) : void 0;
|
|
45
|
+
const limit = opts.limit ?? 100;
|
|
46
|
+
const page = Math.floor((opts.offset ?? 0) / limit) + 1;
|
|
47
|
+
const result = await repository.getAll({
|
|
48
|
+
...filters ? { filters } : {},
|
|
44
49
|
sort: { timestamp: -1 },
|
|
45
|
-
|
|
46
|
-
limit
|
|
47
|
-
})
|
|
48
|
-
|
|
50
|
+
page,
|
|
51
|
+
limit
|
|
52
|
+
});
|
|
53
|
+
return (Array.isArray(result) ? result : result.docs ?? []).map((d) => ({
|
|
54
|
+
id: String(d[idField] ?? ""),
|
|
49
55
|
resource: d.resource ?? "",
|
|
50
56
|
documentId: d.documentId ?? "",
|
|
51
57
|
action: d.action ?? "create",
|
|
@@ -145,6 +151,11 @@ var MemoryAuditStore = class {
|
|
|
145
151
|
results = results.slice(offset, offset + limit);
|
|
146
152
|
return results;
|
|
147
153
|
}
|
|
154
|
+
async purgeOlderThan(cutoff) {
|
|
155
|
+
const before = this.entries.length;
|
|
156
|
+
this.entries = this.entries.filter((e) => e.timestamp >= cutoff);
|
|
157
|
+
return before - this.entries.length;
|
|
158
|
+
}
|
|
148
159
|
async close() {
|
|
149
160
|
this.entries = [];
|
|
150
161
|
}
|
|
@@ -199,6 +210,11 @@ const auditPlugin = async (fastify, opts = {}) => {
|
|
|
199
210
|
async query(options) {
|
|
200
211
|
for (const store of stores) if (store.query) return store.query(options);
|
|
201
212
|
return [];
|
|
213
|
+
},
|
|
214
|
+
async purge(cutoff) {
|
|
215
|
+
let total = 0;
|
|
216
|
+
for (const store of stores) if (store.purgeOlderThan) total += await store.purgeOlderThan(cutoff);
|
|
217
|
+
return total;
|
|
202
218
|
}
|
|
203
219
|
};
|
|
204
220
|
fastify.decorate("audit", audit);
|
|
@@ -223,7 +239,22 @@ const auditPlugin = async (fastify, opts = {}) => {
|
|
|
223
239
|
request.auditContext.duration = Math.round(reply.elapsedTime);
|
|
224
240
|
}
|
|
225
241
|
});
|
|
242
|
+
const retention = opts.retention;
|
|
243
|
+
let retentionTimer = null;
|
|
244
|
+
if (retention) {
|
|
245
|
+
const interval = retention.purgeIntervalMs ?? 864e5;
|
|
246
|
+
if (interval > 0) {
|
|
247
|
+
retentionTimer = setInterval(() => {
|
|
248
|
+
const cutoff = new Date(Date.now() - retention.maxAgeMs);
|
|
249
|
+
audit.purge(cutoff).catch((err) => {
|
|
250
|
+
fastify.log?.warn?.({ err }, "audit retention purge failed");
|
|
251
|
+
});
|
|
252
|
+
}, interval);
|
|
253
|
+
retentionTimer.unref?.();
|
|
254
|
+
}
|
|
255
|
+
}
|
|
226
256
|
fastify.addHook("onClose", async () => {
|
|
257
|
+
if (retentionTimer) clearInterval(retentionTimer);
|
|
227
258
|
await Promise.all(stores.map((store) => store.close?.()));
|
|
228
259
|
});
|
|
229
260
|
const autoAuditConfig = opts.autoAudit ?? true;
|
|
@@ -317,7 +348,8 @@ function createNoopLogger() {
|
|
|
317
348
|
delete: noop,
|
|
318
349
|
restore: noop,
|
|
319
350
|
custom: noop,
|
|
320
|
-
query: async () => []
|
|
351
|
+
query: async () => [],
|
|
352
|
+
purge: async () => 0
|
|
321
353
|
};
|
|
322
354
|
}
|
|
323
355
|
var auditPlugin_default = fp(auditPlugin, {
|
|
@@ -326,4 +358,4 @@ var auditPlugin_default = fp(auditPlugin, {
|
|
|
326
358
|
dependencies: ["arc-core"]
|
|
327
359
|
});
|
|
328
360
|
//#endregion
|
|
329
|
-
export { MemoryAuditStore, auditPlugin_default as auditPlugin, auditPlugin as auditPluginFn, createAuditEntry };
|
|
361
|
+
export { MemoryAuditStore, auditPlugin_default as auditPlugin, auditPlugin as auditPluginFn, createAuditEntry, repositoryAsAuditStore };
|
package/dist/auth/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { Ut as AuthHelpers, Wt as AuthPluginOptions } from "../index-BGbpGVyM.mjs";
|
|
2
|
+
import { c as PermissionCheck } from "../fields-C8Y0XLAu.mjs";
|
|
3
3
|
import { t as ExternalOpenApiPaths } from "../externalPaths-Bapitwvd.mjs";
|
|
4
4
|
import { a as SessionManagerOptions, c as createSessionManager, i as SessionData, n as MemorySessionStoreOptions, o as SessionManagerResult, r as SessionCookieOptions, s as SessionStore, t as MemorySessionStore } from "../sessionManager-D-oNWHz3.mjs";
|
|
5
5
|
import { FastifyPluginAsync, FastifyReply as FastifyReply$1, FastifyRequest } from "fastify";
|
package/dist/auth/index.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { n as normalizeRoles, t as getUserRoles } from "../types-
|
|
2
|
-
import { t as ArcError } from "../errors-
|
|
3
|
-
import {
|
|
1
|
+
import { n as normalizeRoles, t as getUserRoles } from "../types-D57iXYb8.mjs";
|
|
2
|
+
import { t as ArcError } from "../errors-BqdUDja_.mjs";
|
|
3
|
+
import { _ as requireTeamMembership, m as requireOrgRole, p as requireOrgMembership } from "../permissions-wkqRwicB.mjs";
|
|
4
4
|
import { n as extractBetterAuthOpenApi } from "../betterAuthOpenApi--rdY15Ld.mjs";
|
|
5
5
|
import { createHmac, randomUUID, timingSafeEqual } from "node:crypto";
|
|
6
6
|
import fp from "fastify-plugin";
|
package/dist/cache/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { a as CacheEnvelope, c as QueryCache, i as queryCachePlugin, l as QueryCacheConfig, n as QueryCacheDefaults, o as CacheResult, r as QueryCachePluginOptions, s as CacheStatus, t as CrossResourceRule } from "../queryCachePlugin-
|
|
1
|
+
import { n as CacheStats, r as CacheStore, t as CacheLogger } from "../interface-yhyb_pLY.mjs";
|
|
2
|
+
import { a as CacheEnvelope, c as QueryCache, i as queryCachePlugin, l as QueryCacheConfig, n as QueryCacheDefaults, o as CacheResult, r as QueryCachePluginOptions, s as CacheStatus, t as CrossResourceRule } from "../queryCachePlugin-Dumka73q.mjs";
|
|
3
3
|
|
|
4
4
|
//#region src/cache/keys.d.ts
|
|
5
5
|
/**
|
|
@@ -24,8 +24,8 @@ declare function hashParams(params: Record<string, unknown>): string;
|
|
|
24
24
|
//#endregion
|
|
25
25
|
//#region src/cache/memory.d.ts
|
|
26
26
|
interface MemoryCacheStoreOptions {
|
|
27
|
-
/** Default TTL in
|
|
28
|
-
|
|
27
|
+
/** Default TTL in seconds (default: 60) */
|
|
28
|
+
defaultTtlSeconds?: number;
|
|
29
29
|
/** Hard upper bound for entries (default: 1000) */
|
|
30
30
|
maxEntries?: number;
|
|
31
31
|
/** Background cleanup interval in milliseconds (default: 30_000) */
|
|
@@ -59,7 +59,7 @@ interface MemoryCacheStoreOptions {
|
|
|
59
59
|
declare class MemoryCacheStore<TValue = unknown> implements CacheStore<TValue> {
|
|
60
60
|
readonly name = "memory-cache";
|
|
61
61
|
private readonly cache;
|
|
62
|
-
private readonly
|
|
62
|
+
private readonly defaultTtlSeconds;
|
|
63
63
|
private readonly maxEntries;
|
|
64
64
|
private readonly maxEntryBytes;
|
|
65
65
|
private readonly maxMemoryBytes;
|
|
@@ -72,9 +72,9 @@ declare class MemoryCacheStore<TValue = unknown> implements CacheStore<TValue> {
|
|
|
72
72
|
private _evictions;
|
|
73
73
|
constructor(options?: MemoryCacheStoreOptions);
|
|
74
74
|
get(key: string): Promise<TValue | undefined>;
|
|
75
|
-
set(key: string, value: TValue,
|
|
75
|
+
set(key: string, value: TValue, ttlSeconds?: number): Promise<void>;
|
|
76
76
|
delete(key: string): Promise<void>;
|
|
77
|
-
clear(): Promise<void>;
|
|
77
|
+
clear(pattern?: string): Promise<void>;
|
|
78
78
|
close(): Promise<void>;
|
|
79
79
|
stats(): CacheStats;
|
|
80
80
|
private removeEntry;
|
|
@@ -112,8 +112,8 @@ interface RedisCacheStoreOptions {
|
|
|
112
112
|
client: RedisCacheClient;
|
|
113
113
|
/** Key prefix for namespacing (default: 'arc:cache:') */
|
|
114
114
|
prefix?: string;
|
|
115
|
-
/** Default TTL in
|
|
116
|
-
|
|
115
|
+
/** Default TTL in seconds (default: 60) */
|
|
116
|
+
defaultTtlSeconds?: number;
|
|
117
117
|
/** Maximum serialized entry size in bytes. Oversized entries are skipped. */
|
|
118
118
|
maxEntryBytes?: number;
|
|
119
119
|
}
|
|
@@ -126,17 +126,19 @@ declare class RedisCacheStore<TValue = unknown> implements CacheStore<TValue> {
|
|
|
126
126
|
readonly name = "redis-cache";
|
|
127
127
|
private readonly client;
|
|
128
128
|
private readonly prefix;
|
|
129
|
-
private readonly
|
|
129
|
+
private readonly defaultTtlSeconds;
|
|
130
130
|
private readonly maxEntryBytes;
|
|
131
131
|
private _hits;
|
|
132
132
|
private _misses;
|
|
133
133
|
constructor(options: RedisCacheStoreOptions);
|
|
134
134
|
get(key: string): Promise<TValue | undefined>;
|
|
135
|
-
set(key: string, value: TValue,
|
|
135
|
+
set(key: string, value: TValue, ttlSeconds?: number): Promise<void>;
|
|
136
136
|
delete(key: string): Promise<void>;
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
137
|
+
/**
|
|
138
|
+
* Invalidate keys. Pass a glob pattern to delete a subset (`user:*:v2`);
|
|
139
|
+
* omit to clear every key under this store's prefix.
|
|
140
|
+
*/
|
|
141
|
+
clear(pattern?: string): Promise<void>;
|
|
140
142
|
stats(): CacheStats;
|
|
141
143
|
private scanAndDelete;
|
|
142
144
|
private withPrefix;
|
|
@@ -212,4 +214,4 @@ interface UpstashRedisLike {
|
|
|
212
214
|
*/
|
|
213
215
|
declare function upstashAsCacheClient(client: UpstashRedisLike): RedisCacheClient;
|
|
214
216
|
//#endregion
|
|
215
|
-
export { type CacheEnvelope, type CacheLogger, type CacheResult, type
|
|
217
|
+
export { type CacheEnvelope, type CacheLogger, type CacheResult, type CacheStats, type CacheStatus, type CacheStore, type CrossResourceRule, type IoredisLike, MemoryCacheStore, type MemoryCacheStoreOptions, QueryCache, type QueryCacheConfig, type QueryCacheDefaults, type QueryCachePluginOptions, type RedisCacheClient, RedisCacheStore, type RedisCacheStoreOptions, type RedisPipeline, type UpstashRedisLike, buildQueryKey, hashParams, ioredisAsCacheClient, queryCachePlugin, tagVersionKey, upstashAsCacheClient, versionKey };
|