canopycms 0.0.66-int.85 → 0.0.66
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/dist/assets/factory.js +4 -0
- package/dist/assets/store-s3.d.ts +20 -0
- package/dist/assets/store-s3.js +23 -1
- package/dist/cli/cli.js +512 -274
- package/dist/cli/generate-ai-content.js +143 -57
- package/dist/cli/init.js +140 -57
- package/dist/cli/template-files/cms-stack.ts.template +7 -4
- package/dist/config/schemas/config.d.ts +11 -6
- package/dist/config/schemas/media.d.ts +23 -6
- package/dist/config/schemas/media.js +38 -9
- package/dist/config/schemas/url.d.ts +38 -0
- package/dist/config/schemas/url.js +49 -0
- package/dist/config/types.d.ts +2 -0
- package/dist/editor/context/AssetContext.d.ts +12 -7
- package/dist/editor/context/AssetContext.js +12 -7
- package/dist/utils/sanitize-href.d.ts +41 -0
- package/dist/utils/sanitize-href.js +116 -0
- package/package.json +26 -14
|
@@ -23,6 +23,57 @@ var init_logger = __esm({
|
|
|
23
23
|
}
|
|
24
24
|
});
|
|
25
25
|
|
|
26
|
+
// dist/utils/sanitize-href.js
|
|
27
|
+
function declaresScheme(url) {
|
|
28
|
+
return /^[a-z][a-z0-9+.-]*:/i.test(url.trim());
|
|
29
|
+
}
|
|
30
|
+
function isImplicitlyOffOrigin(url) {
|
|
31
|
+
if (declaresScheme(url))
|
|
32
|
+
return false;
|
|
33
|
+
try {
|
|
34
|
+
return new URL(url, SENTINEL_BASE).origin !== SENTINEL_BASE;
|
|
35
|
+
} catch {
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
function isHttpUrlOrSameOriginPath(value, opts = {}) {
|
|
40
|
+
if (value === "")
|
|
41
|
+
return false;
|
|
42
|
+
for (let index = 0; index < value.length; index++) {
|
|
43
|
+
const code = value.charCodeAt(index);
|
|
44
|
+
if (code <= 32 || code === 127)
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
if (value.includes("?") || value.includes("#"))
|
|
48
|
+
return false;
|
|
49
|
+
if (value.includes("\\"))
|
|
50
|
+
return false;
|
|
51
|
+
if (DOT_SEGMENT.test(value))
|
|
52
|
+
return false;
|
|
53
|
+
if (declaresScheme(value)) {
|
|
54
|
+
if (!/^https?:\/\//i.test(value))
|
|
55
|
+
return false;
|
|
56
|
+
try {
|
|
57
|
+
new URL(value);
|
|
58
|
+
return true;
|
|
59
|
+
} catch {
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
if (isImplicitlyOffOrigin(value)) {
|
|
64
|
+
return opts.allowProtocolRelative === true && value.startsWith("//") && value[2] !== "/";
|
|
65
|
+
}
|
|
66
|
+
return value.startsWith("/") && value[1] !== "/";
|
|
67
|
+
}
|
|
68
|
+
var SENTINEL_BASE, DOT_SEGMENT;
|
|
69
|
+
var init_sanitize_href = __esm({
|
|
70
|
+
"dist/utils/sanitize-href.js"() {
|
|
71
|
+
"use strict";
|
|
72
|
+
SENTINEL_BASE = "https://relative.invalid";
|
|
73
|
+
DOT_SEGMENT = /(^|\/)(\.|%2e){1,2}(\/|$)/i;
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
|
|
26
77
|
// dist/paths/normalize.js
|
|
27
78
|
function normalizeFilesystemPath(path22) {
|
|
28
79
|
return path22.split(/[\\/]+/).filter(Boolean).join("/");
|
|
@@ -322,82 +373,114 @@ var init_collection = __esm({
|
|
|
322
373
|
}
|
|
323
374
|
});
|
|
324
375
|
|
|
325
|
-
// dist/config/schemas/
|
|
376
|
+
// dist/config/schemas/url.js
|
|
326
377
|
import { z as z4 } from "zod";
|
|
378
|
+
var uploadTargetUrlSchema, assetMountUrlSchema;
|
|
379
|
+
var init_url = __esm({
|
|
380
|
+
"dist/config/schemas/url.js"() {
|
|
381
|
+
"use strict";
|
|
382
|
+
init_sanitize_href();
|
|
383
|
+
uploadTargetUrlSchema = z4.string().trim().refine((value) => isHttpUrlOrSameOriginPath(value), {
|
|
384
|
+
message: 'must be an absolute http(s) URL or a site-relative path beginning with a single "/", with no query or fragment'
|
|
385
|
+
});
|
|
386
|
+
assetMountUrlSchema = z4.string().trim().refine((value) => isHttpUrlOrSameOriginPath(value, { allowProtocolRelative: true }), {
|
|
387
|
+
message: 'must be an absolute http(s) URL, a protocol-relative "//host" URL, or a site-relative path beginning with a single "/", with no query or fragment'
|
|
388
|
+
});
|
|
389
|
+
}
|
|
390
|
+
});
|
|
391
|
+
|
|
392
|
+
// dist/config/schemas/media.js
|
|
393
|
+
import { z as z5 } from "zod";
|
|
327
394
|
var mediaSchema;
|
|
328
395
|
var init_media = __esm({
|
|
329
396
|
"dist/config/schemas/media.js"() {
|
|
330
397
|
"use strict";
|
|
331
398
|
init_collection();
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
399
|
+
init_url();
|
|
400
|
+
mediaSchema = z5.discriminatedUnion("adapter", [
|
|
401
|
+
z5.object({
|
|
402
|
+
adapter: z5.literal("local"),
|
|
403
|
+
publicBaseUrl: assetMountUrlSchema.optional(),
|
|
336
404
|
/** Root directory for local asset storage. Defaults to the caller's dev-assets dir. */
|
|
337
405
|
directory: relativePathSchema.optional()
|
|
338
|
-
}),
|
|
339
|
-
|
|
340
|
-
adapter:
|
|
341
|
-
bucket:
|
|
342
|
-
region:
|
|
343
|
-
publicBaseUrl:
|
|
406
|
+
}).strict(),
|
|
407
|
+
z5.object({
|
|
408
|
+
adapter: z5.literal("s3"),
|
|
409
|
+
bucket: z5.string().min(1),
|
|
410
|
+
region: z5.string().min(1),
|
|
411
|
+
publicBaseUrl: assetMountUrlSchema.optional(),
|
|
412
|
+
/**
|
|
413
|
+
* Where the browser POSTs a presigned direct upload. Absolute http(s) URL or a
|
|
414
|
+
* site-relative path; defaults to the S3 REST endpoint the AWS SDK returns.
|
|
415
|
+
*
|
|
416
|
+
* Set this to route uploads through your own CDN so they are same-origin with the
|
|
417
|
+
* editor, which removes the need for a bucket CORS rule naming an exact origin. The
|
|
418
|
+
* signature is unaffected — a presigned POST's string-to-sign is the base64 policy
|
|
419
|
+
* alone, so the host never enters it (pinned by store-s3.test.ts).
|
|
420
|
+
*
|
|
421
|
+
* NOT a prefix. Unlike `publicBaseUrl`, nothing is joined onto this value; it replaces
|
|
422
|
+
* the POST target outright and is passed through byte-for-byte. Do not route it through
|
|
423
|
+
* `joinUrlPrefix` "for consistency" — that would strip the trailing slash and silently
|
|
424
|
+
* stop a CDN path pattern like `/asset-upload/*` from matching.
|
|
425
|
+
*/
|
|
426
|
+
uploadUrl: uploadTargetUrlSchema.optional(),
|
|
344
427
|
/** Max upload size in bytes for presigned direct uploads. Defaults to 50 MiB. */
|
|
345
|
-
maxUploadBytes:
|
|
346
|
-
}),
|
|
347
|
-
|
|
348
|
-
adapter:
|
|
349
|
-
publicBaseUrl:
|
|
350
|
-
})
|
|
428
|
+
maxUploadBytes: z5.number().int().positive().optional()
|
|
429
|
+
}).strict(),
|
|
430
|
+
z5.object({
|
|
431
|
+
adapter: z5.literal("lfs"),
|
|
432
|
+
publicBaseUrl: assetMountUrlSchema.optional()
|
|
433
|
+
}).strict()
|
|
351
434
|
]);
|
|
352
435
|
}
|
|
353
436
|
});
|
|
354
437
|
|
|
355
438
|
// dist/config/schemas/config.js
|
|
356
|
-
import { z as
|
|
439
|
+
import { z as z6 } from "zod";
|
|
357
440
|
var defaultBranchAccessSchema, defaultPathAccessLevelSchema, defaultPathAccessLevelsSchema, defaultPathAccessSchema, defaultBaseBranchSchema, defaultRemoteNameSchema, defaultRemoteUrlSchema, gitBotAuthorNameSchema, gitBotAuthorEmailSchema, githubTokenEnvVarSchema, operatingModeSchema, deployedAsSchema, contentRootSchema, sourceRootSchema, basePathSchema, deploymentNameSchema, devContentSyncSchema, devConfigSchema, editorConfigSchema, CanopyConfigSchema, DEFAULT_PROD_WORKSPACE;
|
|
358
441
|
var init_config = __esm({
|
|
359
442
|
"dist/config/schemas/config.js"() {
|
|
360
443
|
"use strict";
|
|
361
444
|
init_collection();
|
|
362
445
|
init_media();
|
|
363
|
-
defaultBranchAccessSchema =
|
|
364
|
-
defaultPathAccessLevelSchema =
|
|
365
|
-
defaultPathAccessLevelsSchema =
|
|
446
|
+
defaultBranchAccessSchema = z6.enum(["allow", "deny"]).default("deny");
|
|
447
|
+
defaultPathAccessLevelSchema = z6.enum(["allow", "deny"]);
|
|
448
|
+
defaultPathAccessLevelsSchema = z6.object({
|
|
366
449
|
read: defaultPathAccessLevelSchema.optional(),
|
|
367
450
|
edit: defaultPathAccessLevelSchema.optional(),
|
|
368
451
|
review: defaultPathAccessLevelSchema.optional()
|
|
369
452
|
}).strict();
|
|
370
|
-
defaultPathAccessSchema =
|
|
371
|
-
defaultBaseBranchSchema =
|
|
372
|
-
defaultRemoteNameSchema =
|
|
373
|
-
defaultRemoteUrlSchema =
|
|
374
|
-
gitBotAuthorNameSchema =
|
|
375
|
-
gitBotAuthorEmailSchema =
|
|
376
|
-
githubTokenEnvVarSchema =
|
|
377
|
-
operatingModeSchema =
|
|
378
|
-
deployedAsSchema =
|
|
453
|
+
defaultPathAccessSchema = z6.union([defaultPathAccessLevelSchema, defaultPathAccessLevelsSchema]).default("deny");
|
|
454
|
+
defaultBaseBranchSchema = z6.string().default("main");
|
|
455
|
+
defaultRemoteNameSchema = z6.string().default("origin");
|
|
456
|
+
defaultRemoteUrlSchema = z6.string().min(1);
|
|
457
|
+
gitBotAuthorNameSchema = z6.string().min(1);
|
|
458
|
+
gitBotAuthorEmailSchema = z6.string().email();
|
|
459
|
+
githubTokenEnvVarSchema = z6.string().default("GITHUB_BOT_TOKEN");
|
|
460
|
+
operatingModeSchema = z6.enum(["prod", "dev"]);
|
|
461
|
+
deployedAsSchema = z6.enum(["static", "server"]).default("server");
|
|
379
462
|
contentRootSchema = relativePathSchema.default("content");
|
|
380
|
-
sourceRootSchema =
|
|
381
|
-
basePathSchema =
|
|
463
|
+
sourceRootSchema = z6.string().min(1).optional();
|
|
464
|
+
basePathSchema = z6.string().transform((value) => value.trim()).refine((value) => value.length > 0, {
|
|
382
465
|
message: "basePath must not be empty or only whitespace"
|
|
383
466
|
}).optional();
|
|
384
|
-
deploymentNameSchema =
|
|
385
|
-
devContentSyncSchema =
|
|
386
|
-
devConfigSchema =
|
|
467
|
+
deploymentNameSchema = z6.string().default("prod");
|
|
468
|
+
devContentSyncSchema = z6.enum(["off", "warn"]).default("warn");
|
|
469
|
+
devConfigSchema = z6.object({
|
|
387
470
|
contentSync: devContentSyncSchema.optional()
|
|
388
471
|
});
|
|
389
|
-
editorConfigSchema =
|
|
390
|
-
title:
|
|
391
|
-
subtitle:
|
|
392
|
-
theme:
|
|
393
|
-
previewBase:
|
|
472
|
+
editorConfigSchema = z6.object({
|
|
473
|
+
title: z6.string().optional(),
|
|
474
|
+
subtitle: z6.string().optional(),
|
|
475
|
+
theme: z6.unknown().optional(),
|
|
476
|
+
previewBase: z6.record(z6.string()).optional(),
|
|
394
477
|
// UI handler functions (runtime only, don't serialize)
|
|
395
|
-
onAccountClick:
|
|
396
|
-
onLogoutClick:
|
|
478
|
+
onAccountClick: z6.function().returns(z6.void()).optional(),
|
|
479
|
+
onLogoutClick: z6.function().returns(z6.void()).optional(),
|
|
397
480
|
// Optional: custom account component (e.g., Clerk's UserButton)
|
|
398
|
-
AccountComponent:
|
|
481
|
+
AccountComponent: z6.custom().optional()
|
|
399
482
|
});
|
|
400
|
-
CanopyConfigSchema =
|
|
483
|
+
CanopyConfigSchema = z6.object({
|
|
401
484
|
media: mediaSchema.optional(),
|
|
402
485
|
// No outer .optional() here: defaultBranchAccessSchema/defaultPathAccessSchema already
|
|
403
486
|
// have .default('deny'), so the field is optional on input but always resolves to
|
|
@@ -413,7 +496,7 @@ var init_config = __esm({
|
|
|
413
496
|
// unset must stay undefined after parsing so dev mode can detect the fork
|
|
414
497
|
// point from git HEAD (see resolveBaseBranch in utils/git.ts).
|
|
415
498
|
defaultBaseBranch: defaultBaseBranchSchema.optional(),
|
|
416
|
-
defaultActiveBranch:
|
|
499
|
+
defaultActiveBranch: z6.string().min(1).optional(),
|
|
417
500
|
defaultRemoteName: defaultRemoteNameSchema.optional(),
|
|
418
501
|
defaultRemoteUrl: defaultRemoteUrlSchema.optional(),
|
|
419
502
|
gitBotAuthorName: gitBotAuthorNameSchema,
|
|
@@ -427,9 +510,9 @@ var init_config = __esm({
|
|
|
427
510
|
// Escape hatch for prod hosts that genuinely have internet access and intentionally
|
|
428
511
|
// run git against a network remote (see GitManager.resolveRemoteUrl's prod-mode guard).
|
|
429
512
|
// Default false/unset — the standard AWS Lambda+worker topology must leave this unset.
|
|
430
|
-
allowNetworkRemoteInProd:
|
|
431
|
-
settingsBranch:
|
|
432
|
-
autoCreateSettingsPR:
|
|
513
|
+
allowNetworkRemoteInProd: z6.boolean().optional(),
|
|
514
|
+
settingsBranch: z6.string().optional(),
|
|
515
|
+
autoCreateSettingsPR: z6.boolean().optional(),
|
|
433
516
|
// Deliberately `deploymentNameSchema.optional()`, NOT `deploymentNameSchema` alone.
|
|
434
517
|
// deploymentNameSchema.default('prod') would make `parse(undefined)` resolve to
|
|
435
518
|
// the literal 'prod' instead of staying `undefined` — collapsing the
|
|
@@ -445,9 +528,9 @@ var init_config = __esm({
|
|
|
445
528
|
sourceRoot: sourceRootSchema.optional(),
|
|
446
529
|
basePath: basePathSchema,
|
|
447
530
|
editor: editorConfigSchema.optional(),
|
|
448
|
-
authPlugin:
|
|
449
|
-
entryLinkUrl:
|
|
450
|
-
validateEntry:
|
|
531
|
+
authPlugin: z6.custom().optional(),
|
|
532
|
+
entryLinkUrl: z6.custom().optional(),
|
|
533
|
+
validateEntry: z6.custom().optional(),
|
|
451
534
|
dev: devConfigSchema.optional()
|
|
452
535
|
}).strict();
|
|
453
536
|
DEFAULT_PROD_WORKSPACE = "/mnt/efs/workspace";
|
|
@@ -638,17 +721,17 @@ var init_deployment_name = __esm({
|
|
|
638
721
|
});
|
|
639
722
|
|
|
640
723
|
// dist/config/schemas/permissions.js
|
|
641
|
-
import { z as
|
|
724
|
+
import { z as z7 } from "zod";
|
|
642
725
|
var permissionTargetSchema, pathPermissionSchema;
|
|
643
726
|
var init_permissions = __esm({
|
|
644
727
|
"dist/config/schemas/permissions.js"() {
|
|
645
728
|
"use strict";
|
|
646
|
-
permissionTargetSchema =
|
|
647
|
-
allowedUsers:
|
|
648
|
-
allowedGroups:
|
|
729
|
+
permissionTargetSchema = z7.object({
|
|
730
|
+
allowedUsers: z7.array(z7.string()).optional(),
|
|
731
|
+
allowedGroups: z7.array(z7.string()).optional()
|
|
649
732
|
});
|
|
650
|
-
pathPermissionSchema =
|
|
651
|
-
path:
|
|
733
|
+
pathPermissionSchema = z7.object({
|
|
734
|
+
path: z7.string().min(1),
|
|
652
735
|
read: permissionTargetSchema.optional(),
|
|
653
736
|
edit: permissionTargetSchema.optional(),
|
|
654
737
|
review: permissionTargetSchema.optional()
|
|
@@ -1257,6 +1340,9 @@ function findBodyFieldName(fields) {
|
|
|
1257
1340
|
return "body";
|
|
1258
1341
|
}
|
|
1259
1342
|
|
|
1343
|
+
// dist/utils/url-prefix.js
|
|
1344
|
+
init_sanitize_href();
|
|
1345
|
+
|
|
1260
1346
|
// dist/static/seo.js
|
|
1261
1347
|
var DEFAULT_SEO_FIELD_NAMES = {
|
|
1262
1348
|
title: "metaTitle",
|
package/dist/cli/init.js
CHANGED
|
@@ -178,82 +178,165 @@ var init_collection = __esm({
|
|
|
178
178
|
}
|
|
179
179
|
});
|
|
180
180
|
|
|
181
|
-
// dist/
|
|
181
|
+
// dist/utils/sanitize-href.js
|
|
182
|
+
function declaresScheme(url) {
|
|
183
|
+
return /^[a-z][a-z0-9+.-]*:/i.test(url.trim());
|
|
184
|
+
}
|
|
185
|
+
function isImplicitlyOffOrigin(url) {
|
|
186
|
+
if (declaresScheme(url))
|
|
187
|
+
return false;
|
|
188
|
+
try {
|
|
189
|
+
return new URL(url, SENTINEL_BASE).origin !== SENTINEL_BASE;
|
|
190
|
+
} catch {
|
|
191
|
+
return false;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
function isHttpUrlOrSameOriginPath(value, opts = {}) {
|
|
195
|
+
if (value === "")
|
|
196
|
+
return false;
|
|
197
|
+
for (let index = 0; index < value.length; index++) {
|
|
198
|
+
const code = value.charCodeAt(index);
|
|
199
|
+
if (code <= 32 || code === 127)
|
|
200
|
+
return false;
|
|
201
|
+
}
|
|
202
|
+
if (value.includes("?") || value.includes("#"))
|
|
203
|
+
return false;
|
|
204
|
+
if (value.includes("\\"))
|
|
205
|
+
return false;
|
|
206
|
+
if (DOT_SEGMENT.test(value))
|
|
207
|
+
return false;
|
|
208
|
+
if (declaresScheme(value)) {
|
|
209
|
+
if (!/^https?:\/\//i.test(value))
|
|
210
|
+
return false;
|
|
211
|
+
try {
|
|
212
|
+
new URL(value);
|
|
213
|
+
return true;
|
|
214
|
+
} catch {
|
|
215
|
+
return false;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
if (isImplicitlyOffOrigin(value)) {
|
|
219
|
+
return opts.allowProtocolRelative === true && value.startsWith("//") && value[2] !== "/";
|
|
220
|
+
}
|
|
221
|
+
return value.startsWith("/") && value[1] !== "/";
|
|
222
|
+
}
|
|
223
|
+
var SENTINEL_BASE, DOT_SEGMENT;
|
|
224
|
+
var init_sanitize_href = __esm({
|
|
225
|
+
"dist/utils/sanitize-href.js"() {
|
|
226
|
+
"use strict";
|
|
227
|
+
SENTINEL_BASE = "https://relative.invalid";
|
|
228
|
+
DOT_SEGMENT = /(^|\/)(\.|%2e){1,2}(\/|$)/i;
|
|
229
|
+
}
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
// dist/config/schemas/url.js
|
|
182
233
|
import { z as z3 } from "zod";
|
|
234
|
+
var uploadTargetUrlSchema, assetMountUrlSchema;
|
|
235
|
+
var init_url = __esm({
|
|
236
|
+
"dist/config/schemas/url.js"() {
|
|
237
|
+
"use strict";
|
|
238
|
+
init_sanitize_href();
|
|
239
|
+
uploadTargetUrlSchema = z3.string().trim().refine((value) => isHttpUrlOrSameOriginPath(value), {
|
|
240
|
+
message: 'must be an absolute http(s) URL or a site-relative path beginning with a single "/", with no query or fragment'
|
|
241
|
+
});
|
|
242
|
+
assetMountUrlSchema = z3.string().trim().refine((value) => isHttpUrlOrSameOriginPath(value, { allowProtocolRelative: true }), {
|
|
243
|
+
message: 'must be an absolute http(s) URL, a protocol-relative "//host" URL, or a site-relative path beginning with a single "/", with no query or fragment'
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
// dist/config/schemas/media.js
|
|
249
|
+
import { z as z4 } from "zod";
|
|
183
250
|
var mediaSchema;
|
|
184
251
|
var init_media = __esm({
|
|
185
252
|
"dist/config/schemas/media.js"() {
|
|
186
253
|
"use strict";
|
|
187
254
|
init_collection();
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
255
|
+
init_url();
|
|
256
|
+
mediaSchema = z4.discriminatedUnion("adapter", [
|
|
257
|
+
z4.object({
|
|
258
|
+
adapter: z4.literal("local"),
|
|
259
|
+
publicBaseUrl: assetMountUrlSchema.optional(),
|
|
192
260
|
/** Root directory for local asset storage. Defaults to the caller's dev-assets dir. */
|
|
193
261
|
directory: relativePathSchema.optional()
|
|
194
|
-
}),
|
|
195
|
-
|
|
196
|
-
adapter:
|
|
197
|
-
bucket:
|
|
198
|
-
region:
|
|
199
|
-
publicBaseUrl:
|
|
262
|
+
}).strict(),
|
|
263
|
+
z4.object({
|
|
264
|
+
adapter: z4.literal("s3"),
|
|
265
|
+
bucket: z4.string().min(1),
|
|
266
|
+
region: z4.string().min(1),
|
|
267
|
+
publicBaseUrl: assetMountUrlSchema.optional(),
|
|
268
|
+
/**
|
|
269
|
+
* Where the browser POSTs a presigned direct upload. Absolute http(s) URL or a
|
|
270
|
+
* site-relative path; defaults to the S3 REST endpoint the AWS SDK returns.
|
|
271
|
+
*
|
|
272
|
+
* Set this to route uploads through your own CDN so they are same-origin with the
|
|
273
|
+
* editor, which removes the need for a bucket CORS rule naming an exact origin. The
|
|
274
|
+
* signature is unaffected — a presigned POST's string-to-sign is the base64 policy
|
|
275
|
+
* alone, so the host never enters it (pinned by store-s3.test.ts).
|
|
276
|
+
*
|
|
277
|
+
* NOT a prefix. Unlike `publicBaseUrl`, nothing is joined onto this value; it replaces
|
|
278
|
+
* the POST target outright and is passed through byte-for-byte. Do not route it through
|
|
279
|
+
* `joinUrlPrefix` "for consistency" — that would strip the trailing slash and silently
|
|
280
|
+
* stop a CDN path pattern like `/asset-upload/*` from matching.
|
|
281
|
+
*/
|
|
282
|
+
uploadUrl: uploadTargetUrlSchema.optional(),
|
|
200
283
|
/** Max upload size in bytes for presigned direct uploads. Defaults to 50 MiB. */
|
|
201
|
-
maxUploadBytes:
|
|
202
|
-
}),
|
|
203
|
-
|
|
204
|
-
adapter:
|
|
205
|
-
publicBaseUrl:
|
|
206
|
-
})
|
|
284
|
+
maxUploadBytes: z4.number().int().positive().optional()
|
|
285
|
+
}).strict(),
|
|
286
|
+
z4.object({
|
|
287
|
+
adapter: z4.literal("lfs"),
|
|
288
|
+
publicBaseUrl: assetMountUrlSchema.optional()
|
|
289
|
+
}).strict()
|
|
207
290
|
]);
|
|
208
291
|
}
|
|
209
292
|
});
|
|
210
293
|
|
|
211
294
|
// dist/config/schemas/config.js
|
|
212
|
-
import { z as
|
|
295
|
+
import { z as z5 } from "zod";
|
|
213
296
|
var defaultBranchAccessSchema, defaultPathAccessLevelSchema, defaultPathAccessLevelsSchema, defaultPathAccessSchema, defaultBaseBranchSchema, defaultRemoteNameSchema, defaultRemoteUrlSchema, gitBotAuthorNameSchema, gitBotAuthorEmailSchema, githubTokenEnvVarSchema, operatingModeSchema, deployedAsSchema, contentRootSchema, sourceRootSchema, basePathSchema, deploymentNameSchema, devContentSyncSchema, devConfigSchema, editorConfigSchema, CanopyConfigSchema, DEFAULT_PROD_WORKSPACE;
|
|
214
297
|
var init_config = __esm({
|
|
215
298
|
"dist/config/schemas/config.js"() {
|
|
216
299
|
"use strict";
|
|
217
300
|
init_collection();
|
|
218
301
|
init_media();
|
|
219
|
-
defaultBranchAccessSchema =
|
|
220
|
-
defaultPathAccessLevelSchema =
|
|
221
|
-
defaultPathAccessLevelsSchema =
|
|
302
|
+
defaultBranchAccessSchema = z5.enum(["allow", "deny"]).default("deny");
|
|
303
|
+
defaultPathAccessLevelSchema = z5.enum(["allow", "deny"]);
|
|
304
|
+
defaultPathAccessLevelsSchema = z5.object({
|
|
222
305
|
read: defaultPathAccessLevelSchema.optional(),
|
|
223
306
|
edit: defaultPathAccessLevelSchema.optional(),
|
|
224
307
|
review: defaultPathAccessLevelSchema.optional()
|
|
225
308
|
}).strict();
|
|
226
|
-
defaultPathAccessSchema =
|
|
227
|
-
defaultBaseBranchSchema =
|
|
228
|
-
defaultRemoteNameSchema =
|
|
229
|
-
defaultRemoteUrlSchema =
|
|
230
|
-
gitBotAuthorNameSchema =
|
|
231
|
-
gitBotAuthorEmailSchema =
|
|
232
|
-
githubTokenEnvVarSchema =
|
|
233
|
-
operatingModeSchema =
|
|
234
|
-
deployedAsSchema =
|
|
309
|
+
defaultPathAccessSchema = z5.union([defaultPathAccessLevelSchema, defaultPathAccessLevelsSchema]).default("deny");
|
|
310
|
+
defaultBaseBranchSchema = z5.string().default("main");
|
|
311
|
+
defaultRemoteNameSchema = z5.string().default("origin");
|
|
312
|
+
defaultRemoteUrlSchema = z5.string().min(1);
|
|
313
|
+
gitBotAuthorNameSchema = z5.string().min(1);
|
|
314
|
+
gitBotAuthorEmailSchema = z5.string().email();
|
|
315
|
+
githubTokenEnvVarSchema = z5.string().default("GITHUB_BOT_TOKEN");
|
|
316
|
+
operatingModeSchema = z5.enum(["prod", "dev"]);
|
|
317
|
+
deployedAsSchema = z5.enum(["static", "server"]).default("server");
|
|
235
318
|
contentRootSchema = relativePathSchema.default("content");
|
|
236
|
-
sourceRootSchema =
|
|
237
|
-
basePathSchema =
|
|
319
|
+
sourceRootSchema = z5.string().min(1).optional();
|
|
320
|
+
basePathSchema = z5.string().transform((value) => value.trim()).refine((value) => value.length > 0, {
|
|
238
321
|
message: "basePath must not be empty or only whitespace"
|
|
239
322
|
}).optional();
|
|
240
|
-
deploymentNameSchema =
|
|
241
|
-
devContentSyncSchema =
|
|
242
|
-
devConfigSchema =
|
|
323
|
+
deploymentNameSchema = z5.string().default("prod");
|
|
324
|
+
devContentSyncSchema = z5.enum(["off", "warn"]).default("warn");
|
|
325
|
+
devConfigSchema = z5.object({
|
|
243
326
|
contentSync: devContentSyncSchema.optional()
|
|
244
327
|
});
|
|
245
|
-
editorConfigSchema =
|
|
246
|
-
title:
|
|
247
|
-
subtitle:
|
|
248
|
-
theme:
|
|
249
|
-
previewBase:
|
|
328
|
+
editorConfigSchema = z5.object({
|
|
329
|
+
title: z5.string().optional(),
|
|
330
|
+
subtitle: z5.string().optional(),
|
|
331
|
+
theme: z5.unknown().optional(),
|
|
332
|
+
previewBase: z5.record(z5.string()).optional(),
|
|
250
333
|
// UI handler functions (runtime only, don't serialize)
|
|
251
|
-
onAccountClick:
|
|
252
|
-
onLogoutClick:
|
|
334
|
+
onAccountClick: z5.function().returns(z5.void()).optional(),
|
|
335
|
+
onLogoutClick: z5.function().returns(z5.void()).optional(),
|
|
253
336
|
// Optional: custom account component (e.g., Clerk's UserButton)
|
|
254
|
-
AccountComponent:
|
|
337
|
+
AccountComponent: z5.custom().optional()
|
|
255
338
|
});
|
|
256
|
-
CanopyConfigSchema =
|
|
339
|
+
CanopyConfigSchema = z5.object({
|
|
257
340
|
media: mediaSchema.optional(),
|
|
258
341
|
// No outer .optional() here: defaultBranchAccessSchema/defaultPathAccessSchema already
|
|
259
342
|
// have .default('deny'), so the field is optional on input but always resolves to
|
|
@@ -269,7 +352,7 @@ var init_config = __esm({
|
|
|
269
352
|
// unset must stay undefined after parsing so dev mode can detect the fork
|
|
270
353
|
// point from git HEAD (see resolveBaseBranch in utils/git.ts).
|
|
271
354
|
defaultBaseBranch: defaultBaseBranchSchema.optional(),
|
|
272
|
-
defaultActiveBranch:
|
|
355
|
+
defaultActiveBranch: z5.string().min(1).optional(),
|
|
273
356
|
defaultRemoteName: defaultRemoteNameSchema.optional(),
|
|
274
357
|
defaultRemoteUrl: defaultRemoteUrlSchema.optional(),
|
|
275
358
|
gitBotAuthorName: gitBotAuthorNameSchema,
|
|
@@ -283,9 +366,9 @@ var init_config = __esm({
|
|
|
283
366
|
// Escape hatch for prod hosts that genuinely have internet access and intentionally
|
|
284
367
|
// run git against a network remote (see GitManager.resolveRemoteUrl's prod-mode guard).
|
|
285
368
|
// Default false/unset — the standard AWS Lambda+worker topology must leave this unset.
|
|
286
|
-
allowNetworkRemoteInProd:
|
|
287
|
-
settingsBranch:
|
|
288
|
-
autoCreateSettingsPR:
|
|
369
|
+
allowNetworkRemoteInProd: z5.boolean().optional(),
|
|
370
|
+
settingsBranch: z5.string().optional(),
|
|
371
|
+
autoCreateSettingsPR: z5.boolean().optional(),
|
|
289
372
|
// Deliberately `deploymentNameSchema.optional()`, NOT `deploymentNameSchema` alone.
|
|
290
373
|
// deploymentNameSchema.default('prod') would make `parse(undefined)` resolve to
|
|
291
374
|
// the literal 'prod' instead of staying `undefined` — collapsing the
|
|
@@ -301,9 +384,9 @@ var init_config = __esm({
|
|
|
301
384
|
sourceRoot: sourceRootSchema.optional(),
|
|
302
385
|
basePath: basePathSchema,
|
|
303
386
|
editor: editorConfigSchema.optional(),
|
|
304
|
-
authPlugin:
|
|
305
|
-
entryLinkUrl:
|
|
306
|
-
validateEntry:
|
|
387
|
+
authPlugin: z5.custom().optional(),
|
|
388
|
+
entryLinkUrl: z5.custom().optional(),
|
|
389
|
+
validateEntry: z5.custom().optional(),
|
|
307
390
|
dev: devConfigSchema.optional()
|
|
308
391
|
}).strict();
|
|
309
392
|
DEFAULT_PROD_WORKSPACE = "/mnt/efs/workspace";
|
|
@@ -311,17 +394,17 @@ var init_config = __esm({
|
|
|
311
394
|
});
|
|
312
395
|
|
|
313
396
|
// dist/config/schemas/permissions.js
|
|
314
|
-
import { z as
|
|
397
|
+
import { z as z6 } from "zod";
|
|
315
398
|
var permissionTargetSchema, pathPermissionSchema;
|
|
316
399
|
var init_permissions = __esm({
|
|
317
400
|
"dist/config/schemas/permissions.js"() {
|
|
318
401
|
"use strict";
|
|
319
|
-
permissionTargetSchema =
|
|
320
|
-
allowedUsers:
|
|
321
|
-
allowedGroups:
|
|
402
|
+
permissionTargetSchema = z6.object({
|
|
403
|
+
allowedUsers: z6.array(z6.string()).optional(),
|
|
404
|
+
allowedGroups: z6.array(z6.string()).optional()
|
|
322
405
|
});
|
|
323
|
-
pathPermissionSchema =
|
|
324
|
-
path:
|
|
406
|
+
pathPermissionSchema = z6.object({
|
|
407
|
+
path: z6.string().min(1),
|
|
325
408
|
read: permissionTargetSchema.optional(),
|
|
326
409
|
edit: permissionTargetSchema.optional(),
|
|
327
410
|
review: permissionTargetSchema.optional()
|
|
@@ -140,10 +140,13 @@ export class CmsStack extends Stack {
|
|
|
140
140
|
// hand-written `additionalBehaviors` ever gets that order wrong
|
|
141
141
|
// instead.
|
|
142
142
|
//
|
|
143
|
-
// `editorOrigins` is
|
|
144
|
-
//
|
|
145
|
-
//
|
|
146
|
-
//
|
|
143
|
+
// `editorOrigins` is the S3 CORS allowlist for the editor's presigned
|
|
144
|
+
// uploads, so it must list the origin the editor is served from. Include
|
|
145
|
+
// http://localhost:3000 only if you upload from a local dev editor
|
|
146
|
+
// against this bucket. It is optional only because `uploadBehavior` is
|
|
147
|
+
// the other way to satisfy the same requirement (CORS supplied at the
|
|
148
|
+
// CloudFront edge instead); with neither, a construct-created bucket
|
|
149
|
+
// refuses to synth.
|
|
147
150
|
//
|
|
148
151
|
// Run `pnpm --filter canopycms-cdk run build:lambda` before deploying --
|
|
149
152
|
// the transform Lambda needs its native sharp binary, and the construct
|
|
@@ -81,9 +81,9 @@ export declare const editorConfigSchema: z.ZodObject<{
|
|
|
81
81
|
export declare const CanopyConfigSchema: z.ZodObject<{
|
|
82
82
|
media: z.ZodOptional<z.ZodDiscriminatedUnion<"adapter", [z.ZodObject<{
|
|
83
83
|
adapter: z.ZodLiteral<"local">;
|
|
84
|
-
publicBaseUrl: z.ZodOptional<z.ZodString
|
|
84
|
+
publicBaseUrl: z.ZodOptional<z.ZodEffects<z.ZodString, string, string>>;
|
|
85
85
|
directory: z.ZodOptional<z.ZodEffects<z.ZodEffects<z.ZodEffects<z.ZodString, string, string>, string, string>, string, string>>;
|
|
86
|
-
}, "
|
|
86
|
+
}, "strict", z.ZodTypeAny, {
|
|
87
87
|
adapter: "local";
|
|
88
88
|
publicBaseUrl?: string | undefined;
|
|
89
89
|
directory?: string | undefined;
|
|
@@ -95,24 +95,27 @@ export declare const CanopyConfigSchema: z.ZodObject<{
|
|
|
95
95
|
adapter: z.ZodLiteral<"s3">;
|
|
96
96
|
bucket: z.ZodString;
|
|
97
97
|
region: z.ZodString;
|
|
98
|
-
publicBaseUrl: z.ZodOptional<z.ZodString
|
|
98
|
+
publicBaseUrl: z.ZodOptional<z.ZodEffects<z.ZodString, string, string>>;
|
|
99
|
+
uploadUrl: z.ZodOptional<z.ZodEffects<z.ZodString, string, string>>;
|
|
99
100
|
maxUploadBytes: z.ZodOptional<z.ZodNumber>;
|
|
100
|
-
}, "
|
|
101
|
+
}, "strict", z.ZodTypeAny, {
|
|
101
102
|
adapter: "s3";
|
|
102
103
|
bucket: string;
|
|
103
104
|
region: string;
|
|
104
105
|
publicBaseUrl?: string | undefined;
|
|
106
|
+
uploadUrl?: string | undefined;
|
|
105
107
|
maxUploadBytes?: number | undefined;
|
|
106
108
|
}, {
|
|
107
109
|
adapter: "s3";
|
|
108
110
|
bucket: string;
|
|
109
111
|
region: string;
|
|
110
112
|
publicBaseUrl?: string | undefined;
|
|
113
|
+
uploadUrl?: string | undefined;
|
|
111
114
|
maxUploadBytes?: number | undefined;
|
|
112
115
|
}>, z.ZodObject<{
|
|
113
116
|
adapter: z.ZodLiteral<"lfs">;
|
|
114
|
-
publicBaseUrl: z.ZodOptional<z.ZodString
|
|
115
|
-
}, "
|
|
117
|
+
publicBaseUrl: z.ZodOptional<z.ZodEffects<z.ZodString, string, string>>;
|
|
118
|
+
}, "strict", z.ZodTypeAny, {
|
|
116
119
|
adapter: "lfs";
|
|
117
120
|
publicBaseUrl?: string | undefined;
|
|
118
121
|
}, {
|
|
@@ -209,6 +212,7 @@ export declare const CanopyConfigSchema: z.ZodObject<{
|
|
|
209
212
|
bucket: string;
|
|
210
213
|
region: string;
|
|
211
214
|
publicBaseUrl?: string | undefined;
|
|
215
|
+
uploadUrl?: string | undefined;
|
|
212
216
|
maxUploadBytes?: number | undefined;
|
|
213
217
|
} | {
|
|
214
218
|
adapter: "lfs";
|
|
@@ -253,6 +257,7 @@ export declare const CanopyConfigSchema: z.ZodObject<{
|
|
|
253
257
|
bucket: string;
|
|
254
258
|
region: string;
|
|
255
259
|
publicBaseUrl?: string | undefined;
|
|
260
|
+
uploadUrl?: string | undefined;
|
|
256
261
|
maxUploadBytes?: number | undefined;
|
|
257
262
|
} | {
|
|
258
263
|
adapter: "lfs";
|