@voltro/cms 0.50.1 → 0.52.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +231 -0
- package/THIRD-PARTY-NOTICES.md +4284 -5
- package/dist/index.d.ts +21 -0
- package/dist/index.js +99 -82
- package/dist/web.d.ts +32 -0
- package/package.json +4 -3
package/dist/index.d.ts
CHANGED
|
@@ -168,6 +168,9 @@ export declare interface ContentType {
|
|
|
168
168
|
readonly fields: Readonly<Record<string, FieldSchema>>;
|
|
169
169
|
readonly list?: ListConfig;
|
|
170
170
|
readonly idPrefix?: string;
|
|
171
|
+
/** Declared ISR revalidations fired on publish/unpublish — see
|
|
172
|
+
* `ContentTypeSpec.revalidate`. */
|
|
173
|
+
readonly revalidate?: ContentTypeSpec['revalidate'];
|
|
171
174
|
/** Field names whose value is computed-on-save (derivedFrom). */
|
|
172
175
|
readonly derivedFields: ReadonlyArray<string>;
|
|
173
176
|
/** Field names marked unique-per-tenant (composite `(tenantId, field)` UNIQUE). */
|
|
@@ -183,6 +186,24 @@ export declare interface ContentTypeSpec {
|
|
|
183
186
|
readonly list?: ListConfig;
|
|
184
187
|
/** Override the TypeID prefix for awkward plurals / long names. */
|
|
185
188
|
readonly idPrefix?: string;
|
|
189
|
+
/**
|
|
190
|
+
* ISR routes to drop when content of this type is published or
|
|
191
|
+
* unpublished — `publish()`/`unpublish()` call `revalidatePath` /
|
|
192
|
+
* `revalidateTag` (from `@voltro/runtime`) for each entry after the
|
|
193
|
+
* write commits, reaching every `voltro start` replica.
|
|
194
|
+
*
|
|
195
|
+
* You DON'T need this on postgres for the plain publish case: a route
|
|
196
|
+
* declaring `cacheInvalidatesOn: ['<type>_published']` is already dropped
|
|
197
|
+
* by CDC when the published table changes. Declare routes here for the
|
|
198
|
+
* cases CDC can't cover — non-postgres dialects, pattern purges of routes
|
|
199
|
+
* whose loader reads this content indirectly, or tag fanout.
|
|
200
|
+
*/
|
|
201
|
+
readonly revalidate?: {
|
|
202
|
+
/** Route patterns (`/blog/[slug]`) or concrete paths (`/pricing`). */
|
|
203
|
+
readonly paths?: ReadonlyArray<string>;
|
|
204
|
+
/** Tags matched against routes' `cacheInvalidatesOn` entries. */
|
|
205
|
+
readonly tags?: ReadonlyArray<string>;
|
|
206
|
+
};
|
|
186
207
|
}
|
|
187
208
|
|
|
188
209
|
/**
|
package/dist/index.js
CHANGED
|
@@ -3,6 +3,7 @@ import { and as f, boolean as p, date as m, eq as h, gt as ne, gte as re, id as
|
|
|
3
3
|
import { tenant as se } from "@voltro/plugin-multitenancy/mixin";
|
|
4
4
|
import { createHmac as S, timingSafeEqual as ce } from "node:crypto";
|
|
5
5
|
import { Effect as C } from "effect";
|
|
6
|
+
import { revalidatePath as le, revalidateTag as ue } from "@voltro/runtime";
|
|
6
7
|
//#region src/contentType.ts
|
|
7
8
|
var w = [
|
|
8
9
|
"draft",
|
|
@@ -17,7 +18,7 @@ var w = [
|
|
|
17
18
|
"updatedAt",
|
|
18
19
|
"createdBy",
|
|
19
20
|
"updatedBy"
|
|
20
|
-
],
|
|
21
|
+
], de = (e) => {
|
|
21
22
|
let t = {}, n = [], r = [];
|
|
22
23
|
for (let [i, a] of Object.entries(e.fields)) {
|
|
23
24
|
if (T.includes(i)) throw Error(`defineContentType('${e.name}'): field '${i}' collides with a reserved lifecycle column (${T.join(", ")}). Rename it.`);
|
|
@@ -38,10 +39,11 @@ var w = [
|
|
|
38
39
|
fields: t,
|
|
39
40
|
...e.list ? { list: e.list } : {},
|
|
40
41
|
...e.idPrefix ? { idPrefix: e.idPrefix } : {},
|
|
42
|
+
...e.revalidate ? { revalidate: e.revalidate } : {},
|
|
41
43
|
derivedFields: n,
|
|
42
44
|
uniqueFields: r
|
|
43
45
|
};
|
|
44
|
-
},
|
|
46
|
+
}, fe = (e) => {
|
|
45
47
|
switch (e.kind) {
|
|
46
48
|
case "string": return e.isOptional ? b().nullable() : b();
|
|
47
49
|
case "number": return e.isOptional ? _().nullable() : _();
|
|
@@ -61,40 +63,40 @@ var w = [
|
|
|
61
63
|
case "media":
|
|
62
64
|
case "reference": return e.isOptional ? b().nullable() : b();
|
|
63
65
|
}
|
|
64
|
-
},
|
|
66
|
+
}, pe = (e) => {
|
|
65
67
|
let t = {};
|
|
66
|
-
for (let [n, r] of Object.entries(e.fields)) t[n] =
|
|
68
|
+
for (let [n, r] of Object.entries(e.fields)) t[n] = fe(r);
|
|
67
69
|
return t;
|
|
68
|
-
},
|
|
69
|
-
let n =
|
|
70
|
+
}, me = (e) => e.idPrefix, E = (e, t) => {
|
|
71
|
+
let n = me(e), r = n ? g({ prefix: n }) : g(), i = oe(`${e.name}_${t}`, {
|
|
70
72
|
id: r,
|
|
71
73
|
status: b().oneOf(w),
|
|
72
74
|
...t === "drafts" ? { publishAt: x().nullable() } : {},
|
|
73
|
-
...
|
|
75
|
+
...pe(e)
|
|
74
76
|
}).with(se());
|
|
75
77
|
return e.uniqueFields.reduce((e, t) => e.unique(`uq_${t}`, ["tenantId", t]), i);
|
|
76
|
-
},
|
|
77
|
-
draft:
|
|
78
|
-
published:
|
|
79
|
-
}),
|
|
80
|
-
let r =
|
|
78
|
+
}, he = (e) => ({
|
|
79
|
+
draft: E(e, "drafts"),
|
|
80
|
+
published: E(e, "published")
|
|
81
|
+
}), ge = 3600, D = (e) => (Buffer.isBuffer(e) ? e : Buffer.from(e, "utf8")).toString("base64").replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, ""), O = (e) => Buffer.from(e.replace(/-/g, "+").replace(/_/g, "/"), "base64"), k = (e) => e ?? process.env.VOLTRO_SESSION_SECRET ?? null, _e = (e, t, n = {}) => {
|
|
82
|
+
let r = k(n.secret);
|
|
81
83
|
if (r === null) throw Error("cms previewToken requires a signing secret — set VOLTRO_SESSION_SECRET or pass { secret }. Refusing to sign with a hardcoded default (it would ship in the bundle and be forgeable).");
|
|
82
84
|
let i = {
|
|
83
85
|
t: e,
|
|
84
86
|
i: t,
|
|
85
|
-
exp: Math.floor(Date.now() / 1e3) + (n.ttlSeconds ??
|
|
86
|
-
}, a =
|
|
87
|
-
return `${a}.${
|
|
88
|
-
},
|
|
87
|
+
exp: Math.floor(Date.now() / 1e3) + (n.ttlSeconds ?? ge)
|
|
88
|
+
}, a = D(JSON.stringify(i));
|
|
89
|
+
return `${a}.${D(S("sha256", r).update(a).digest())}`;
|
|
90
|
+
}, A = (e, t = {}) => {
|
|
89
91
|
if (!e) return null;
|
|
90
|
-
let n =
|
|
92
|
+
let n = k(t.secret);
|
|
91
93
|
if (n === null) return null;
|
|
92
94
|
let r = e.indexOf(".");
|
|
93
95
|
if (r <= 0 || r === e.length - 1) return null;
|
|
94
|
-
let i = e.slice(0, r), a = e.slice(r + 1), o = S("sha256", n).update(i).digest(), s =
|
|
96
|
+
let i = e.slice(0, r), a = e.slice(r + 1), o = S("sha256", n).update(i).digest(), s = O(a);
|
|
95
97
|
if (s.length !== o.length || !ce(s, o)) return null;
|
|
96
98
|
try {
|
|
97
|
-
let e = JSON.parse(
|
|
99
|
+
let e = JSON.parse(O(i).toString("utf8"));
|
|
98
100
|
return typeof e.t != "string" || typeof e.i != "string" || typeof e.exp != "number" || e.exp < Math.floor(Date.now() / 1e3) ? null : {
|
|
99
101
|
type: e.t,
|
|
100
102
|
id: e.i
|
|
@@ -102,7 +104,7 @@ var w = [
|
|
|
102
104
|
} catch {
|
|
103
105
|
return null;
|
|
104
106
|
}
|
|
105
|
-
},
|
|
107
|
+
}, ve = (e, t, n) => {
|
|
106
108
|
switch (t) {
|
|
107
109
|
case "=": return h(e, n);
|
|
108
110
|
case "!=": return y(e, n);
|
|
@@ -111,7 +113,7 @@ var w = [
|
|
|
111
113
|
case ">": return ne(e, n);
|
|
112
114
|
case ">=": return re(e, n);
|
|
113
115
|
}
|
|
114
|
-
},
|
|
116
|
+
}, j = (e) => {
|
|
115
117
|
let t = e.excludeArchived ? [...e.predicates, y("status", "archived")] : e.predicates, n = t.length === 0 ? void 0 : t.length === 1 ? t[0] : f(...t);
|
|
116
118
|
return {
|
|
117
119
|
table: e.table,
|
|
@@ -121,15 +123,15 @@ var w = [
|
|
|
121
123
|
skip: e.skip,
|
|
122
124
|
projection: void 0
|
|
123
125
|
};
|
|
124
|
-
},
|
|
126
|
+
}, M = (e, t) => ({
|
|
125
127
|
where(n, r, i) {
|
|
126
|
-
return
|
|
128
|
+
return M(e, {
|
|
127
129
|
...t,
|
|
128
|
-
predicates: [...t.predicates,
|
|
130
|
+
predicates: [...t.predicates, ve(n, r, i)]
|
|
129
131
|
});
|
|
130
132
|
},
|
|
131
133
|
orderBy(n, r = "asc") {
|
|
132
|
-
return
|
|
134
|
+
return M(e, {
|
|
133
135
|
...t,
|
|
134
136
|
order: [...t.order, {
|
|
135
137
|
column: n,
|
|
@@ -138,27 +140,27 @@ var w = [
|
|
|
138
140
|
});
|
|
139
141
|
},
|
|
140
142
|
limit(n) {
|
|
141
|
-
return
|
|
143
|
+
return M(e, {
|
|
142
144
|
...t,
|
|
143
145
|
take: n
|
|
144
146
|
});
|
|
145
147
|
},
|
|
146
148
|
offset(n) {
|
|
147
|
-
return
|
|
149
|
+
return M(e, {
|
|
148
150
|
...t,
|
|
149
151
|
skip: n
|
|
150
152
|
});
|
|
151
153
|
},
|
|
152
154
|
async all() {
|
|
153
|
-
return e.query(
|
|
155
|
+
return e.query(j(t));
|
|
154
156
|
},
|
|
155
157
|
async one() {
|
|
156
|
-
return (await e.query(
|
|
158
|
+
return (await e.query(j({
|
|
157
159
|
...t,
|
|
158
160
|
take: 1
|
|
159
161
|
})))[0] ?? null;
|
|
160
162
|
}
|
|
161
|
-
}),
|
|
163
|
+
}), N = (e, t, n) => {
|
|
162
164
|
let r = new Map(t.map((e) => [e.name, e])), i = (e) => {
|
|
163
165
|
let t = r.get(e);
|
|
164
166
|
if (!t) throw Error(`cms.contentType('${e}'): unknown content type. Register it via defineContentType and pass it to cmsContext / createCmsClient.`);
|
|
@@ -166,7 +168,7 @@ var w = [
|
|
|
166
168
|
};
|
|
167
169
|
return {
|
|
168
170
|
contentType(t) {
|
|
169
|
-
return i(t),
|
|
171
|
+
return i(t), M(e, {
|
|
170
172
|
table: `${t}_published`,
|
|
171
173
|
predicates: [],
|
|
172
174
|
order: [],
|
|
@@ -177,7 +179,7 @@ var w = [
|
|
|
177
179
|
},
|
|
178
180
|
async preview(t, r, a) {
|
|
179
181
|
i(t);
|
|
180
|
-
let o = a ?
|
|
182
|
+
let o = a ? A(a, n ? { secret: n } : {}) : null, s = o !== null && o.type === t && o.id === r ? `${t}_drafts` : `${t}_published`;
|
|
181
183
|
return (await e.query({
|
|
182
184
|
table: s,
|
|
183
185
|
predicate: h("id", r),
|
|
@@ -188,10 +190,10 @@ var w = [
|
|
|
188
190
|
}))[0] ?? null;
|
|
189
191
|
}
|
|
190
192
|
};
|
|
191
|
-
},
|
|
193
|
+
}, P = (e, t, n = {}) => N(e, t, n.secret), F = (e) => N(e.store, e.types, e.secret), ye = (e) => {
|
|
192
194
|
if (e.cms === void 0) throw Error("useCms: `ctx.cms` is not set. Build it with cmsContext(store, types) and attach it to your AppContext (or use createCmsClient at build time).");
|
|
193
195
|
return e.cms;
|
|
194
|
-
},
|
|
196
|
+
}, I = (e) => `${e.name}_drafts`, L = (e) => `${e.name}_published`, R = (e, t, n) => ({
|
|
195
197
|
table: e,
|
|
196
198
|
predicate: h("id", t),
|
|
197
199
|
order: [],
|
|
@@ -199,21 +201,21 @@ var w = [
|
|
|
199
201
|
skip: void 0,
|
|
200
202
|
projection: void 0,
|
|
201
203
|
...n === !0 ? { crossTenant: !0 } : {}
|
|
202
|
-
}),
|
|
204
|
+
}), z = async (e, t, n) => (await e.query(R(t, n)))[0], B = (e, t) => {
|
|
203
205
|
let n = {};
|
|
204
206
|
for (let r of Object.keys(e.fields)) r in t && (n[r] = t[r]);
|
|
205
207
|
return n;
|
|
206
|
-
},
|
|
208
|
+
}, V = async (t, n, r) => {
|
|
207
209
|
let i = u(n, r);
|
|
208
210
|
e(n, i);
|
|
209
|
-
let a =
|
|
211
|
+
let a = I(n), o = B(n, i), s = i.id;
|
|
210
212
|
if (s !== void 0) {
|
|
211
|
-
let e = await
|
|
213
|
+
let e = await z(t, a, s);
|
|
212
214
|
if (e !== void 0) return await t.update(a, s, {
|
|
213
215
|
...o,
|
|
214
216
|
status: "draft"
|
|
215
217
|
}) ?? e;
|
|
216
|
-
if ((await t.query(
|
|
218
|
+
if ((await t.query(R(a, s, !0))).length > 0) throw new c({
|
|
217
219
|
contentType: n.name,
|
|
218
220
|
id: s
|
|
219
221
|
});
|
|
@@ -227,47 +229,62 @@ var w = [
|
|
|
227
229
|
...o,
|
|
228
230
|
status: "draft"
|
|
229
231
|
});
|
|
230
|
-
},
|
|
231
|
-
let
|
|
232
|
-
if (
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
if (o !== void 0) s = await e.update(i, n, {
|
|
239
|
-
...a,
|
|
240
|
-
status: "published"
|
|
241
|
-
}) ?? o;
|
|
242
|
-
else {
|
|
243
|
-
let t = r.tenantId;
|
|
244
|
-
s = await e.insert(i, {
|
|
245
|
-
...a,
|
|
246
|
-
id: n,
|
|
247
|
-
status: "published",
|
|
248
|
-
...t == null ? {} : { tenantId: t }
|
|
232
|
+
}, H = (e) => {
|
|
233
|
+
let t = e.revalidate;
|
|
234
|
+
if (t !== void 0) {
|
|
235
|
+
for (let n of t.paths ?? []) le(n).catch((t) => {
|
|
236
|
+
console.warn(`[voltro cms] revalidatePath('${n}') for '${e.name}' failed:`, t);
|
|
237
|
+
});
|
|
238
|
+
for (let n of t.tags ?? []) ue(n).catch((t) => {
|
|
239
|
+
console.warn(`[voltro cms] revalidateTag('${n}') for '${e.name}' failed:`, t);
|
|
249
240
|
});
|
|
250
241
|
}
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
242
|
+
}, U = async (e, t, n) => {
|
|
243
|
+
let r = await e.transactional(async (e) => {
|
|
244
|
+
let r = await z(e, I(t), n);
|
|
245
|
+
if (r === void 0) throw new d({
|
|
246
|
+
contentType: t.name,
|
|
247
|
+
id: n,
|
|
248
|
+
stage: "draft"
|
|
249
|
+
});
|
|
250
|
+
let i = L(t), a = B(t, r), o = await z(e, i, n), s;
|
|
251
|
+
if (o !== void 0) s = await e.update(i, n, {
|
|
252
|
+
...a,
|
|
253
|
+
status: "published"
|
|
254
|
+
}) ?? o;
|
|
255
|
+
else {
|
|
256
|
+
let t = r.tenantId;
|
|
257
|
+
s = await e.insert(i, {
|
|
258
|
+
...a,
|
|
259
|
+
id: n,
|
|
260
|
+
status: "published",
|
|
261
|
+
...t == null ? {} : { tenantId: t }
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
return await e.update(I(t), n, { status: "published" }), s;
|
|
257
265
|
});
|
|
258
|
-
|
|
259
|
-
}
|
|
260
|
-
|
|
266
|
+
return H(t), r;
|
|
267
|
+
}, W = async (e, t, n) => {
|
|
268
|
+
await e.transactional(async (e) => {
|
|
269
|
+
if (await z(e, L(t), n) === void 0) throw new d({
|
|
270
|
+
contentType: t.name,
|
|
271
|
+
id: n,
|
|
272
|
+
stage: "published"
|
|
273
|
+
});
|
|
274
|
+
await e.delete(L(t), n), await z(e, I(t), n) !== void 0 && await e.update(I(t), n, { status: "draft" });
|
|
275
|
+
}), H(t);
|
|
276
|
+
}, G = async (e, t, n) => e.transactional(async (e) => {
|
|
277
|
+
let r = await z(e, I(t), n), i = await z(e, L(t), n);
|
|
261
278
|
if (r === void 0 && i === void 0) throw new d({
|
|
262
279
|
contentType: t.name,
|
|
263
280
|
id: n,
|
|
264
281
|
stage: "draft"
|
|
265
282
|
});
|
|
266
|
-
r !== void 0 && await e.update(
|
|
283
|
+
r !== void 0 && await e.update(I(t), n, { status: "archived" }), i !== void 0 && await e.update(L(t), n, { status: "archived" });
|
|
267
284
|
}), K = (e, t) => C.tryPromise({
|
|
268
285
|
try: e,
|
|
269
286
|
catch: (e) => e
|
|
270
|
-
}).pipe(C.catchAll((e) => t(e) ? C.fail(e) : C.die(e))),
|
|
287
|
+
}).pipe(C.catchAll((e) => t(e) ? C.fail(e) : C.die(e))), be = (e) => e instanceof t || e instanceof c, q = (e) => e instanceof d, xe = (e, t, n) => K(() => V(e, t, n), be), Se = (e, t, n) => K(() => U(e, t, n), q), Ce = (e, t, n) => K(() => W(e, t, n), q), we = (e, t, n) => K(() => G(e, t, n), q), J = (e) => `${e.name}_drafts`, Y = async (e, t, n, r) => {
|
|
271
288
|
let i = J(t), a = await e.query({
|
|
272
289
|
table: i,
|
|
273
290
|
predicate: h("id", n),
|
|
@@ -282,7 +299,7 @@ var w = [
|
|
|
282
299
|
stage: "draft"
|
|
283
300
|
});
|
|
284
301
|
return await e.update(i, n, { publishAt: r }) ?? a[0];
|
|
285
|
-
},
|
|
302
|
+
}, Te = (e, t) => ({
|
|
286
303
|
table: J(e),
|
|
287
304
|
predicate: f(v("publishAt", t), h("status", "draft")),
|
|
288
305
|
order: [],
|
|
@@ -290,52 +307,52 @@ var w = [
|
|
|
290
307
|
skip: void 0,
|
|
291
308
|
projection: void 0
|
|
292
309
|
}), X = async (e, t, n = /* @__PURE__ */ new Date()) => {
|
|
293
|
-
let r = await e.query(
|
|
310
|
+
let r = await e.query(Te(t, n)), i = [];
|
|
294
311
|
for (let n of r) {
|
|
295
312
|
let r = n.id;
|
|
296
313
|
typeof r == "string" && (await U(e, t, r), await e.update(J(t), r, { publishAt: null }), i.push(r));
|
|
297
314
|
}
|
|
298
315
|
return i;
|
|
299
|
-
},
|
|
316
|
+
}, Z = (e, t, n, r) => C.tryPromise({
|
|
300
317
|
try: () => Y(e, t, n, r),
|
|
301
318
|
catch: (e) => e
|
|
302
|
-
}).pipe(C.catchAll((e) => e instanceof d ? C.fail(e) : C.die(e))),
|
|
319
|
+
}).pipe(C.catchAll((e) => e instanceof d ? C.fail(e) : C.die(e))), Ee = (e, t, n = /* @__PURE__ */ new Date()) => C.promise(() => X(e, t, n)), De = (e) => Object.entries(e.fields).filter(([, e]) => e.kind === "media").map(([e]) => e), Q = async (e, t, n) => {
|
|
303
320
|
if (t == null) return t;
|
|
304
321
|
switch (e.kind) {
|
|
305
322
|
case "media": return typeof t == "string" ? await n(t) ?? t : t;
|
|
306
323
|
case "array": {
|
|
307
324
|
let r = e.element;
|
|
308
|
-
return r === void 0 || !Array.isArray(t) ? t : Promise.all(t.map((e) =>
|
|
325
|
+
return r === void 0 || !Array.isArray(t) ? t : Promise.all(t.map((e) => Q(r, e, n)));
|
|
309
326
|
}
|
|
310
327
|
case "struct": {
|
|
311
328
|
let r = e.struct;
|
|
312
329
|
if (r === void 0 || typeof t != "object" || Array.isArray(t)) return t;
|
|
313
330
|
let i = t, a = { ...i };
|
|
314
|
-
for (let [e, t] of Object.entries(r)) e in i && (a[e] = await
|
|
331
|
+
for (let [e, t] of Object.entries(r)) e in i && (a[e] = await Q(t, i[e], n));
|
|
315
332
|
return a;
|
|
316
333
|
}
|
|
317
334
|
default: return t;
|
|
318
335
|
}
|
|
319
|
-
},
|
|
336
|
+
}, $ = async (e, t, n) => {
|
|
320
337
|
let r = { ...t };
|
|
321
338
|
for (let [i, a] of Object.entries(e.fields)) {
|
|
322
339
|
let e = a;
|
|
323
|
-
(e.kind === "media" || e.kind === "array" || e.kind === "struct") && i in t && (r[i] = await
|
|
340
|
+
(e.kind === "media" || e.kind === "array" || e.kind === "struct") && i in t && (r[i] = await Q(e, t[i], n));
|
|
324
341
|
}
|
|
325
342
|
return r;
|
|
326
|
-
},
|
|
343
|
+
}, Oe = async (e, t, n) => Promise.all(t.map((t) => $(e, t, n))), ke = (e) => De(e), Ae = "/v1/cms/", je = (e) => {
|
|
327
344
|
let t = e.authorization;
|
|
328
345
|
return t && t.toLowerCase().startsWith("bearer ") ? t.slice(7).trim() : e["x-api-key"];
|
|
329
|
-
},
|
|
346
|
+
}, Me = async (e, t) => {
|
|
330
347
|
if (e.method.toUpperCase() !== "GET") return {
|
|
331
348
|
status: 405,
|
|
332
349
|
body: { error: "method_not_allowed" }
|
|
333
350
|
};
|
|
334
|
-
if (!e.path.startsWith(
|
|
351
|
+
if (!e.path.startsWith(Ae)) return {
|
|
335
352
|
status: 404,
|
|
336
353
|
body: { error: "not_found" }
|
|
337
354
|
};
|
|
338
|
-
let n =
|
|
355
|
+
let n = je(e.headers), r = n ? t.apiKeys.find((e) => e.key === n) : void 0;
|
|
339
356
|
if (!r) return {
|
|
340
357
|
status: 401,
|
|
341
358
|
body: { error: "unauthorized" }
|
|
@@ -373,4 +390,4 @@ var w = [
|
|
|
373
390
|
};
|
|
374
391
|
};
|
|
375
392
|
//#endregion
|
|
376
|
-
export { w as CONTENT_STATUSES, ee as ContentForm, c as ContentIdConflict, d as ContentNotFound, t as ContentValidationFailed, a as Schema, u as applyDerivations, G as archive,
|
|
393
|
+
export { w as CONTENT_STATUSES, ee as ContentForm, c as ContentIdConflict, d as ContentNotFound, t as ContentValidationFailed, a as Schema, u as applyDerivations, G as archive, we as archiveEffect, P as cmsContext, he as contentTypeToEntities, i as contentViolations, F as createCmsClient, de as defineContentType, te as derivedFrom, Me as handleCmsRest, n as maxLength, ke as mediaFields, r as pattern, _e as previewToken, U as publish, X as publishDue, Ee as publishDueEffect, Se as publishEffect, $ as resolveMedia, Oe as resolveMediaAll, V as saveDraft, xe as saveDraftEffect, Y as schedulePublish, Z as schedulePublishEffect, l as slugify, o as unique, W as unpublish, Ce as unpublishEffect, ye as useCms, e as validateContent, A as verifyPreviewToken, s as widgetFor };
|
package/dist/web.d.ts
CHANGED
|
@@ -76,12 +76,44 @@ declare interface ContentType {
|
|
|
76
76
|
readonly fields: Readonly<Record<string, FieldSchema>>;
|
|
77
77
|
readonly list?: ListConfig;
|
|
78
78
|
readonly idPrefix?: string;
|
|
79
|
+
/** Declared ISR revalidations fired on publish/unpublish — see
|
|
80
|
+
* `ContentTypeSpec.revalidate`. */
|
|
81
|
+
readonly revalidate?: ContentTypeSpec['revalidate'];
|
|
79
82
|
/** Field names whose value is computed-on-save (derivedFrom). */
|
|
80
83
|
readonly derivedFields: ReadonlyArray<string>;
|
|
81
84
|
/** Field names marked unique-per-tenant (composite `(tenantId, field)` UNIQUE). */
|
|
82
85
|
readonly uniqueFields: ReadonlyArray<string>;
|
|
83
86
|
}
|
|
84
87
|
|
|
88
|
+
/** The user-authored spec passed to `defineContentType`. */
|
|
89
|
+
declare interface ContentTypeSpec {
|
|
90
|
+
readonly name: string;
|
|
91
|
+
readonly displayName: string;
|
|
92
|
+
readonly pluralName: string;
|
|
93
|
+
readonly fields: Readonly<Record<string, FieldSchema | PipeableField>>;
|
|
94
|
+
readonly list?: ListConfig;
|
|
95
|
+
/** Override the TypeID prefix for awkward plurals / long names. */
|
|
96
|
+
readonly idPrefix?: string;
|
|
97
|
+
/**
|
|
98
|
+
* ISR routes to drop when content of this type is published or
|
|
99
|
+
* unpublished — `publish()`/`unpublish()` call `revalidatePath` /
|
|
100
|
+
* `revalidateTag` (from `@voltro/runtime`) for each entry after the
|
|
101
|
+
* write commits, reaching every `voltro start` replica.
|
|
102
|
+
*
|
|
103
|
+
* You DON'T need this on postgres for the plain publish case: a route
|
|
104
|
+
* declaring `cacheInvalidatesOn: ['<type>_published']` is already dropped
|
|
105
|
+
* by CDC when the published table changes. Declare routes here for the
|
|
106
|
+
* cases CDC can't cover — non-postgres dialects, pattern purges of routes
|
|
107
|
+
* whose loader reads this content indirectly, or tag fanout.
|
|
108
|
+
*/
|
|
109
|
+
readonly revalidate?: {
|
|
110
|
+
/** Route patterns (`/blog/[slug]`) or concrete paths (`/pricing`). */
|
|
111
|
+
readonly paths?: ReadonlyArray<string>;
|
|
112
|
+
/** Tags matched against routes' `cacheInvalidatesOn` entries. */
|
|
113
|
+
readonly tags?: ReadonlyArray<string>;
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
|
|
85
117
|
/**
|
|
86
118
|
* Raised by `validateContent` / `saveDraft` when a row violates the
|
|
87
119
|
* content type's field rules (pattern / maxLength / typed kinds /
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@voltro/cms",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.52.0",
|
|
4
4
|
"description": "Headless CMS — typed content-type schemas in code (defineContentType + the Schema field namespace), draft/published table derivation (contentTypeToEntities), the ctx.cms typed read surface, the write pipeline (saveDraft/publish/unpublish/archive with save-time validation + derivation), a REST mount (GET /v1/cms/<type>), signed preview tokens, and a browser-safe schema→widget <ContentForm> renderer.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"voltro",
|
|
@@ -38,8 +38,9 @@
|
|
|
38
38
|
"node": ">=24.0.0"
|
|
39
39
|
},
|
|
40
40
|
"dependencies": {
|
|
41
|
-
"@voltro/database": "0.
|
|
42
|
-
"@voltro/plugin-multitenancy": "0.
|
|
41
|
+
"@voltro/database": "0.52.0",
|
|
42
|
+
"@voltro/plugin-multitenancy": "0.52.0",
|
|
43
|
+
"@voltro/runtime": "0.52.0"
|
|
43
44
|
},
|
|
44
45
|
"peerDependencies": {
|
|
45
46
|
"effect": "^3.22.0",
|