canopycms 0.0.67-int.89 → 0.0.67-int.90
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/ai/resolve-branch.d.ts +2 -2
- package/dist/ai/resolve-branch.js +5 -5
- package/dist/api/assets.js +1 -1
- package/dist/api/branch.js +3 -2
- package/dist/assets/pipeline.d.ts +4 -3
- package/dist/assets/pipeline.js +11 -15
- package/dist/assets/sharp-loader.d.ts +28 -0
- package/dist/assets/sharp-loader.js +44 -0
- package/dist/assets/transform-directives.d.ts +1 -1
- package/dist/assets/transform-directives.js +1 -1
- package/dist/assets/transform.d.ts +14 -2
- package/dist/assets/transform.js +23 -3
- package/dist/auth/plugin.js +1 -1
- package/dist/branch-workspace.d.ts +4 -2
- package/dist/branch-workspace.js +16 -10
- package/dist/build-mode.d.ts +32 -4
- package/dist/build-mode.js +32 -4
- package/dist/cli/cli.d.ts +11 -1
- package/dist/cli/cli.js +1260 -73
- package/dist/cli/generate-ai-content.js +5 -2
- package/dist/cli/init-github-app.d.ts +519 -0
- package/dist/cli/init-github-app.js +1551 -0
- package/dist/cli/init.js +71 -8
- package/dist/cli/project-detect.js +9 -1
- package/dist/cli/template-files/Dockerfile.cms.template +22 -25
- package/dist/cli/template-files/canopycms.config.ts.template +8 -6
- package/dist/cli/template-files/cdk-app.ts.template +48 -1
- package/dist/cli/template-files/cdk-tsconfig.json.template +41 -0
- package/dist/cli/template-files/cms-stack.ts.template +82 -19
- package/dist/cli/template-files/deploy-cms.yml.template +81 -1
- package/dist/cli/template-files/dockerignore.template +6 -0
- package/dist/cli/template-files/middleware-clerk.ts.template +17 -0
- package/dist/cli/template-files/middleware.ts.template +11 -7
- package/dist/cli/templates.d.ts +1 -0
- package/dist/cli/templates.js +8 -4
- package/dist/content-reader.js +5 -4
- package/dist/context.d.ts +5 -5
- package/dist/git-manager.js +3 -3
- package/dist/github-service.d.ts +41 -3
- package/dist/github-service.js +11 -1
- package/dist/operating-mode/mode-env.d.ts +9 -4
- package/dist/operating-mode/mode-env.js +9 -4
- package/dist/services.js +7 -6
- package/dist/utils/error.js +36 -0
- package/dist/utils/provisioning-lock.d.ts +4 -4
- package/dist/utils/provisioning-lock.js +7 -7
- package/dist/worker/cms-worker.d.ts +153 -3
- package/dist/worker/cms-worker.js +228 -6
- package/dist/worker/git-sync.js +8 -2
- package/dist/worker/github-auth.d.ts +268 -0
- package/dist/worker/github-auth.js +413 -0
- package/dist/worker/task-runner.d.ts +8 -1
- package/dist/worker/task-runner.js +42 -3
- package/dist/worker/worker-context.d.ts +26 -3
- package/package.json +2 -1
|
@@ -6,8 +6,8 @@ import type { CanopyConfig } from '../config/index.js';
|
|
|
6
6
|
/**
|
|
7
7
|
* Resolve the branch root directory for reading content.
|
|
8
8
|
*
|
|
9
|
-
* - Static deployment: current working directory (content is in the checkout)
|
|
10
|
-
* - Server (prod/dev): load or create the default active branch workspace
|
|
9
|
+
* - Static deployment, or any build: current working directory (content is in the checkout)
|
|
10
|
+
* - Server (prod/dev) at run time: load or create the default active branch workspace
|
|
11
11
|
*
|
|
12
12
|
* Branch resolution priority (mirrors createActiveBranchDetector in services.ts):
|
|
13
13
|
* 1. Explicit `defaultActiveBranch` in config
|
|
@@ -3,13 +3,13 @@
|
|
|
3
3
|
* Used by both the route handler and build utility.
|
|
4
4
|
*/
|
|
5
5
|
import { loadOrCreateBranchContext } from '../branch-workspace.js';
|
|
6
|
-
import {
|
|
6
|
+
import { readsFromCheckout } from '../build-mode.js';
|
|
7
7
|
import { detectHeadBranch } from '../utils/git.js';
|
|
8
8
|
/**
|
|
9
9
|
* Resolve the branch root directory for reading content.
|
|
10
10
|
*
|
|
11
|
-
* - Static deployment: current working directory (content is in the checkout)
|
|
12
|
-
* - Server (prod/dev): load or create the default active branch workspace
|
|
11
|
+
* - Static deployment, or any build: current working directory (content is in the checkout)
|
|
12
|
+
* - Server (prod/dev) at run time: load or create the default active branch workspace
|
|
13
13
|
*
|
|
14
14
|
* Branch resolution priority (mirrors createActiveBranchDetector in services.ts):
|
|
15
15
|
* 1. Explicit `defaultActiveBranch` in config
|
|
@@ -17,8 +17,8 @@ import { detectHeadBranch } from '../utils/git.js';
|
|
|
17
17
|
* 3. Fall back to `defaultBaseBranch` or 'main'
|
|
18
18
|
*/
|
|
19
19
|
export async function resolveBranchRoot(config) {
|
|
20
|
-
// Static deployments read content directly from the checkout — no branch workspace needed
|
|
21
|
-
if (
|
|
20
|
+
// Static deployments and builds read content directly from the checkout — no branch workspace needed
|
|
21
|
+
if (readsFromCheckout(config)) {
|
|
22
22
|
return process.cwd();
|
|
23
23
|
}
|
|
24
24
|
let activeBranch;
|
package/dist/api/assets.js
CHANGED
|
@@ -232,7 +232,7 @@ const deleteAssetHandler = async (ctx, req, params) => {
|
|
|
232
232
|
/** Cache-Control applied to every transform output this route writes/serves - matches finalize.ts's PUBLIC_CACHE_CONTROL for static public objects. */
|
|
233
233
|
const TRANSFORM_CACHE_CONTROL = 'public, max-age=31536000, immutable';
|
|
234
234
|
/**
|
|
235
|
-
* Lazy dev-mode emulation of the prod transform Lambda (
|
|
235
|
+
* Lazy dev-mode emulation of the prod transform Lambda (which reuses
|
|
236
236
|
* `parseTransformPath`/`formatDirectives`/`applyTransform` unchanged): parse
|
|
237
237
|
* the request, load the original, transform it, write the result back under
|
|
238
238
|
* its CANONICAL key (so a non-canonically-ordered directive string still
|
package/dist/api/branch.js
CHANGED
|
@@ -106,8 +106,9 @@ export const createBranchHandler = async (ctx, req, body) => {
|
|
|
106
106
|
// reserved canopycms-settings- prefix, and the remote-mirror check
|
|
107
107
|
// further down) apply ONLY to this user-facing creation path.
|
|
108
108
|
// http/handler.ts's auto-create (base/active/settings branches) and
|
|
109
|
-
// branch-workspace.ts's loadOrCreateBranchContext (reached from
|
|
110
|
-
// reads and the AI pipeline
|
|
109
|
+
// branch-workspace.ts's loadOrCreateBranchContext (reached from run-time
|
|
110
|
+
// content reads and the AI pipeline; build-time reads return the checkout
|
|
111
|
+
// and never provision) provision system/known branch names, not
|
|
111
112
|
// user-chosen ones, and deliberately stay uncovered -- intentional, not
|
|
112
113
|
// an oversight.
|
|
113
114
|
// Prevent git branch name collision with the settings branch. Settings
|
|
@@ -6,9 +6,10 @@
|
|
|
6
6
|
*
|
|
7
7
|
* Server-only - never import this module from client/editor code. It pulls
|
|
8
8
|
* in `file-type`, `image-size`, and the SVG sanitizer, none of which belong
|
|
9
|
-
* in a browser bundle. `sharp` is loaded
|
|
10
|
-
* `rasterIsDecodable` below) rather than statically
|
|
11
|
-
* server-only - it is never reachable from a browser
|
|
9
|
+
* in a browser bundle. `sharp` is loaded on first use through `loadSharp()`
|
|
10
|
+
* (sharp-loader.ts; see `rasterIsDecodable` below) rather than statically
|
|
11
|
+
* imported, but is just as server-only - it is never reachable from a browser
|
|
12
|
+
* bundle either way.
|
|
12
13
|
*/
|
|
13
14
|
import type { AssetMeta } from './types.js';
|
|
14
15
|
/** Client-declared content types accepted at presign time (UX only - never trusted at finalize). */
|
package/dist/assets/pipeline.js
CHANGED
|
@@ -6,15 +6,17 @@
|
|
|
6
6
|
*
|
|
7
7
|
* Server-only - never import this module from client/editor code. It pulls
|
|
8
8
|
* in `file-type`, `image-size`, and the SVG sanitizer, none of which belong
|
|
9
|
-
* in a browser bundle. `sharp` is loaded
|
|
10
|
-
* `rasterIsDecodable` below) rather than statically
|
|
11
|
-
* server-only - it is never reachable from a browser
|
|
9
|
+
* in a browser bundle. `sharp` is loaded on first use through `loadSharp()`
|
|
10
|
+
* (sharp-loader.ts; see `rasterIsDecodable` below) rather than statically
|
|
11
|
+
* imported, but is just as server-only - it is never reachable from a browser
|
|
12
|
+
* bundle either way.
|
|
12
13
|
*/
|
|
13
14
|
import { fileTypeFromBuffer } from 'file-type';
|
|
14
15
|
import { imageSize } from 'image-size';
|
|
15
16
|
import { create as createContentDisposition } from 'content-disposition';
|
|
16
17
|
import { getErrorMessage } from '../utils/error.js';
|
|
17
18
|
import { hashBytes, publicKey, slugifyFilename } from './keys.js';
|
|
19
|
+
import { loadSharp } from './sharp-loader.js';
|
|
18
20
|
import { sanitizeSvg } from './svg-sanitizer.js';
|
|
19
21
|
import { MAX_ANIMATED_FRAMES, MAX_INPUT_PIXELS } from './transform-directives.js';
|
|
20
22
|
const RASTER_MIME_TYPES = new Set(['image/png', 'image/jpeg', 'image/webp', 'image/gif']);
|
|
@@ -175,26 +177,20 @@ const UNDECODABLE_RASTER_ERROR = 'This image could not be decoded - it may be co
|
|
|
175
177
|
* same bytes from its own probe with the same 422.
|
|
176
178
|
*/
|
|
177
179
|
async function rasterIsDecodable(data) {
|
|
178
|
-
//
|
|
179
|
-
//
|
|
180
|
-
|
|
181
|
-
// the ESM entry point resolved by `import()`, so `typeof import('sharp')`
|
|
182
|
-
// is the two-property namespace object, not the callable itself.
|
|
183
|
-
let sharpModule;
|
|
180
|
+
// `loadSharp()` has already logged the load failure once for the process;
|
|
181
|
+
// this warning is per upload, and names what the failure costs here.
|
|
182
|
+
let sharp;
|
|
184
183
|
try {
|
|
185
|
-
|
|
184
|
+
sharp = await loadSharp();
|
|
186
185
|
}
|
|
187
186
|
catch (err) {
|
|
188
187
|
console.warn(`[canopycms] sharp could not be loaded - skipping raster decode validation at finalize: ${getErrorMessage(err)}`);
|
|
189
188
|
return true;
|
|
190
189
|
}
|
|
191
190
|
try {
|
|
192
|
-
const probeMeta = await
|
|
193
|
-
.default(data, { limitInputPixels: MAX_INPUT_PIXELS })
|
|
194
|
-
.metadata();
|
|
191
|
+
const probeMeta = await sharp(data, { limitInputPixels: MAX_INPUT_PIXELS }).metadata();
|
|
195
192
|
const pagesToRead = Math.min(probeMeta.pages ?? 1, MAX_ANIMATED_FRAMES);
|
|
196
|
-
await
|
|
197
|
-
.default(data, { pages: pagesToRead, limitInputPixels: MAX_INPUT_PIXELS })
|
|
193
|
+
await sharp(data, { pages: pagesToRead, limitInputPixels: MAX_INPUT_PIXELS })
|
|
198
194
|
.resize({ width: DECODE_CHECK_SIZE, height: DECODE_CHECK_SIZE, fit: 'fill' })
|
|
199
195
|
.toBuffer();
|
|
200
196
|
return true;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The one place this package loads `sharp` at runtime. Everything else
|
|
3
|
+
* reaches it through `loadSharp()` and imports only its types:
|
|
4
|
+
* `@typescript-eslint/no-restricted-imports` in eslint.config.mjs rejects a
|
|
5
|
+
* static value import of `sharp` anywhere under `src/` outside tests.
|
|
6
|
+
*
|
|
7
|
+
* Why lazily. sharp is a native addon, and loading it dlopens libvips. A
|
|
8
|
+
* static `import sharp from 'sharp'` makes importing the MODULE GRAPH load
|
|
9
|
+
* libvips. With sharp external, as in an adopter's Next 16 standalone build,
|
|
10
|
+
* Turbopack wraps it in an async module that awaits the load when the graph is
|
|
11
|
+
* evaluated (cms-image-build-epic.md in `.claude/future-tasks/`, "Every route
|
|
12
|
+
* loads sharp"). transform.ts is reachable from `canopycms/server` and, through
|
|
13
|
+
* api/assets.ts, from `canopycms/http` and so from `canopycms-next`, which a
|
|
14
|
+
* host app's root layout can import (that adopter's did). When the libvips
|
|
15
|
+
* `.so` was missing from its standalone output, that one static import turned
|
|
16
|
+
* it into a 500 on every on-demand route, 404s included, and defeated
|
|
17
|
+
* pipeline.ts's deliberate fail-open. Loaded on first use, a missing binary
|
|
18
|
+
* affects only the image operations that need it.
|
|
19
|
+
*
|
|
20
|
+
* Memoized INCLUDING a rejection: a failed dlopen does not heal inside a
|
|
21
|
+
* running process, so a retry would only pay for the failure again and log
|
|
22
|
+
* it again. The failure is logged once, here, and every later caller gets the
|
|
23
|
+
* same rejected promise to handle as its operation requires: transform.ts
|
|
24
|
+
* lets it propagate as a server error, pipeline.ts fails open.
|
|
25
|
+
*/
|
|
26
|
+
import type { SharpConstructor } from 'sharp';
|
|
27
|
+
/** Resolve sharp's callable constructor, loading the native module on the first call only. */
|
|
28
|
+
export declare function loadSharp(): Promise<SharpConstructor>;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The one place this package loads `sharp` at runtime. Everything else
|
|
3
|
+
* reaches it through `loadSharp()` and imports only its types:
|
|
4
|
+
* `@typescript-eslint/no-restricted-imports` in eslint.config.mjs rejects a
|
|
5
|
+
* static value import of `sharp` anywhere under `src/` outside tests.
|
|
6
|
+
*
|
|
7
|
+
* Why lazily. sharp is a native addon, and loading it dlopens libvips. A
|
|
8
|
+
* static `import sharp from 'sharp'` makes importing the MODULE GRAPH load
|
|
9
|
+
* libvips. With sharp external, as in an adopter's Next 16 standalone build,
|
|
10
|
+
* Turbopack wraps it in an async module that awaits the load when the graph is
|
|
11
|
+
* evaluated (cms-image-build-epic.md in `.claude/future-tasks/`, "Every route
|
|
12
|
+
* loads sharp"). transform.ts is reachable from `canopycms/server` and, through
|
|
13
|
+
* api/assets.ts, from `canopycms/http` and so from `canopycms-next`, which a
|
|
14
|
+
* host app's root layout can import (that adopter's did). When the libvips
|
|
15
|
+
* `.so` was missing from its standalone output, that one static import turned
|
|
16
|
+
* it into a 500 on every on-demand route, 404s included, and defeated
|
|
17
|
+
* pipeline.ts's deliberate fail-open. Loaded on first use, a missing binary
|
|
18
|
+
* affects only the image operations that need it.
|
|
19
|
+
*
|
|
20
|
+
* Memoized INCLUDING a rejection: a failed dlopen does not heal inside a
|
|
21
|
+
* running process, so a retry would only pay for the failure again and log
|
|
22
|
+
* it again. The failure is logged once, here, and every later caller gets the
|
|
23
|
+
* same rejected promise to handle as its operation requires: transform.ts
|
|
24
|
+
* lets it propagate as a server error, pipeline.ts fails open.
|
|
25
|
+
*/
|
|
26
|
+
import { getErrorMessage } from '../utils/error.js';
|
|
27
|
+
import { canopyLogError } from '../utils/logger.js';
|
|
28
|
+
let loading;
|
|
29
|
+
/** Resolve sharp's callable constructor, loading the native module on the first call only. */
|
|
30
|
+
export function loadSharp() {
|
|
31
|
+
if (!loading) {
|
|
32
|
+
loading = import('sharp').then((mod) => mod.default, (err) => {
|
|
33
|
+
canopyLogError(`[canopycms] sharp failed to load - image transforms and upload decode validation are unavailable in this process: ${getErrorMessage(err)}`);
|
|
34
|
+
throw err;
|
|
35
|
+
});
|
|
36
|
+
// Marks the stored promise handled; every caller still gets the rejection.
|
|
37
|
+
// Without it, a caller that does not await - a warm-up `void loadSharp()` -
|
|
38
|
+
// leaves a rejected promise with no handler, which Node treats as fatal by
|
|
39
|
+
// default: a whole-process outage, the shape this module exists to prevent.
|
|
40
|
+
// The failure is already logged above.
|
|
41
|
+
loading.catch(() => undefined);
|
|
42
|
+
}
|
|
43
|
+
return loading;
|
|
44
|
+
}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* NO imports (not even other files in this directory) so it can be imported
|
|
5
5
|
* from client bundles (via `assetUrl`/`assetSrcSet` in asset-url.ts, exported
|
|
6
6
|
* off the package's main entry) as well as from the server-only transform
|
|
7
|
-
* engine (transform.ts) and the
|
|
7
|
+
* engine (transform.ts) and the prod transform Lambda, without
|
|
8
8
|
* ever pulling in node:crypto, sharp, or any other server-only dependency.
|
|
9
9
|
*
|
|
10
10
|
* `{directives}` is either the literal identity token (`orig`) or a
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* NO imports (not even other files in this directory) so it can be imported
|
|
5
5
|
* from client bundles (via `assetUrl`/`assetSrcSet` in asset-url.ts, exported
|
|
6
6
|
* off the package's main entry) as well as from the server-only transform
|
|
7
|
-
* engine (transform.ts) and the
|
|
7
|
+
* engine (transform.ts) and the prod transform Lambda, without
|
|
8
8
|
* ever pulling in node:crypto, sharp, or any other server-only dependency.
|
|
9
9
|
*
|
|
10
10
|
* `{directives}` is either the literal identity token (`orig`) or a
|
|
@@ -3,8 +3,15 @@
|
|
|
3
3
|
* source image bytes with sharp. Server-only - never import this from
|
|
4
4
|
* client/editor code (this is why it lives in its own file, separate from
|
|
5
5
|
* the dependency-free transform-directives.ts). Used by the dev-mode lazy
|
|
6
|
-
* `/assets/t/*` emulation in api/assets.ts
|
|
7
|
-
*
|
|
6
|
+
* `/assets/t/*` emulation in api/assets.ts and, unchanged, by the prod
|
|
7
|
+
* transform Lambda (packages/canopycms-cdk/lambda/asset-transform).
|
|
8
|
+
*
|
|
9
|
+
* sharp is imported for its TYPES only and loaded on first use through
|
|
10
|
+
* `loadSharp()` (sharp-loader.ts), so importing this module never loads
|
|
11
|
+
* libvips. That matters because this module sits in the import graph of
|
|
12
|
+
* `canopycms/server` and `canopycms/http`: see sharp-loader.ts for the
|
|
13
|
+
* adopter outage a static import caused, and eslint.config.mjs for the rule
|
|
14
|
+
* that keeps it from coming back.
|
|
8
15
|
*
|
|
9
16
|
* Pipeline: a cheap metadata-only probe (`limitInputPixels: MAX_INPUT_PIXELS`,
|
|
10
17
|
* decompression-bomb defense #1) learns the real page count, so animated
|
|
@@ -47,4 +54,9 @@ export interface TransformRejection {
|
|
|
47
54
|
error: string;
|
|
48
55
|
}
|
|
49
56
|
export type TransformResult = TransformSuccess | TransformRejection;
|
|
57
|
+
/**
|
|
58
|
+
* Resolves a `TransformRejection` for anything wrong with the input or the
|
|
59
|
+
* output. REJECTS (throws) when sharp itself cannot be loaded in this process
|
|
60
|
+
* - see the comment at the `loadSharp()` call below.
|
|
61
|
+
*/
|
|
50
62
|
export declare function applyTransform(input: ApplyTransformInput, directives: TransformDirectives): Promise<TransformResult>;
|
package/dist/assets/transform.js
CHANGED
|
@@ -3,8 +3,15 @@
|
|
|
3
3
|
* source image bytes with sharp. Server-only - never import this from
|
|
4
4
|
* client/editor code (this is why it lives in its own file, separate from
|
|
5
5
|
* the dependency-free transform-directives.ts). Used by the dev-mode lazy
|
|
6
|
-
* `/assets/t/*` emulation in api/assets.ts
|
|
7
|
-
*
|
|
6
|
+
* `/assets/t/*` emulation in api/assets.ts and, unchanged, by the prod
|
|
7
|
+
* transform Lambda (packages/canopycms-cdk/lambda/asset-transform).
|
|
8
|
+
*
|
|
9
|
+
* sharp is imported for its TYPES only and loaded on first use through
|
|
10
|
+
* `loadSharp()` (sharp-loader.ts), so importing this module never loads
|
|
11
|
+
* libvips. That matters because this module sits in the import graph of
|
|
12
|
+
* `canopycms/server` and `canopycms/http`: see sharp-loader.ts for the
|
|
13
|
+
* adopter outage a static import caused, and eslint.config.mjs for the rule
|
|
14
|
+
* that keeps it from coming back.
|
|
8
15
|
*
|
|
9
16
|
* Pipeline: a cheap metadata-only probe (`limitInputPixels: MAX_INPUT_PIXELS`,
|
|
10
17
|
* decompression-bomb defense #1) learns the real page count, so animated
|
|
@@ -28,8 +35,8 @@
|
|
|
28
35
|
* path with no extra native dependency) purely for pipeline uniformity, not
|
|
29
36
|
* because GIF needs stripping.
|
|
30
37
|
*/
|
|
31
|
-
import sharp from 'sharp';
|
|
32
38
|
import { getErrorMessage } from '../utils/error.js';
|
|
39
|
+
import { loadSharp } from './sharp-loader.js';
|
|
33
40
|
import { MAX_ANIMATED_FRAMES, MAX_INPUT_PIXELS, } from './transform-directives.js';
|
|
34
41
|
/** Raster formats the transform engine accepts as input. svg/pdf never reach here - they're served statically. */
|
|
35
42
|
const ALLOWED_INPUT_EXTS = new Set(['png', 'jpg', 'jpeg', 'webp', 'gif']);
|
|
@@ -126,6 +133,11 @@ function encodeSourceFormat(pipeline, sourceExt, quality) {
|
|
|
126
133
|
return pipeline.png(options);
|
|
127
134
|
}
|
|
128
135
|
}
|
|
136
|
+
/**
|
|
137
|
+
* Resolves a `TransformRejection` for anything wrong with the input or the
|
|
138
|
+
* output. REJECTS (throws) when sharp itself cannot be loaded in this process
|
|
139
|
+
* - see the comment at the `loadSharp()` call below.
|
|
140
|
+
*/
|
|
129
141
|
export async function applyTransform(input, directives) {
|
|
130
142
|
const sourceExt = input.ext.toLowerCase();
|
|
131
143
|
if (!ALLOWED_INPUT_EXTS.has(sourceExt)) {
|
|
@@ -135,6 +147,14 @@ export async function applyTransform(input, directives) {
|
|
|
135
147
|
error: `Unsupported input format for transform: '${sourceExt}'`,
|
|
136
148
|
};
|
|
137
149
|
}
|
|
150
|
+
// After the format check and OUTSIDE the try below, both on purpose. A load
|
|
151
|
+
// failure is a fault in this environment (the native binary is missing or
|
|
152
|
+
// built for another platform), not a fact about these bytes, so it must
|
|
153
|
+
// reach the caller as a thrown error - a 500 from http/handler.ts's
|
|
154
|
+
// top-level catch or from the transform Lambda's - and never the 422 the
|
|
155
|
+
// catch below means "sharp could not process this input". The 400 above
|
|
156
|
+
// needs no decoder, so it keeps working where sharp cannot load.
|
|
157
|
+
const sharp = await loadSharp();
|
|
138
158
|
const resize = directives.identity ? undefined : directives;
|
|
139
159
|
const format = resize?.format;
|
|
140
160
|
const quality = resize?.quality;
|
package/dist/auth/plugin.js
CHANGED
|
@@ -22,6 +22,6 @@ export function assertAuthPluginAllowedForMode(plugin, mode) {
|
|
|
22
22
|
'`verifiesCredentials: true`. This plugin performs no real credential verification, ' +
|
|
23
23
|
'so anyone could impersonate any user (including admins). Configure a verifying auth ' +
|
|
24
24
|
"plugin for production (e.g. createClerkAuthPlugin from 'canopycms-auth-clerk' with " +
|
|
25
|
-
"
|
|
25
|
+
"CLERK_JWT_KEY set), or run with mode: 'dev' for local development.");
|
|
26
26
|
}
|
|
27
27
|
}
|
|
@@ -25,8 +25,10 @@ export { loadBranchContext } from './branch-metadata.js';
|
|
|
25
25
|
/**
|
|
26
26
|
* Load an existing branch context, or create the workspace if it doesn't exist yet.
|
|
27
27
|
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
28
|
+
* When content is read from the checkout (`readsFromCheckout`: a static
|
|
29
|
+
* deployment, or any build) this skips every git and branch-workspace
|
|
30
|
+
* operation and returns a synthetic context rooted at the current working
|
|
31
|
+
* directory. `branchName` is echoed back on that context but selects nothing.
|
|
30
32
|
*/
|
|
31
33
|
export declare function loadOrCreateBranchContext(options: {
|
|
32
34
|
config: CanopyConfig;
|
package/dist/branch-workspace.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
2
|
import { ensureBranchRoot } from './paths/index.js';
|
|
3
3
|
import { getBranchMetadataFileManager, loadBranchContext } from './branch-metadata.js';
|
|
4
|
-
import {
|
|
4
|
+
import { readsFromCheckout } from './build-mode.js';
|
|
5
5
|
import { operatingStrategy } from './operating-mode/index.js';
|
|
6
6
|
import { GitManager } from './git-manager.js';
|
|
7
7
|
import { createDebugLogger } from './utils/debug.js';
|
|
@@ -28,11 +28,15 @@ export class BranchWorkspaceManager {
|
|
|
28
28
|
}
|
|
29
29
|
// Create new lock promise
|
|
30
30
|
const lockPromise = (async () => {
|
|
31
|
-
// The in-memory lock above only serializes within one process.
|
|
32
|
-
//
|
|
33
|
-
//
|
|
34
|
-
//
|
|
35
|
-
//
|
|
31
|
+
// The in-memory lock above only serializes within one process. Separate
|
|
32
|
+
// processes can provision the same branch workspace at once -- several
|
|
33
|
+
// Lambda containers sharing one EFS workspace root in prod, or the dev
|
|
34
|
+
// server beside a content-reading script run outside a build in dev --
|
|
35
|
+
// and would otherwise both clone into it ("destination path already
|
|
36
|
+
// exists"), so guard the workspace init with a cross-process lock too.
|
|
37
|
+
// initializeWorkspace is idempotent, so the waiter simply finds the
|
|
38
|
+
// workspace already cloned. (A build's content reads never get here:
|
|
39
|
+
// loadOrCreateBranchContext returns the checkout before provisioning.)
|
|
36
40
|
let releaseLock;
|
|
37
41
|
try {
|
|
38
42
|
log.debug('workspace', 'Ensuring git workspace', {
|
|
@@ -122,12 +126,14 @@ export { loadBranchContext } from './branch-metadata.js';
|
|
|
122
126
|
/**
|
|
123
127
|
* Load an existing branch context, or create the workspace if it doesn't exist yet.
|
|
124
128
|
*
|
|
125
|
-
*
|
|
126
|
-
*
|
|
129
|
+
* When content is read from the checkout (`readsFromCheckout`: a static
|
|
130
|
+
* deployment, or any build) this skips every git and branch-workspace
|
|
131
|
+
* operation and returns a synthetic context rooted at the current working
|
|
132
|
+
* directory. `branchName` is echoed back on that context but selects nothing.
|
|
127
133
|
*/
|
|
128
134
|
export async function loadOrCreateBranchContext(options) {
|
|
129
|
-
// Static deployments read content directly from the checkout — no git ops
|
|
130
|
-
if (
|
|
135
|
+
// Static deployments and builds read content directly from the checkout — no git ops
|
|
136
|
+
if (readsFromCheckout(options.config)) {
|
|
131
137
|
const cwd = process.cwd();
|
|
132
138
|
return {
|
|
133
139
|
branch: {
|
package/dist/build-mode.d.ts
CHANGED
|
@@ -8,12 +8,40 @@ export declare const isDeployedStatic: (config: {
|
|
|
8
8
|
deployedAs?: string;
|
|
9
9
|
}) => boolean;
|
|
10
10
|
/**
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
11
|
+
* Detect a build, where there is no request and no auth.
|
|
12
|
+
*
|
|
13
|
+
* Under Next.js this is `NEXT_PHASE === 'phase-production-build'`, which
|
|
14
|
+
* `next build` sets itself: after compiling, and immediately before it creates
|
|
15
|
+
* the static worker that collects page data and prerenders, whose processes
|
|
16
|
+
* inherit it. So it is true in page modules, `generateStaticParams` and
|
|
17
|
+
* prerendering, but NOT yet set when `next build` loads `next.config.*`, which
|
|
18
|
+
* it does first. `next dev`, `next start` and the standalone server never set
|
|
19
|
+
* it. Verified in Next 15.5.21 and 16.1.7, where `build/index.js` (in both
|
|
20
|
+
* `dist/` and `dist/esm/`) holds the only assignment -- re-check on every Next
|
|
21
|
+
* major.
|
|
22
|
+
*
|
|
23
|
+
* `CANOPY_BUILD_MODE=true` is the framework-neutral switch: for builds Next
|
|
24
|
+
* does not drive, and for scripts run alongside one (the generated
|
|
25
|
+
* `Dockerfile.cms` sets it in its builder stage, ahead of the build command).
|
|
15
26
|
*/
|
|
16
27
|
export declare const isBuildMode: () => boolean;
|
|
28
|
+
/**
|
|
29
|
+
* Is content read straight from the checkout, rather than from a branch
|
|
30
|
+
* workspace?
|
|
31
|
+
*
|
|
32
|
+
* This decides WHERE content is read. `isDeployedStatic` and `isBuildMode`
|
|
33
|
+
* used on their own decide WHO reads it (`STATIC_DEPLOY_USER`, no ACLs).
|
|
34
|
+
*
|
|
35
|
+
* True for a static deployment, and for every build in either mode and either
|
|
36
|
+
* deployment type. A build reads the working tree at `process.cwd()` and
|
|
37
|
+
* never touches git, `.canopy-dev` or a branch clone: CI builds exactly the
|
|
38
|
+
* checked-out commit, and a local build reads what is on disk, uncommitted
|
|
39
|
+
* files included. Editor saves not yet copied out of `.canopy-dev`
|
|
40
|
+
* (`canopycms sync pull`) are not part of a build.
|
|
41
|
+
*/
|
|
42
|
+
export declare const readsFromCheckout: (config: {
|
|
43
|
+
deployedAs?: string;
|
|
44
|
+
}) => boolean;
|
|
17
45
|
/**
|
|
18
46
|
* Synthetic user with full access for static deployments and build phase.
|
|
19
47
|
* Has Admin privileges — all content is readable, permissions are skipped.
|
package/dist/build-mode.js
CHANGED
|
@@ -7,10 +7,21 @@ export const isDeployedStatic = (config) => {
|
|
|
7
7
|
return config.deployedAs === 'static';
|
|
8
8
|
};
|
|
9
9
|
/**
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
10
|
+
* Detect a build, where there is no request and no auth.
|
|
11
|
+
*
|
|
12
|
+
* Under Next.js this is `NEXT_PHASE === 'phase-production-build'`, which
|
|
13
|
+
* `next build` sets itself: after compiling, and immediately before it creates
|
|
14
|
+
* the static worker that collects page data and prerenders, whose processes
|
|
15
|
+
* inherit it. So it is true in page modules, `generateStaticParams` and
|
|
16
|
+
* prerendering, but NOT yet set when `next build` loads `next.config.*`, which
|
|
17
|
+
* it does first. `next dev`, `next start` and the standalone server never set
|
|
18
|
+
* it. Verified in Next 15.5.21 and 16.1.7, where `build/index.js` (in both
|
|
19
|
+
* `dist/` and `dist/esm/`) holds the only assignment -- re-check on every Next
|
|
20
|
+
* major.
|
|
21
|
+
*
|
|
22
|
+
* `CANOPY_BUILD_MODE=true` is the framework-neutral switch: for builds Next
|
|
23
|
+
* does not drive, and for scripts run alongside one (the generated
|
|
24
|
+
* `Dockerfile.cms` sets it in its builder stage, ahead of the build command).
|
|
14
25
|
*/
|
|
15
26
|
export const isBuildMode = () => {
|
|
16
27
|
// Next.js build phase
|
|
@@ -21,6 +32,23 @@ export const isBuildMode = () => {
|
|
|
21
32
|
return true;
|
|
22
33
|
return false;
|
|
23
34
|
};
|
|
35
|
+
/**
|
|
36
|
+
* Is content read straight from the checkout, rather than from a branch
|
|
37
|
+
* workspace?
|
|
38
|
+
*
|
|
39
|
+
* This decides WHERE content is read. `isDeployedStatic` and `isBuildMode`
|
|
40
|
+
* used on their own decide WHO reads it (`STATIC_DEPLOY_USER`, no ACLs).
|
|
41
|
+
*
|
|
42
|
+
* True for a static deployment, and for every build in either mode and either
|
|
43
|
+
* deployment type. A build reads the working tree at `process.cwd()` and
|
|
44
|
+
* never touches git, `.canopy-dev` or a branch clone: CI builds exactly the
|
|
45
|
+
* checked-out commit, and a local build reads what is on disk, uncommitted
|
|
46
|
+
* files included. Editor saves not yet copied out of `.canopy-dev`
|
|
47
|
+
* (`canopycms sync pull`) are not part of a build.
|
|
48
|
+
*/
|
|
49
|
+
export const readsFromCheckout = (config) => {
|
|
50
|
+
return isDeployedStatic(config) || isBuildMode();
|
|
51
|
+
};
|
|
24
52
|
/**
|
|
25
53
|
* Synthetic user with full access for static deployments and build phase.
|
|
26
54
|
* Has Admin privileges — all content is readable, permissions are skipped.
|
package/dist/cli/cli.d.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* CanopyCMS CLI entrypoint.
|
|
4
4
|
*
|
|
5
5
|
* Routes commands to their implementations:
|
|
6
|
-
* init, init-deploy, worker, generate-ai-content, sync, migrate
|
|
6
|
+
* init, init-deploy, init-github-app, worker, generate-ai-content, sync, migrate
|
|
7
7
|
*
|
|
8
8
|
* Command implementations live in separate files (init.ts, sync.ts, etc.)
|
|
9
9
|
* and are dynamically imported to keep startup fast.
|
|
@@ -16,6 +16,16 @@ export declare function parseArgs(rawArgs: string[]): {
|
|
|
16
16
|
flags: Record<string, string | boolean>;
|
|
17
17
|
command: string | undefined;
|
|
18
18
|
};
|
|
19
|
+
/**
|
|
20
|
+
* The argv after a literal `--`, as a real string array.
|
|
21
|
+
*
|
|
22
|
+
* minimist types its parsed object with an `any` index signature, so reading
|
|
23
|
+
* `argv['--']` directly would hand an `any` straight to spawn(). Narrowed here
|
|
24
|
+
* through `unknown` instead, and returned as `[]` when absent so callers can
|
|
25
|
+
* treat "no passthrough" and "empty passthrough" the same way. Exported for
|
|
26
|
+
* testing.
|
|
27
|
+
*/
|
|
28
|
+
export declare function passthroughArgs(argv: Record<string, unknown>): string[];
|
|
19
29
|
/**
|
|
20
30
|
* Validate the --auth flag value for `init`. Returns undefined when the flag
|
|
21
31
|
* was not provided (caller should fall through to interactive prompt / default).
|