astroidjs 0.8.1 → 0.9.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/README.md +11 -0
- package/dist/config.d.ts +19 -0
- package/dist/config.js +20 -0
- package/dist/worker/generate.js +14 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -61,10 +61,21 @@ export default defineAstroid({
|
|
|
61
61
|
theme: { name: "Meg Bowen Studio", colors: { brand: "#2b2b2b" } },
|
|
62
62
|
sections: ["hero", "gallery", "aboutIntro", "contact"],
|
|
63
63
|
portal: { enabled: true },
|
|
64
|
+
// The masters ARE the product here: 40 MB camera files upload once and only
|
|
65
|
+
// resized derivatives are ever served, so the size costs storage, not page
|
|
66
|
+
// weight. Omit to keep the 10 MB default.
|
|
67
|
+
media: { maxUploadBytes: 40 * 1024 * 1024 },
|
|
64
68
|
deploy: { platform: "cloudflare" },
|
|
65
69
|
});
|
|
66
70
|
```
|
|
67
71
|
|
|
72
|
+
`media.maxUploadBytes` is checked at generate time, not in production: a value
|
|
73
|
+
above Cloudflare's **100 MB request-body limit** is rejected outright, because
|
|
74
|
+
the edge drops an oversized body before the Worker runs — the media route never
|
|
75
|
+
gets to answer with its own `413`, so the editor would see an opaque failure.
|
|
76
|
+
Zero, negatives, and non-integers are rejected too (that last one catches the
|
|
77
|
+
megabytes-not-bytes slip).
|
|
78
|
+
|
|
68
79
|
## Commerce
|
|
69
80
|
|
|
70
81
|
**Providers fill roles.** Not "the commerce provider" — the toolkit's clients
|
package/dist/config.d.ts
CHANGED
|
@@ -407,6 +407,23 @@ export interface SettingsConfig {
|
|
|
407
407
|
* settings values validated as media-library URLs on write. */
|
|
408
408
|
imageKeys?: string[];
|
|
409
409
|
}
|
|
410
|
+
/** Media-library upload policy. */
|
|
411
|
+
export interface MediaConfig {
|
|
412
|
+
/**
|
|
413
|
+
* Largest accepted upload, in bytes. Default 10 MB (louise-toolkit's
|
|
414
|
+
* `DEFAULT_MAX_BYTES`).
|
|
415
|
+
*
|
|
416
|
+
* Raise it when the masters ARE the product — a photographer's or painter's
|
|
417
|
+
* portfolio uploads 40 MB camera files and only ever serves Cloudflare-
|
|
418
|
+
* resized derivatives, so the master's size costs storage, not page weight.
|
|
419
|
+
*
|
|
420
|
+
* Bounded by the platform, not by this setting: a Cloudflare Worker rejects
|
|
421
|
+
* a request body over 100 MB before any handler runs, so a value above that
|
|
422
|
+
* would fail at the edge with no error you can catch. Validated at generate
|
|
423
|
+
* time rather than surfacing as a mystery upload failure in production.
|
|
424
|
+
*/
|
|
425
|
+
maxUploadBytes?: number;
|
|
426
|
+
}
|
|
410
427
|
export interface DeployConfig {
|
|
411
428
|
platform: "cloudflare";
|
|
412
429
|
/** Media base for R2 + `cf-image` resizing — matches Louise's media route
|
|
@@ -483,6 +500,8 @@ export interface AstroidConfig {
|
|
|
483
500
|
/** Site-specific editable settings — extra `custom` keys + image keys on top
|
|
484
501
|
* of Astroid's base `site_settings` columns. */
|
|
485
502
|
settings?: SettingsConfig;
|
|
503
|
+
/** Media-library upload policy (e.g. a larger `maxUploadBytes`). */
|
|
504
|
+
media?: MediaConfig;
|
|
486
505
|
/**
|
|
487
506
|
* Force the contact form + `inquiries` table on or off. Omit to detect from
|
|
488
507
|
* the config (a `contact` section, or a wholesale-inquiry module). Set `true`
|
package/dist/config.js
CHANGED
|
@@ -160,6 +160,23 @@ function assertTenancy(config) {
|
|
|
160
160
|
}
|
|
161
161
|
}
|
|
162
162
|
}
|
|
163
|
+
/** Cloudflare rejects a request body over 100 MB at the edge, before any Worker
|
|
164
|
+
* handler runs — so an upload limit above it can never be honoured, and the
|
|
165
|
+
* failure arrives as an opaque edge error rather than the route's own 413. */
|
|
166
|
+
const WORKERS_MAX_REQUEST_BODY_BYTES = 100 * 1024 * 1024;
|
|
167
|
+
function assertMediaConfig(media) {
|
|
168
|
+
const max = media?.maxUploadBytes;
|
|
169
|
+
if (max === undefined)
|
|
170
|
+
return;
|
|
171
|
+
if (!Number.isInteger(max) || max <= 0) {
|
|
172
|
+
throw new AstroidConfigError(`\`media.maxUploadBytes\` must be a positive integer number of BYTES; got ${max}`);
|
|
173
|
+
}
|
|
174
|
+
if (max > WORKERS_MAX_REQUEST_BODY_BYTES) {
|
|
175
|
+
throw new AstroidConfigError(`\`media.maxUploadBytes\` (${max}) exceeds Cloudflare's 100 MB request-body limit. ` +
|
|
176
|
+
"A body that large is rejected at the edge before the Worker runs, so the upload " +
|
|
177
|
+
"would fail with an error the media route never sees.");
|
|
178
|
+
}
|
|
179
|
+
}
|
|
163
180
|
export function defineAstroid(config) {
|
|
164
181
|
if (!config.key || config.key.trim().length === 0) {
|
|
165
182
|
throw new AstroidConfigError("Astroid config requires a non-empty `key` (it names the generated worker/D1/R2 bindings)");
|
|
@@ -174,6 +191,9 @@ export function defineAstroid(config) {
|
|
|
174
191
|
// Fourthwall, a storefront over Stripe) fails here rather than at runtime on
|
|
175
192
|
// the first invoice, as a missing function.
|
|
176
193
|
assertCommerceRoles(config.commerce);
|
|
194
|
+
// A media limit above the platform's own body cap is unhonourable — reject it
|
|
195
|
+
// here rather than let an editor watch a 120 MB upload die at the edge.
|
|
196
|
+
assertMediaConfig(config.media);
|
|
177
197
|
// A portal is a SECOND Better Auth instance beside the editor's. Reject any
|
|
178
198
|
// isolation that would collide with the editor on the same origin (a shared
|
|
179
199
|
// cookie prefix silently cross-signs-out; a shared table prefix merges the two
|
package/dist/worker/generate.js
CHANGED
|
@@ -123,11 +123,18 @@ export function generateAstroidWorker(config) {
|
|
|
123
123
|
return "aiRoute({ resolveEditor, ai: aiRunner })";
|
|
124
124
|
case "seoFix":
|
|
125
125
|
return "seoFixRoute({ table: pages, resolveEditor, ai: aiRunner })";
|
|
126
|
-
case "media":
|
|
126
|
+
case "media": {
|
|
127
127
|
// `altText` fills a new upload's alt from the image itself. Best-effort
|
|
128
128
|
// by contract — a model error or a missing binding never fails the
|
|
129
129
|
// upload — so it costs nothing on a project that doesn't want it.
|
|
130
|
-
|
|
130
|
+
//
|
|
131
|
+
// `maxBytes` is emitted only when the site raised it: omitted, the
|
|
132
|
+
// route keeps louise-toolkit's DEFAULT_MAX_BYTES, so the generated
|
|
133
|
+
// line stays identical for every project that doesn't care.
|
|
134
|
+
const maxBytes = config.media?.maxUploadBytes;
|
|
135
|
+
const maxArg = maxBytes ? `, maxBytes: ${maxBytes}` : "";
|
|
136
|
+
return `mediaRoute({ table: media, resolveEditor, referenceSources: MEDIA_REFERENCE_SOURCES, altText: aiRunner${maxArg} })`;
|
|
137
|
+
}
|
|
131
138
|
case "editors":
|
|
132
139
|
// The editor instance's user table is `louise_`-prefixed (the editor
|
|
133
140
|
// convention — the unprefixed `user` table is left for a second/portal
|
|
@@ -606,11 +613,14 @@ export function generateAstroidMiddleware(config) {
|
|
|
606
613
|
" // map, checked before the tenant — an app exists whether or not any",
|
|
607
614
|
" // tenant does, and needs no lookup.",
|
|
608
615
|
" const app = appPrefix(context.url.hostname, TENANCY);",
|
|
609
|
-
"
|
|
616
|
+
" // `search` is preserved: the rewrite chooses which page renders, not",
|
|
617
|
+
" // what was asked of it. Dropping it silently loses filters, pagination,",
|
|
618
|
+
" // campaign tags — and every typed search param a routed island reads.",
|
|
619
|
+
" if (app) return `${app}${context.url.pathname}${context.url.search}`;",
|
|
610
620
|
]
|
|
611
621
|
: []),
|
|
612
622
|
" const tenant = context.locals.tenant;",
|
|
613
|
-
` return tenant ? \`${rewritePrefix}/\${tenant.slug}\${context.url.pathname}\` : undefined;`,
|
|
623
|
+
` return tenant ? \`${rewritePrefix}/\${tenant.slug}\${context.url.pathname}\${context.url.search}\` : undefined;`,
|
|
614
624
|
" },",
|
|
615
625
|
]
|
|
616
626
|
: []),
|
package/package.json
CHANGED