@stnd/core 0.22.1 → 0.22.3
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 +6 -3
- package/package.json +19 -18
- package/src/loader.js +41 -8
- package/src/virtual.js +15 -0
- package/standard.js +5 -33
package/README.md
CHANGED
|
@@ -2,21 +2,24 @@
|
|
|
2
2
|
title: "@stnd/core"
|
|
3
3
|
aliases: []
|
|
4
4
|
created: 2026-06-20 13:56
|
|
5
|
-
modified: 2026-
|
|
5
|
+
modified: 2026-09-16T17:31:40.687Z
|
|
6
6
|
last_audited: 2026-07-14
|
|
7
7
|
audit_interval_days: 90
|
|
8
8
|
next_audit: 2026-10-12
|
|
9
9
|
audit_priority: 3
|
|
10
10
|
maturity: tree
|
|
11
11
|
mode: read
|
|
12
|
-
publish:
|
|
12
|
+
publish: true
|
|
13
13
|
status: active
|
|
14
14
|
tags:
|
|
15
15
|
- package
|
|
16
16
|
- stnd
|
|
17
17
|
theme: kernel
|
|
18
18
|
type: package
|
|
19
|
-
visibility:
|
|
19
|
+
visibility: public
|
|
20
|
+
garden-url: https://standard.garden/@francis/readme
|
|
21
|
+
garden-short: https://stnd.gd/DT0h6O
|
|
22
|
+
permalink: readme
|
|
20
23
|
---
|
|
21
24
|
|
|
22
25
|
# @[stnd](../README)/core
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stnd/core",
|
|
3
|
-
"version": "0.22.
|
|
3
|
+
"version": "0.22.3",
|
|
4
4
|
"description": "A fine-art typography framework implementing classical design principles, mathematical precision with the golden ratio, and micro-typography rules inspired by the masters of print design.",
|
|
5
5
|
"author": "Francis Fontaine",
|
|
6
6
|
"license": "MIT",
|
|
@@ -26,6 +26,7 @@
|
|
|
26
26
|
"svelte": "./Hook.svelte",
|
|
27
27
|
"default": "./Hook.astro"
|
|
28
28
|
},
|
|
29
|
+
"./module-filter": "./module-filter.js",
|
|
29
30
|
"./loader": "./src/loader.js",
|
|
30
31
|
"./virtual": "./src/virtual.js",
|
|
31
32
|
"./src/dev-toolbar/*": "./src/dev-toolbar/*",
|
|
@@ -53,25 +54,25 @@
|
|
|
53
54
|
"dependencies": {
|
|
54
55
|
"@astrojs/sitemap": "^3.7.3",
|
|
55
56
|
"@astrojs/svelte": "9.0.1",
|
|
56
|
-
"astro": "7.1
|
|
57
|
+
"astro": "7.3.1",
|
|
57
58
|
"glob": "^13.0.6",
|
|
58
|
-
"svelte": "5.
|
|
59
|
+
"svelte": "5.57.0",
|
|
59
60
|
"zod": "^4.4.3",
|
|
60
|
-
"@stnd/client": "0.5.
|
|
61
|
-
"@stnd/cloudflare": "0.5.
|
|
62
|
-
"@stnd/fonts": "0.5.
|
|
63
|
-
"@stnd/icon": "0.2.
|
|
64
|
-
"@stnd/lab": "1.0.
|
|
65
|
-
"@stnd/launcher": "1.0.
|
|
66
|
-
"@stnd/layout": "0.5.
|
|
67
|
-
"@stnd/log": "0.5.
|
|
68
|
-
"@stnd/modules": "0.5.
|
|
69
|
-
"@stnd/press": "0.7.
|
|
70
|
-
"@stnd/store": "0.5.
|
|
71
|
-
"@stnd/styles": "0.5.
|
|
72
|
-
"@stnd/themes": "0.5.
|
|
73
|
-
"@stnd/ui": "0.5.
|
|
74
|
-
"@stnd/utils": "0.5.
|
|
61
|
+
"@stnd/client": "0.5.1",
|
|
62
|
+
"@stnd/cloudflare": "0.5.1",
|
|
63
|
+
"@stnd/fonts": "0.5.1",
|
|
64
|
+
"@stnd/icon": "0.2.1",
|
|
65
|
+
"@stnd/lab": "1.0.2",
|
|
66
|
+
"@stnd/launcher": "1.0.2",
|
|
67
|
+
"@stnd/layout": "0.5.2",
|
|
68
|
+
"@stnd/log": "0.5.1",
|
|
69
|
+
"@stnd/modules": "0.5.3",
|
|
70
|
+
"@stnd/press": "0.7.1",
|
|
71
|
+
"@stnd/store": "0.5.1",
|
|
72
|
+
"@stnd/styles": "0.5.3",
|
|
73
|
+
"@stnd/themes": "0.5.2",
|
|
74
|
+
"@stnd/ui": "0.5.2",
|
|
75
|
+
"@stnd/utils": "0.5.1"
|
|
75
76
|
},
|
|
76
77
|
"devDependencies": {
|
|
77
78
|
"@playwright/test": "^1.61.1",
|
package/src/loader.js
CHANGED
|
@@ -60,6 +60,19 @@ const ModuleManifestSchema = z
|
|
|
60
60
|
dependencies: z.array(z.string()).optional(),
|
|
61
61
|
config: z.record(z.any()).optional(),
|
|
62
62
|
routes: z.array(RouteSchema).optional(),
|
|
63
|
+
// Where this module is allowed to exist. `domain` lists the canonical
|
|
64
|
+
// domain(s) it belongs to; the module's routes and hooks disappear
|
|
65
|
+
// everywhere else. The framework never resolves a hostname itself — the
|
|
66
|
+
// app tells the guard which canonical domain the current request is on,
|
|
67
|
+
// so "standard.garden" still matches while developing on localhost.
|
|
68
|
+
// Absent = the module exists everywhere (the default for every module
|
|
69
|
+
// that doesn't opt in).
|
|
70
|
+
filter: z
|
|
71
|
+
.object({
|
|
72
|
+
domain: z.union([z.string(), z.array(z.string())]).optional(),
|
|
73
|
+
})
|
|
74
|
+
.passthrough()
|
|
75
|
+
.optional(),
|
|
63
76
|
hooks: z.record(z.union([HookSchema, z.array(HookSchema)])).optional(),
|
|
64
77
|
styles: z.array(z.string()).optional(),
|
|
65
78
|
scripts: z.array(ScriptSchema).optional(),
|
|
@@ -183,7 +196,7 @@ export async function loadModules({
|
|
|
183
196
|
const task = tasks[index];
|
|
184
197
|
if (result.status === "rejected") {
|
|
185
198
|
log.fatal(
|
|
186
|
-
`Failed to load module: ${task.resolvedPath}\nSee: https://stnd.build/manual/
|
|
199
|
+
`Failed to load module: ${task.resolvedPath}\nSee: https://stnd.build/manual/diagnostic/module-load-failure`,
|
|
187
200
|
result.reason,
|
|
188
201
|
);
|
|
189
202
|
}
|
|
@@ -196,7 +209,7 @@ export async function loadModules({
|
|
|
196
209
|
if (task.sourceModule) {
|
|
197
210
|
errorMsg += `\n Required by: "${task.sourceModule}" module`;
|
|
198
211
|
}
|
|
199
|
-
errorMsg += `\nSee: https://stnd.build/manual/
|
|
212
|
+
errorMsg += `\nSee: https://stnd.build/manual/diagnostic/core-is-not-a-module`;
|
|
200
213
|
log.fatal(errorMsg);
|
|
201
214
|
}
|
|
202
215
|
|
|
@@ -210,7 +223,7 @@ export async function loadModules({
|
|
|
210
223
|
if (task.sourceModule) {
|
|
211
224
|
errorMsg += ` Required by: "${task.sourceModule}" module\n`;
|
|
212
225
|
}
|
|
213
|
-
errorMsg += `See: https://stnd.build/manual/
|
|
226
|
+
errorMsg += `See: https://stnd.build/manual/diagnostic/content-validation`;
|
|
214
227
|
log.fatal(errorMsg);
|
|
215
228
|
}
|
|
216
229
|
|
|
@@ -225,7 +238,7 @@ export async function loadModules({
|
|
|
225
238
|
`\n Path 1: ${existingPath}` +
|
|
226
239
|
`\n Path 2: ${resolvedPath}` +
|
|
227
240
|
`\n Every module must have a unique ID.` +
|
|
228
|
-
`\n See: https://stnd.build/manual/
|
|
241
|
+
`\n See: https://stnd.build/manual/diagnostic/module-duplicate-id`,
|
|
229
242
|
);
|
|
230
243
|
}
|
|
231
244
|
}
|
|
@@ -276,7 +289,7 @@ export async function loadModules({
|
|
|
276
289
|
|
|
277
290
|
if (!idToPath.has(depId)) {
|
|
278
291
|
log.fatal(
|
|
279
|
-
`Module "${moduleId}" has unresolved dependency: "${rawDep}"\nSee: https://stnd.build/manual/
|
|
292
|
+
`Module "${moduleId}" has unresolved dependency: "${rawDep}"\nSee: https://stnd.build/manual/diagnostic/module-dependency-unresolved`,
|
|
280
293
|
);
|
|
281
294
|
}
|
|
282
295
|
|
|
@@ -302,13 +315,17 @@ export async function loadModules({
|
|
|
302
315
|
|
|
303
316
|
if (sortedIds.length !== allIds.length) {
|
|
304
317
|
log.fatal(
|
|
305
|
-
"Circular dependency detected in module graph.\nSee: https://stnd.build/manual/
|
|
318
|
+
"Circular dependency detected in module graph.\nSee: https://stnd.build/manual/diagnostic/module-dependency-circular",
|
|
306
319
|
);
|
|
307
320
|
}
|
|
308
321
|
|
|
309
322
|
// 3. Process
|
|
310
323
|
const modules = {
|
|
311
324
|
runtime: [],
|
|
325
|
+
// Server-only: {id, filter, routes} for modules declaring `filter.domain`.
|
|
326
|
+
// See the comment at the injectRoute() call site for why this exists
|
|
327
|
+
// separately from `runtime` (which is the client-bundled payload).
|
|
328
|
+
domainFilters: [],
|
|
312
329
|
config: [],
|
|
313
330
|
aliases: {},
|
|
314
331
|
ui: {},
|
|
@@ -405,6 +422,22 @@ export async function loadModules({
|
|
|
405
422
|
});
|
|
406
423
|
}
|
|
407
424
|
|
|
425
|
+
// A module declaring `filter: { domain }` needs its route PATTERNS at
|
|
426
|
+
// request time, server-side, to turn "hidden in the UI" into "the URL
|
|
427
|
+
// itself answers 404" (routes are injected once here, at build time, so
|
|
428
|
+
// hiding a button never stops the URL from working). This is collected
|
|
429
|
+
// separately from `modules.runtime` because that array is the CLIENT
|
|
430
|
+
// payload and deliberately strips `routes` (see the destructuring a few
|
|
431
|
+
// hundred lines down) — bundling every route pattern to the browser for
|
|
432
|
+
// a check only the server needs would be pure waste.
|
|
433
|
+
if (def.filter) {
|
|
434
|
+
modules.domainFilters.push({
|
|
435
|
+
id: def.id,
|
|
436
|
+
filter: def.filter,
|
|
437
|
+
routes: def.routes || [],
|
|
438
|
+
});
|
|
439
|
+
}
|
|
440
|
+
|
|
408
441
|
if (def.styles) {
|
|
409
442
|
def.styles.forEach((style) => {
|
|
410
443
|
// Preserve external URLs (https:// or protocol-relative //) as-is so they don't get path-resolved into local files.
|
|
@@ -520,7 +553,7 @@ export async function loadModules({
|
|
|
520
553
|
`\n Module "${def.id}" registered a ${kind} entry (${entryPath}), but "${hookName}" was already classified as ${previousKind} by an earlier module.` +
|
|
521
554
|
`\n A hook name must consistently resolve to one kind: logic hooks are ".js/.ts" entries not prefixed "launcher:" or suffixed ":action"; everything else is a UI/action zone.` +
|
|
522
555
|
`\n Rename this hook or make its entry type consistent with the other registrations.` +
|
|
523
|
-
`\n See: https://stnd.build/manual/
|
|
556
|
+
`\n See: https://stnd.build/manual/diagnostic/hook-kind-mismatch`,
|
|
524
557
|
);
|
|
525
558
|
}
|
|
526
559
|
hookKindByName.set(hookName, kind);
|
|
@@ -545,7 +578,7 @@ export async function loadModules({
|
|
|
545
578
|
`Hook "${hookName}" (module "${def.id}") sets "server: true", but this hook classifies as a UI/action zone contribution, which has no server/client split.` +
|
|
546
579
|
`\n "server: true" only has an effect on logic hooks (".js/.ts" entries not prefixed "launcher:" or suffixed ":action").` +
|
|
547
580
|
`\n Remove "server: true", or rename this hook off the "launcher:"/":action" pattern if it must stay out of the client bundle.` +
|
|
548
|
-
`\n See: https://stnd.build/manual/
|
|
581
|
+
`\n See: https://stnd.build/manual/diagnostic/hook-server-flag-ignored`,
|
|
549
582
|
);
|
|
550
583
|
}
|
|
551
584
|
if (!modules.ui[hookName]) modules.ui[hookName] = [];
|
package/src/virtual.js
CHANGED
|
@@ -96,6 +96,21 @@ export function generateModulesModule(runtimeModules) {
|
|
|
96
96
|
);
|
|
97
97
|
}
|
|
98
98
|
|
|
99
|
+
/**
|
|
100
|
+
* Server-only virtual module carrying route patterns for domain-filtered
|
|
101
|
+
* modules (`filter: { domain }` in a manifest). Deliberately separate from
|
|
102
|
+
* `generateModulesModule`/`generateModulesMetadataModule`, whose payload is
|
|
103
|
+
* bundled to the client — route patterns have no reason to ship there.
|
|
104
|
+
* Consumed by the app's own request middleware to 404 a build-time-injected
|
|
105
|
+
* route that shouldn't answer on the current domain.
|
|
106
|
+
*
|
|
107
|
+
* @param {{id: string, filter: object, routes: Array<{path: string}>}[]} domainFilterModules
|
|
108
|
+
* @returns {string} module source
|
|
109
|
+
*/
|
|
110
|
+
export function generateModuleDomainsModule(domainFilterModules = []) {
|
|
111
|
+
return `export const modules = ${JSON.stringify(domainFilterModules)};`;
|
|
112
|
+
}
|
|
113
|
+
|
|
99
114
|
export function generateModulesMetadataModule(runtimeModules) {
|
|
100
115
|
// Build a sanitized client payload with functions removed (safe for JSON serialization)
|
|
101
116
|
const sanitizedModules = runtimeModules.map((m) => {
|
package/standard.js
CHANGED
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
generateConfigModule,
|
|
17
17
|
generateModulesModule,
|
|
18
18
|
generateModulesMetadataModule,
|
|
19
|
+
generateModuleDomainsModule,
|
|
19
20
|
generateStylesModule,
|
|
20
21
|
generateScriptsModule,
|
|
21
22
|
generateComponentsModule,
|
|
@@ -225,7 +226,7 @@ export default function standard(options = {}) {
|
|
|
225
226
|
log.warn(
|
|
226
227
|
`Modules define actions but 'src/actions/index.ts' is missing.\n` +
|
|
227
228
|
` Actions will trigger 404 errors without this bridge file.\n` +
|
|
228
|
-
` See fix: https://stnd.build/manual/
|
|
229
|
+
` See fix: https://stnd.build/manual/diagnostic/actions-404`,
|
|
229
230
|
);
|
|
230
231
|
}
|
|
231
232
|
}
|
|
@@ -344,6 +345,8 @@ export default function standard(options = {}) {
|
|
|
344
345
|
return generateModulesModule(modules.runtime);
|
|
345
346
|
if (id === "\0virtual-stnd/modules-metadata")
|
|
346
347
|
return generateModulesMetadataModule(modules.runtime);
|
|
348
|
+
if (id === "\0virtual-stnd/module-domains")
|
|
349
|
+
return generateModuleDomainsModule(modules.domainFilters);
|
|
347
350
|
if (id === "\0virtual-stnd/components")
|
|
348
351
|
return generateComponentsModule(modules.ui);
|
|
349
352
|
if (id === "\0virtual-stnd/middleware")
|
|
@@ -377,37 +380,6 @@ export default function standard(options = {}) {
|
|
|
377
380
|
.filter(Boolean),
|
|
378
381
|
];
|
|
379
382
|
|
|
380
|
-
const astro7Workarounds = {
|
|
381
|
-
name: "stnd-astro-7-workarounds",
|
|
382
|
-
enforce: "post",
|
|
383
|
-
configEnvironment(name, config) {
|
|
384
|
-
if (name === "prerender") {
|
|
385
|
-
config.build ||= {};
|
|
386
|
-
config.build.rollupOptions ||= {};
|
|
387
|
-
config.build.rollupOptions.output = {
|
|
388
|
-
...config.build.rollupOptions.output,
|
|
389
|
-
entryFileNames: "prerender-entry.[hash].mjs",
|
|
390
|
-
format: "esm"
|
|
391
|
-
};
|
|
392
|
-
}
|
|
393
|
-
},
|
|
394
|
-
options(rollupOptions) {
|
|
395
|
-
if (this.environment?.name === "client") {
|
|
396
|
-
const envConfig = this.environment.config;
|
|
397
|
-
const rolldownInput = envConfig?.build?.rolldownOptions?.input;
|
|
398
|
-
const isDefaultInput = !rollupOptions.input ||
|
|
399
|
-
rollupOptions.input === "index.html" ||
|
|
400
|
-
(typeof rollupOptions.input === "string" && rollupOptions.input.endsWith("index.html")) ||
|
|
401
|
-
(Array.isArray(rollupOptions.input) && rollupOptions.input.length === 0);
|
|
402
|
-
|
|
403
|
-
if (rolldownInput && isDefaultInput) {
|
|
404
|
-
rollupOptions.input = rolldownInput;
|
|
405
|
-
}
|
|
406
|
-
}
|
|
407
|
-
return rollupOptions;
|
|
408
|
-
}
|
|
409
|
-
};
|
|
410
|
-
|
|
411
383
|
updateConfig({
|
|
412
384
|
// Speed is a respect (Standard value #2): every Standard app prefetches
|
|
413
385
|
// links in the viewport and speculatively prerenders them. These are
|
|
@@ -425,7 +397,7 @@ export default function standard(options = {}) {
|
|
|
425
397
|
},
|
|
426
398
|
integrations: moduleIntegrations,
|
|
427
399
|
vite: {
|
|
428
|
-
plugins: [virtualModuleStnd
|
|
400
|
+
plugins: [virtualModuleStnd],
|
|
429
401
|
resolve: {
|
|
430
402
|
alias: aliases,
|
|
431
403
|
dedupe: ["svelte"],
|