@stnd/core 0.24.38 → 0.24.40

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.
Files changed (3) hide show
  1. package/README.md +39 -21
  2. package/package.json +7 -7
  3. package/src/loader.js +24 -9
package/README.md CHANGED
@@ -61,11 +61,11 @@ The concept relies on a strict separation between the **Core** (static, tested)
61
61
 
62
62
  ---
63
63
 
64
- ## 🔌 Module System (Spores)
64
+ ## Module System (Spores)
65
65
 
66
66
  Modules are auto-discovered and orchestrate both logic and interface via a **Unified Hooks** system.
67
67
 
68
- ### Anatomy of a module (`index.module.js`):
68
+ ### Anatomy of a Module (`index.module.js`)
69
69
 
70
70
  ```javascript
71
71
  export default {
@@ -87,21 +87,40 @@ export default {
87
87
  };
88
88
  ```
89
89
 
90
- ### Virtual Modules & Rendering:
90
+ ### Virtual Modules & Rendering
91
91
 
92
- - `virtual:stnd/hooks`: To execute **logic hooks** — `.js`/`.ts` entries not prefixed `launcher:` and not suffixed `:action` (those two patterns always classify as UI/action zone contributions instead — see `virtual:stnd/components` below — mismatched classification of the same hook name across registrations is a build-time fatal error). Two calling conventions over the same `hooks:` registrations: `runHook(name, ...args)` fans a call out to every registered handler and collects their results into an array (e.g. `astro:build:done`, `stnd:rss:register`); `runPipeline(name, initialValue, ...args)` threads one value through every registered handler in sequence, each required to explicitly return the (possibly transformed) value — for "let each interested module inspect and optionally override the same thing" (e.g. `apps/stnd.gd`'s `note:render`, where password-gate overrides how a locked note renders without the route naming it). Both isolate handler failures — a throwing handler is logged and skipped, not left to abort the rest. Handlers registered with `server: true` are excluded from the client build of this virtual module — required for any handler touching server-only data (DB calls, secrets), since client-bundled consumers like `packages/launcher/launcher.ts` import from the same module; setting `server: true` on a `launcher:`/`:action`-classified entry is a fatal error rather than a silent no-op, since that pool has no SSR/client split.
93
- - `virtual:stnd/components`: Used by the `<Hook />` component for UI rendering, and read directly (`extensions[zone]`) by client code that needs callable functions, not just components — e.g. `launcher:action`/`graft:action` entries.
92
+ The module loader compiles `hooks:` registrations into two dedicated virtual modules based on entry classification:
94
93
 
95
- **UI Consumption:**
94
+ | Virtual Module | Entry Classification | Primary Consumers |
95
+ | :--- | :--- | :--- |
96
+ | `virtual:stnd/hooks` | **Logic hooks** — `.js` / `.ts` entries (excluding `launcher:*` and `*:action`) | `runHook()` and `runPipeline()` |
97
+ | `virtual:stnd/components` | **UI & action zones** — `.astro` / `.svelte` / `.md` components, plus all `launcher:*` and `*:action` entries | `<Hook zone="..." />` and `extensions[zone]` |
98
+
99
+ #### Calling Conventions (`virtual:stnd/hooks`)
100
+
101
+ - **Fan-out (`runHook(name, ...args)`)**: Calls every registered handler for `name` and collects their return values into an array (e.g. `astro:build:done`, `stnd:rss:register`).
102
+ - **Sequential Pipeline (`runPipeline(name, initialValue, ...args)`)**: Threads a single value through every registered handler in sequence. Each handler must explicitly return the (optionally transformed) value — ideal when modules inspect and override a shared descriptor (e.g. `note:render` in `apps/stnd.gd`).
103
+
104
+ #### Isolation & Bundling Guarantees
105
+
106
+ - **Failure Isolation**: Both `runHook` and `runPipeline` isolate handler exceptions — a throwing handler is logged and skipped so it cannot abort sibling handlers.
107
+ - **Server-Only Filtering (`server: true`)**: Handlers marked with `server: true` are stripped from the client bundle of `virtual:stnd/hooks`. Use this for any handler touching databases or secrets when client modules (like `@stnd/launcher`) also import from `virtual:stnd/hooks`.
108
+ - **Build-Time Classification Guards**:
109
+ - Registering the same hook name as both a logic hook and a UI/action zone across modules throws a build-time fatal error ([`hook-kind-mismatch`](https://stnd.build/manual/diagnostic/hook-kind-mismatch)).
110
+ - Setting `server: true` on a `launcher:*` or `*:action` entry throws a build-time fatal error ([`hook-server-flag-ignored`](https://stnd.build/manual/diagnostic/hook-server-flag-ignored)), since `virtual:stnd/components` has no SSR/client split.
111
+
112
+ #### UI Consumption
96
113
 
97
114
  ```astro
115
+ ---
98
116
  import Hook from "@stnd/core/Hook.astro";
99
- <Hook id="header:top" />
117
+ ---
118
+ <Hook zone="header:top" />
100
119
  ```
101
120
 
102
121
  ---
103
122
 
104
- ## 🛠 Helpers Reference (Core & Server)
123
+ ## Helpers Reference (Core & Server)
105
124
 
106
125
  Importable via `@stnd/server/[filename]` or `@stnd/utils/[filename]`.
107
126
 
@@ -140,7 +159,7 @@ Importable via `@stnd/server/[filename]` or `@stnd/utils/[filename]`.
140
159
  | `getReadingTime` | `getReadingTime(text)` | Estimated reading time. |
141
160
  | `validateEmail` | `validateEmail(email)` | Simplified email validation. |
142
161
 
143
- ### Other Helpers:
162
+ ### Other Helpers
144
163
 
145
164
  - `@stnd/server/cors.js`: `getCorsHeaders`, `handleCorsPreflight`.
146
165
  - `@stnd/server/errors.js`: Standardized error classes (`StandardError`, `AuthError`, etc.).
@@ -150,11 +169,11 @@ Importable via `@stnd/server/[filename]` or `@stnd/utils/[filename]`.
150
169
 
151
170
  ---
152
171
 
153
- ## 🎨 Design Tokens (CSS Variables)
172
+ ## Design Tokens (CSS Variables)
154
173
 
155
174
  The system uses CSS variables (`--stnd-*` or root variables `:root`) for consistency.
156
175
 
157
- ### 📐 Geometry & Rhythm (`packages/styles/_standard-01-token.scss`)
176
+ ### Geometry & Rhythm (`packages/styles/_standard-01-token.scss`)
158
177
 
159
178
  | Variable | Default Value | Description |
160
179
  | :--- | :--- | :--- |
@@ -170,7 +189,7 @@ The system uses CSS variables (`--stnd-*` or root variables `:root`) for consist
170
189
  | `--rhythm-block-scale` | `2` | Multiplier for non-text block elements. |
171
190
  | `--radius` | `--leading` | Border radius consistent with rhythm. |
172
191
 
173
- ### 🎨 Colors (`packages/styles/_standard-02-color.scss`)
192
+ ### Colors (`packages/styles/_standard-02-color.scss`)
174
193
 
175
194
  OKLCH-based system generating harmony from `--color-accent`.
176
195
 
@@ -184,7 +203,7 @@ OKLCH-based system generating harmony from `--color-accent`.
184
203
  | `--color-[green\|red\|yellow\|blue]` | Pigment colors derived from accent. |
185
204
  | `--color-success\|error\|warning` | Semantic states. |
186
205
 
187
- ### 🛠 SCSS Breakpoints
206
+ ### SCSS Breakpoints
188
207
 
189
208
  - `$mobile`: 600px
190
209
  - `$small`: 768px
@@ -193,7 +212,7 @@ OKLCH-based system generating harmony from `--color-accent`.
193
212
 
194
213
  ---
195
214
 
196
- ## 💡 Notes for AI
215
+ ## Notes for AI
197
216
 
198
217
  - **Module Integration**: Standardized on *“Module”* terminology (formerly Folios/Spores).
199
218
  - **Actions**: Core actions are now fully decanted into their respective modules and aggregated via `virtual:stnd/actions`.
@@ -223,11 +242,10 @@ Part of the [Road to Public Release](../README.md#-road-to-public-release) (Phas
223
242
  - [ ] **`adeSsrDeps` → `@stnd/cloudflare`** (not core) — decided during Phase 2: [priority:: 3] [token_scale:: 3] [created:: 2026-07-14] [area:: framework]
224
243
  the SSR/workerd optimizeDeps fix belongs with the Cloudflare package, split from the app-specific deps that stay in the app.
225
244
 
226
- ## 🔗 Liens & Normes
245
+ ## Links & Standards
227
246
 
228
- Ce paquet implémente les principes directeurs de l’écosystème Standard. Pour plus de détails sur le fonctionnement global, consultez les documents suivants :
229
- - **Architecture Système** : [4.1 Architecture](../../apps/stnd.build/modules/manual/content/4-system/4.1-architecture.md)
230
- - **Principes de Code** : [4.2 Code Standards](../../apps/stnd.build/modules/manual/content/4-system/4.2-code-standards.md)
231
- - **Normes de Documentation** : [4.9 Normes README](../../apps/stnd.build/modules/manual/content/4-system/4.9-readme-standard.md)
232
- - **Le Standard** : [packages/README.md](../README.md)
233
- - **Entrée Monorepo** : [README.md](../../README.md)
247
+ This package implements the core architectural principles of the Standard ecosystem. For further details on how the framework operates globally, see:
248
+ - **System Architecture**: [Framework Guide & Invariants](../../apps/stnd.build/modules/manual/content/system/framework-guide.md)
249
+ - **Engineering Philosophy**: [Engineering Philosophy](../../apps/stnd.build/modules/manual/content/philosophy/engineering-philosophy.md)
250
+ - **Package Ecosystem**: [packages/README.md](../README.md)
251
+ - **Monorepo Root**: [README.md](../../README.md)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stnd/core",
3
- "version": "0.24.38",
3
+ "version": "0.24.40",
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",
@@ -63,16 +63,16 @@
63
63
  "@stnd/fonts": "0.8.1",
64
64
  "@stnd/icon": "0.10.0",
65
65
  "@stnd/lab": "1.6.1",
66
- "@stnd/launcher": "1.10.2",
66
+ "@stnd/launcher": "1.10.3",
67
67
  "@stnd/layout": "0.10.1",
68
- "@stnd/modules": "0.14.0",
68
+ "@stnd/log": "0.6.0",
69
+ "@stnd/modules": "0.14.2",
69
70
  "@stnd/press": "0.13.2",
70
- "@stnd/styles": "0.27.2",
71
71
  "@stnd/store": "0.6.0",
72
+ "@stnd/styles": "0.28.0",
73
+ "@stnd/themes": "0.26.5",
72
74
  "@stnd/ui": "0.16.1",
73
- "@stnd/utils": "0.10.0",
74
- "@stnd/log": "0.6.0",
75
- "@stnd/themes": "0.26.4"
75
+ "@stnd/utils": "0.10.0"
76
76
  },
77
77
  "devDependencies": {
78
78
  "@playwright/test": "^1.61.1",
package/src/loader.js CHANGED
@@ -129,19 +129,33 @@ export async function loadModules({
129
129
  log.success("Initializing module engine...");
130
130
 
131
131
  const modulePatterns = buildModulePatterns(finalConfig);
132
- const require = createRequire(import.meta.url);
132
+ const coreRequire = createRequire(import.meta.url);
133
+ let appRequire = coreRequire;
134
+ try {
135
+ appRequire = createRequire(path.join(appRoot, "package.json"));
136
+ } catch {
137
+ // Fallback to coreRequire when appRoot has no package.json
138
+ }
133
139
 
134
140
  const discoveredFiles = discoverModuleFiles(appRoot, modulePatterns);
135
141
 
136
- const resolveModulePath = (idOrPath) => {
142
+ const resolveModulePath = (idOrPath, fromFile = null) => {
137
143
  if (path.isAbsolute(idOrPath)) return idOrPath;
138
144
 
139
145
  const tryResolve = (id) => {
140
- try {
141
- return require.resolve(id);
142
- } catch {
143
- return null;
146
+ for (const req of [
147
+ appRequire,
148
+ fromFile ? createRequire(fromFile) : null,
149
+ coreRequire,
150
+ ]) {
151
+ if (!req) continue;
152
+ try {
153
+ return req.resolve(id);
154
+ } catch {
155
+ // try next resolver
156
+ }
144
157
  }
158
+ return null;
145
159
  };
146
160
 
147
161
  const isBare =
@@ -175,8 +189,8 @@ export async function loadModules({
175
189
  while (loadQueue.length > 0) {
176
190
  const batch = loadQueue.splice(0, loadQueue.length);
177
191
  const tasks = batch
178
- .map(({ rawId, origin, sourceModule }) => {
179
- const resolvedPath = resolveModulePath(rawId);
192
+ .map(({ rawId, origin, sourceModule, sourceFile }) => {
193
+ const resolvedPath = resolveModulePath(rawId, sourceFile);
180
194
  if (!resolvedPath || processedPaths.has(resolvedPath)) return null;
181
195
  processedPaths.add(resolvedPath);
182
196
  return { rawId, origin, sourceModule, resolvedPath };
@@ -254,6 +268,7 @@ export async function loadModules({
254
268
  rawId: depId,
255
269
  origin: "dependency",
256
270
  sourceModule: def.id,
271
+ sourceFile: resolvedPath,
257
272
  });
258
273
  }
259
274
  }
@@ -281,7 +296,7 @@ export async function loadModules({
281
296
  for (const rawDep of def.dependencies) {
282
297
  let depId = rawDep;
283
298
  if (!idToPath.has(depId)) {
284
- const resolvedDepPath = resolveModulePath(rawDep);
299
+ const resolvedDepPath = resolveModulePath(rawDep, pathStr);
285
300
  if (resolvedDepPath && moduleMap.has(resolvedDepPath)) {
286
301
  depId = moduleMap.get(resolvedDepPath).def.id;
287
302
  }