@foxford/den 3.2.0 → 3.2.2

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.mdx CHANGED
@@ -125,6 +125,41 @@ export class ContentPageVM extends ViewModel<State> {
125
125
 
126
126
  Редирект объявляется там же: `{ redirect: '/user/login' }` — без статуса это `302`.
127
127
 
128
+ ### Приложение без рендера
129
+
130
+ `view` и `viewLayer` объявляются парой либо не объявляются вовсе. Приложение без них сообщает
131
+ документу данные, а свой кусок страницы рисует кто-то другой — например, клиент монолита.
132
+
133
+ ```ts
134
+ export default {
135
+ name: 'spa-portal',
136
+ units: [],
137
+ viewModels: [shellVm], // describeDocument отдаёт данные документа и артефакты адреса
138
+ }
139
+ ```
140
+
141
+ ```ts
142
+ describeDocument(): DocumentContribution {
143
+ const { shell } = this.state.get()
144
+
145
+ return {
146
+ // состав знает шаблон документа хоста, ядру перечислять его нечем
147
+ data: { containerId: shell.containerId, manifest: shell.manifest },
148
+ // чанки зависят от адреса, поэтому их знает рендер, а не сборка
149
+ assets: { css: shell.css, js: shell.js, scripts: 'classic' },
150
+ }
151
+ }
152
+ ```
153
+
154
+ Жизненный цикл у такого приложения тот же — контейнер, единицы, активация VM, вклад в документ, —
155
+ укорачивается ровно отрисовка: `html` в ответе пустой, `state` посчитан как обычно. `den build`
156
+ собирает его так же: `den.config.ts` без пресета означает, что клиентского выхода не бывает,
157
+ и остаются серверный дескриптор с точкой входа процесса.
158
+
159
+ `scripts: 'classic'` нужен там, где чанки собраны не ES-модулями: вид скрипта знает сборка, а
160
+ теги печатает хост, и выбрать между `type="module"` и обычным `defer` он может только по этому
161
+ признаку.
162
+
128
163
  ## Сетевой транспорт: то же самое по HTTP
129
164
 
130
165
  Приложение поднимается отдельным сервисом **без единой строки своего серверного кода** —
package/bin/den.cjs CHANGED
@@ -1,7 +1,8 @@
1
1
  "use strict";
2
2
 
3
3
 
4
- var _chunkRRR2DJMNcjs = require('../chunk-RRR2DJMN.cjs');
4
+ var _chunk3X4WVHUUcjs = require('../chunk-3X4WVHUU.cjs');
5
+ require('../chunk-KQAB6C6Q.cjs');
5
6
  require('../chunk-A6ZPAM6Z.cjs');
6
7
 
7
8
 
@@ -69,11 +70,11 @@ function run(argv) {
69
70
  }
70
71
  const target = { base: options["base"], root: (_a = options["root"]) != null ? _a : process.cwd() };
71
72
  if (command === "build") {
72
- yield _chunkRRR2DJMNcjs.buildApp.call(void 0, target);
73
+ yield _chunk3X4WVHUUcjs.buildApp.call(void 0, target);
73
74
  return;
74
75
  }
75
76
  if (command === "dev") {
76
- yield _chunkRRR2DJMNcjs.devApp.call(void 0, target);
77
+ yield _chunk3X4WVHUUcjs.devApp.call(void 0, target);
77
78
  return;
78
79
  }
79
80
  fail(`\u043D\u0435\u0438\u0437\u0432\u0435\u0441\u0442\u043D\u0430\u044F \u043A\u043E\u043C\u0430\u043D\u0434\u0430 \xAB${command}\xBB`);
package/bin/den.js CHANGED
@@ -1,7 +1,8 @@
1
1
  import {
2
2
  buildApp,
3
3
  devApp
4
- } from "../chunk-BMI5HNGE.js";
4
+ } from "../chunk-XU5HUIPK.js";
5
+ import "../chunk-RG7ZV5BD.js";
5
6
  import "../chunk-TPBI6TOU.js";
6
7
  import {
7
8
  __async
package/builder/index.cjs CHANGED
@@ -2,11 +2,12 @@
2
2
 
3
3
 
4
4
 
5
- var _chunkRRR2DJMNcjs = require('../chunk-RRR2DJMN.cjs');
5
+ var _chunk3X4WVHUUcjs = require('../chunk-3X4WVHUU.cjs');
6
+ require('../chunk-KQAB6C6Q.cjs');
6
7
  require('../chunk-A6ZPAM6Z.cjs');
7
8
  require('../chunk-T5QVCXVB.cjs');
8
9
 
9
10
 
10
11
 
11
12
 
12
- exports.buildApp = _chunkRRR2DJMNcjs.buildApp; exports.defineAppConfig = _chunkRRR2DJMNcjs.defineAppConfig; exports.devApp = _chunkRRR2DJMNcjs.devApp;
13
+ exports.buildApp = _chunk3X4WVHUUcjs.buildApp; exports.defineAppConfig = _chunk3X4WVHUUcjs.defineAppConfig; exports.devApp = _chunk3X4WVHUUcjs.devApp;
@@ -80,13 +80,18 @@ interface AppServerConfig {
80
80
  interface AppConfig {
81
81
  /** Модуль дескриптора; по умолчанию `src/index.ts`. */
82
82
  entry?: string;
83
- /** Пресет фреймворка — из пакета адаптера. */
84
- preset: AppBuildPreset;
83
+ /**
84
+ * Пресет фреймворка — из пакета адаптера.
85
+ *
86
+ * Не объявлен — приложение своего фрагмента не рисует (дескриптор без `view`), и клиентского
87
+ * выхода у него не бывает: собирать нечем и оживлять нечего.
88
+ */
89
+ preset?: AppBuildPreset;
85
90
  server?: AppServerConfig;
86
91
  /**
87
92
  * Последнее слово за приложением: правит собранный конфиг сборщика перед запуском.
88
93
  *
89
- * Щель на случай, когда коробки не хватило. Зовётся отдельно для каждого выхода, поэтому
94
+ * Щель на случай, когда умолчаний сборки не хватило. Зовётся отдельно для каждого выхода, поэтому
90
95
  * правка адресуется тому, кому предназначена.
91
96
  */
92
97
  vite?: (config: Record<string, unknown>, target: 'server' | 'client') => Record<string, unknown>;
@@ -80,13 +80,18 @@ interface AppServerConfig {
80
80
  interface AppConfig {
81
81
  /** Модуль дескриптора; по умолчанию `src/index.ts`. */
82
82
  entry?: string;
83
- /** Пресет фреймворка — из пакета адаптера. */
84
- preset: AppBuildPreset;
83
+ /**
84
+ * Пресет фреймворка — из пакета адаптера.
85
+ *
86
+ * Не объявлен — приложение своего фрагмента не рисует (дескриптор без `view`), и клиентского
87
+ * выхода у него не бывает: собирать нечем и оживлять нечего.
88
+ */
89
+ preset?: AppBuildPreset;
85
90
  server?: AppServerConfig;
86
91
  /**
87
92
  * Последнее слово за приложением: правит собранный конфиг сборщика перед запуском.
88
93
  *
89
- * Щель на случай, когда коробки не хватило. Зовётся отдельно для каждого выхода, поэтому
94
+ * Щель на случай, когда умолчаний сборки не хватило. Зовётся отдельно для каждого выхода, поэтому
90
95
  * правка адресуется тому, кому предназначена.
91
96
  */
92
97
  vite?: (config: Record<string, unknown>, target: 'server' | 'client') => Record<string, unknown>;
package/builder/index.js CHANGED
@@ -2,7 +2,8 @@ import {
2
2
  buildApp,
3
3
  defineAppConfig,
4
4
  devApp
5
- } from "../chunk-BMI5HNGE.js";
5
+ } from "../chunk-XU5HUIPK.js";
6
+ import "../chunk-RG7ZV5BD.js";
6
7
  import "../chunk-TPBI6TOU.js";
7
8
  import "../chunk-6FNC3XMI.js";
8
9
  export {
@@ -1,5 +1,8 @@
1
1
  "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { newObj[key] = obj[key]; } } } newObj.default = obj; return newObj; } } function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
2
2
 
3
+ var _chunkKQAB6C6Qcjs = require('./chunk-KQAB6C6Q.cjs');
4
+
5
+
3
6
  var _chunkA6ZPAM6Zcjs = require('./chunk-A6ZPAM6Z.cjs');
4
7
 
5
8
 
@@ -92,7 +95,9 @@ import config from ${JSON.stringify(configPath)}
92
95
  const server = config.server ?? {}
93
96
 
94
97
  async function setup() {
95
- viewAdapterOf(app.viewLayer).installResolver()
98
+ if (app.viewLayer) {
99
+ viewAdapterOf(app.viewLayer).installResolver()
100
+ }
96
101
  server.container?.(getAppContainer())
97
102
  await server.setup?.()
98
103
  }
@@ -109,11 +114,16 @@ export default descriptor
109
114
  function generateServerProcess(configPath, base, mode) {
110
115
  const assets = mode === "development" ? ` assets: { base: ${JSON.stringify(base)}, dir: fileURLToPath(new URL('./client', import.meta.url)) },
111
116
  ` : "";
112
- return `${mode === "development" ? "import { fileURLToPath } from 'node:url'\n\n" : ""}import { serveApp } from '@foxford/den/serve'
117
+ return `import { fileURLToPath } from 'node:url'
118
+
119
+ import { loadAppEnv, serveApp } from '@foxford/den/serve'
113
120
 
114
121
  import app from ${JSON.stringify(VIRTUAL_SERVER_APP)}
115
122
  import config from ${JSON.stringify(configPath)}
116
123
 
124
+ // \u0423\u043C\u043E\u043B\u0447\u0430\u043D\u0438\u044F \u043E\u043A\u0440\u0443\u0436\u0435\u043D\u0438\u044F \u2014 \u0434\u043E \u043F\u0435\u0440\u0432\u043E\u0433\u043E \u0447\u0442\u0435\u043D\u0438\u044F: \u043F\u043E\u0440\u0442 \u0438 \u0430\u0434\u0440\u0435\u0441\u0430 \u0447\u0438\u0442\u0430\u044E\u0442\u0441\u044F \u043D\u0438\u0436\u0435
125
+ loadAppEnv(fileURLToPath(new URL('..', import.meta.url)))
126
+
117
127
  await serveApp(app, {
118
128
  ${assets} configure: config.server?.configure,
119
129
  host: process.env.HOST,
@@ -126,7 +136,14 @@ ${assets} configure: config.server?.configure,
126
136
  function denAppModules(options) {
127
137
  const { assets, base, config, configPath, entry, mode } = options;
128
138
  const sources = {
129
- [VIRTUAL_CLIENT]: () => generateClientEntry(entry, config.preset.clientEntry),
139
+ // Клиентский вход спрашивают только у клиентского выхода, а его без пресета не собирают.
140
+ // Отказ здесь означает, что выход всё-таки запустили, — это ошибка команд, а не приложения
141
+ [VIRTUAL_CLIENT]: () => {
142
+ if (!config.preset) {
143
+ throw new Error("den: \u043A\u043B\u0438\u0435\u043D\u0442\u0441\u043A\u0438\u0439 \u0432\u0445\u043E\u0434 \u0437\u0430\u043F\u0440\u043E\u0448\u0435\u043D, \u043D\u043E \u043F\u0440\u0435\u0441\u0435\u0442 \u043D\u0435 \u043E\u0431\u044A\u044F\u0432\u043B\u0435\u043D \u2014 \u043E\u0436\u0438\u0432\u043B\u044F\u0442\u044C \u043D\u0435\u0447\u0435\u043C");
144
+ }
145
+ return generateClientEntry(entry, config.preset.clientEntry);
146
+ },
130
147
  [VIRTUAL_SERVER_APP]: () => generateServerApp(entry, configPath, assets),
131
148
  [VIRTUAL_SERVER_PROCESS]: () => generateServerProcess(configPath, base, mode)
132
149
  };
@@ -167,8 +184,8 @@ function loadAppConfig(root) {
167
184
  try {
168
185
  const loaded = yield server.ssrLoadModule(configPath);
169
186
  const config = loaded.default;
170
- if (!(config == null ? void 0 : config.preset)) {
171
- throw new Error(`den: ${CONFIG_FILE} \u043D\u0435 \u043E\u0431\u044A\u044F\u0432\u043B\u044F\u0435\u0442 \u043F\u0440\u0435\u0441\u0435\u0442 \u0444\u0440\u0435\u0439\u043C\u0432\u043E\u0440\u043A\u0430 \u2014 \u0441\u043E\u0431\u0438\u0440\u0430\u0442\u044C \u043D\u0435\u0447\u0435\u043C`);
187
+ if (!config) {
188
+ throw new Error(`den: ${CONFIG_FILE} \u043D\u0435 \u043E\u0442\u0434\u0430\u0451\u0442 \u043A\u043E\u043D\u0444\u0438\u0433 \u043F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u044F \u2014 \u0441\u043E\u0431\u0438\u0440\u0430\u0442\u044C \u043D\u0435\u0447\u0435\u043C`);
172
189
  }
173
190
  return { config, configPath };
174
191
  } finally {
@@ -186,7 +203,7 @@ function dependenciesOf(root) {
186
203
  return [...Object.keys((_a = manifest.dependencies) != null ? _a : {}), ...Object.keys((_b = manifest.devDependencies) != null ? _b : {})];
187
204
  }
188
205
  function viteConfigFor(options) {
189
- var _a, _b, _c;
206
+ var _a, _b, _c, _d, _e;
190
207
  const { assets, base, config, configPath, entryFileNames, input, mode, outDir, root, target } = options;
191
208
  const entry = (_a = config.entry) != null ? _a : "src/index.ts";
192
209
  const composed = {
@@ -211,34 +228,43 @@ function viteConfigFor(options) {
211
228
  // программный вызов он иначе не доедет.
212
229
  mode,
213
230
  plugins: [
214
- ...config.preset.plugins,
231
+ ...(_c = (_b = config.preset) == null ? void 0 : _b.plugins) != null ? _c : [],
215
232
  denAppModules({ assets, base, config, configPath, entry: _path2.default.join(root, entry), mode })
216
233
  ],
217
234
  root,
218
- ssr: { noExternal: (_c = (_b = config.server) == null ? void 0 : _b.noExternal) != null ? _c : [] }
235
+ ssr: { noExternal: (_e = (_d = config.server) == null ? void 0 : _d.noExternal) != null ? _e : [] }
219
236
  };
220
237
  return config.vite ? config.vite(composed, target) : composed;
221
238
  }
222
239
  function buildApp(options) {
223
240
  return _chunkT5QVCXVBcjs.__async.call(void 0, this, null, function* () {
224
- var _a, _b, _c, _d;
241
+ var _a, _b, _c;
225
242
  const { mode = "production" } = options;
226
243
  process.env["NODE_ENV"] = mode;
227
244
  const root = _path2.default.resolve(options.root);
245
+ _chunkKQAB6C6Qcjs.loadAppEnv.call(void 0, root);
228
246
  const { config, configPath } = yield loadAppConfig(root);
229
247
  const port = resolvePort((_a = config.server) == null ? void 0 : _a.port);
230
248
  const base = (_b = options.base) != null ? _b : "";
231
- const refusal = (_d = (_c = config.preset).validate) == null ? void 0 : _d.call(_c, dependenciesOf(root));
249
+ const { preset } = config;
250
+ const refusal = (_c = preset == null ? void 0 : preset.validate) == null ? void 0 : _c.call(preset, dependenciesOf(root));
232
251
  if (refusal !== void 0 && refusal !== null) {
233
252
  throw new Error(`den: ${refusal}`);
234
253
  }
235
254
  const { build } = yield Promise.resolve().then(() => _interopRequireWildcard(require("vite")));
236
255
  const shared = { base, config, configPath, mode, root };
237
256
  const empty = { css: [], js: [] };
238
- _chunkA6ZPAM6Zcjs.logger.info(`\u0441\u0431\u043E\u0440\u043A\u0430 \u043A\u043B\u0438\u0435\u043D\u0442\u0441\u043A\u043E\u0433\u043E \u0432\u044B\u0445\u043E\u0434\u0430 (${config.preset.view})`);
239
- yield build(viteConfigFor(_chunkT5QVCXVBcjs.__spreadProps.call(void 0, _chunkT5QVCXVBcjs.__spreadValues.call(void 0, {}, shared), { assets: empty, input: VIRTUAL_CLIENT, outDir: OUT_CLIENT, target: "client" })));
240
- const assets = collectAssets(_path2.default.join(root, OUT_CLIENT), base);
241
- _chunkA6ZPAM6Zcjs.logger.info(`\u0430\u0440\u0442\u0435\u0444\u0430\u043A\u0442\u044B: ${String(assets.js.length)} js, ${String(assets.css.length)} css`);
257
+ let assets = empty;
258
+ if (preset) {
259
+ _chunkA6ZPAM6Zcjs.logger.info(`\u0441\u0431\u043E\u0440\u043A\u0430 \u043A\u043B\u0438\u0435\u043D\u0442\u0441\u043A\u043E\u0433\u043E \u0432\u044B\u0445\u043E\u0434\u0430 (${preset.view})`);
260
+ yield build(
261
+ viteConfigFor(_chunkT5QVCXVBcjs.__spreadProps.call(void 0, _chunkT5QVCXVBcjs.__spreadValues.call(void 0, {}, shared), { assets: empty, input: VIRTUAL_CLIENT, outDir: OUT_CLIENT, target: "client" }))
262
+ );
263
+ assets = collectAssets(_path2.default.join(root, OUT_CLIENT), base);
264
+ _chunkA6ZPAM6Zcjs.logger.info(`\u0430\u0440\u0442\u0435\u0444\u0430\u043A\u0442\u044B: ${String(assets.js.length)} js, ${String(assets.css.length)} css`);
265
+ } else {
266
+ _chunkA6ZPAM6Zcjs.logger.info("\u043A\u043B\u0438\u0435\u043D\u0442\u0441\u043A\u043E\u0433\u043E \u0432\u044B\u0445\u043E\u0434\u0430 \u043D\u0435\u0442: \u043F\u0440\u0435\u0441\u0435\u0442 \u043D\u0435 \u043E\u0431\u044A\u044F\u0432\u043B\u0435\u043D, \u043F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u0435 \u0441\u0432\u043E\u0435\u0433\u043E \u0444\u0440\u0430\u0433\u043C\u0435\u043D\u0442\u0430 \u043D\u0435 \u0440\u0438\u0441\u0443\u0435\u0442");
267
+ }
242
268
  _chunkA6ZPAM6Zcjs.logger.info("\u0441\u0431\u043E\u0440\u043A\u0430 \u0441\u0435\u0440\u0432\u0435\u0440\u043D\u043E\u0433\u043E \u0434\u0435\u0441\u043A\u0440\u0438\u043F\u0442\u043E\u0440\u0430");
243
269
  yield build(
244
270
  viteConfigFor(_chunkT5QVCXVBcjs.__spreadProps.call(void 0, _chunkT5QVCXVBcjs.__spreadValues.call(void 0, {}, shared), {
@@ -309,7 +335,9 @@ function bannerLines(facts2) {
309
335
  `${label("\u043B\u043E\u043A\u0430\u043B\u044C\u043D\u043E")}http://localhost:${port}`,
310
336
  ...network === void 0 ? [] : [`${label("\u0432 \u0441\u0435\u0442\u0438")}http://${network}:${port}`],
311
337
  `${label("\u0440\u0443\u0447\u043A\u0438")}${ENDPOINTS.join(" ")}`,
312
- ...facts2.base === void 0 ? [] : [`${label("\u0430\u0440\u0442\u0435\u0444\u0430\u043A\u0442\u044B")}${facts2.base}`],
338
+ // Префикс печатается, только когда он есть: при относительной сборке артефакты идут с
339
+ // адреса самого приложения, а он уже назван строкой выше
340
+ ...facts2.base === void 0 || facts2.base === "" ? [] : [`${label("\u043F\u0440\u0435\u0444\u0438\u043A\u0441")}${facts2.base}`],
313
341
  `${label("\u0447\u0430\u043D\u043A\u0438")}${assetsSummary(facts2.assets)}`,
314
342
  `${label("\u0432\u044B\u0445\u043E\u0434\u044B")}${OUTPUTS.join(", ")}`,
315
343
  `${label("\u043F\u0440\u0430\u0432\u043A\u0438")}${facts2.watched.join(", ")}`,
@@ -0,0 +1,15 @@
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }// src/serve/env.ts
2
+ var _fs = require('fs'); var _fs2 = _interopRequireDefault(_fs);
3
+ var _path = require('path'); var _path2 = _interopRequireDefault(_path);
4
+ var ENV_FILE = ".env";
5
+ function loadAppEnv(root) {
6
+ const file = _path2.default.join(root, ENV_FILE);
7
+ if (!_fs2.default.existsSync(file)) {
8
+ return;
9
+ }
10
+ process.loadEnvFile(file);
11
+ }
12
+
13
+
14
+
15
+ exports.loadAppEnv = loadAppEnv;
@@ -0,0 +1,15 @@
1
+ // src/serve/env.ts
2
+ import fs from "fs";
3
+ import path from "path";
4
+ var ENV_FILE = ".env";
5
+ function loadAppEnv(root) {
6
+ const file = path.join(root, ENV_FILE);
7
+ if (!fs.existsSync(file)) {
8
+ return;
9
+ }
10
+ process.loadEnvFile(file);
11
+ }
12
+
13
+ export {
14
+ loadAppEnv
15
+ };
@@ -35,29 +35,33 @@ function getAppContainer() {
35
35
  import { log } from "@foxford/logger";
36
36
  var EMPTY_ROUTE = { params: {}, pathname: "", search: "" };
37
37
  var REDIRECT_FOUND = 302;
38
+ function registerIslandParts(container, descriptor, islandLog) {
39
+ var _a, _b, _c;
40
+ (_a = descriptor.viewLayer) == null ? void 0 : _a.register(container);
41
+ const viewModels = (_b = descriptor.viewModels) != null ? _b : [];
42
+ const slots = Object.entries((_c = descriptor.slots) != null ? _c : {});
43
+ islandLog.info(
44
+ `\u043E\u0441\u0442\u0440\u043E\u0432: \u043A\u043E\u043D\u0442\u0435\u0439\u043D\u0435\u0440 \u0441\u043E\u0437\u0434\u0430\u043D (units=${descriptor.units.length}, vms=${viewModels.length}, slots=${slots.length})`
45
+ );
46
+ for (const unit of descriptor.units) {
47
+ unit.register(container);
48
+ }
49
+ for (const vm of viewModels) {
50
+ vm.register(container);
51
+ }
52
+ for (const [name, slot] of slots) {
53
+ slot.register(container, name);
54
+ }
55
+ }
38
56
  function createIslandContainer(descriptor, options = {}) {
39
- var _a, _b, _c, _d, _e;
57
+ var _a, _b, _c;
40
58
  const { parent = getAppContainer() } = options;
41
59
  const appLog = (_b = log.getLogger((_a = descriptor.name) != null ? _a : "app")) != null ? _b : logger;
42
60
  const islandLog = (_c = appLog.getLogger("client")) != null ? _c : appLog;
43
61
  try {
44
62
  const container = parent.createChild();
45
63
  container.bind(LoggerToken).toValue(islandLog);
46
- descriptor.viewLayer.register(container);
47
- const viewModels = (_d = descriptor.viewModels) != null ? _d : [];
48
- const slots = Object.entries((_e = descriptor.slots) != null ? _e : {});
49
- islandLog.info(
50
- `\u043E\u0441\u0442\u0440\u043E\u0432: \u043A\u043E\u043D\u0442\u0435\u0439\u043D\u0435\u0440 \u0441\u043E\u0437\u0434\u0430\u043D (units=${descriptor.units.length}, vms=${viewModels.length}, slots=${slots.length})`
51
- );
52
- for (const unit of descriptor.units) {
53
- unit.register(container);
54
- }
55
- for (const vm of viewModels) {
56
- vm.register(container);
57
- }
58
- for (const [name, slot] of slots) {
59
- slot.register(container, name);
60
- }
64
+ registerIslandParts(container, descriptor, islandLog);
61
65
  return container;
62
66
  } catch (error) {
63
67
  islandLog.error("\u043E\u0441\u0442\u0440\u043E\u0432: \u0441\u0431\u043E\u0440\u043A\u0430 \u043A\u043E\u043D\u0442\u0435\u0439\u043D\u0435\u0440\u0430 \u0443\u043F\u0430\u043B\u0430", error);
@@ -111,6 +115,12 @@ function collectDocumentContribution(container, descriptor) {
111
115
  if (part.head) {
112
116
  contribution.head = __spreadValues(__spreadValues({}, contribution.head), part.head);
113
117
  }
118
+ if (part.data) {
119
+ contribution.data = __spreadValues(__spreadValues({}, contribution.data), part.data);
120
+ }
121
+ if (part.assets && contribution.assets === void 0) {
122
+ contribution.assets = part.assets;
123
+ }
114
124
  const declared = part.status !== void 0 || part.redirect !== void 0;
115
125
  if (declared && contribution.status === void 0) {
116
126
  contribution.status = (_b = part.status) != null ? _b : REDIRECT_FOUND;
@@ -149,7 +159,7 @@ function setupOnce(descriptor) {
149
159
  }
150
160
  function renderApp(descriptor, request) {
151
161
  return __async(this, null, function* () {
152
- var _a, _b;
162
+ var _a, _b, _c;
153
163
  yield setupOnce(descriptor);
154
164
  const route = {
155
165
  params: (_a = request.params) != null ? _a : {},
@@ -165,16 +175,21 @@ function renderApp(descriptor, request) {
165
175
  container.bind(RequestContextToken).toValue(request.context);
166
176
  }
167
177
  yield activateIslandViewModels(container, descriptor, route);
168
- const { head, redirect, status = STATUS_OK } = collectDocumentContribution(container, descriptor);
178
+ const contribution = collectDocumentContribution(container, descriptor);
179
+ const { data, head, redirect, status = STATUS_OK } = contribution;
169
180
  if (status !== STATUS_OK) {
170
- return { head, html: "", redirect, state: {}, status };
181
+ return { data, head, html: "", redirect, state: {}, status };
171
182
  }
183
+ const assets = (_c = contribution.assets) != null ? _c : descriptor.assets;
172
184
  const state = collectIslandState(container, descriptor);
185
+ if (descriptor.view === void 0 || descriptor.viewLayer === void 0) {
186
+ return { assets, data, head, html: "", state, status };
187
+ }
173
188
  const rendered = resolveViewAdapter(container).renderIsland(descriptor, {
174
189
  container,
175
190
  slots: request.slots
176
191
  });
177
- return { assets: descriptor.assets, head, headHtml: rendered.head, html: rendered.html, state, status };
192
+ return { assets, data, head, headHtml: rendered.head, html: rendered.html, state, status };
178
193
  } finally {
179
194
  yield container.dispose();
180
195
  }
@@ -35,29 +35,33 @@ function getAppContainer() {
35
35
  var _logger = require('@foxford/logger');
36
36
  var EMPTY_ROUTE = { params: {}, pathname: "", search: "" };
37
37
  var REDIRECT_FOUND = 302;
38
+ function registerIslandParts(container, descriptor, islandLog) {
39
+ var _a, _b, _c;
40
+ (_a = descriptor.viewLayer) == null ? void 0 : _a.register(container);
41
+ const viewModels = (_b = descriptor.viewModels) != null ? _b : [];
42
+ const slots = Object.entries((_c = descriptor.slots) != null ? _c : {});
43
+ islandLog.info(
44
+ `\u043E\u0441\u0442\u0440\u043E\u0432: \u043A\u043E\u043D\u0442\u0435\u0439\u043D\u0435\u0440 \u0441\u043E\u0437\u0434\u0430\u043D (units=${descriptor.units.length}, vms=${viewModels.length}, slots=${slots.length})`
45
+ );
46
+ for (const unit of descriptor.units) {
47
+ unit.register(container);
48
+ }
49
+ for (const vm of viewModels) {
50
+ vm.register(container);
51
+ }
52
+ for (const [name, slot] of slots) {
53
+ slot.register(container, name);
54
+ }
55
+ }
38
56
  function createIslandContainer(descriptor, options = {}) {
39
- var _a, _b, _c, _d, _e;
57
+ var _a, _b, _c;
40
58
  const { parent = getAppContainer() } = options;
41
59
  const appLog = (_b = _logger.log.getLogger((_a = descriptor.name) != null ? _a : "app")) != null ? _b : _chunkA6ZPAM6Zcjs.logger;
42
60
  const islandLog = (_c = appLog.getLogger("client")) != null ? _c : appLog;
43
61
  try {
44
62
  const container = parent.createChild();
45
63
  container.bind(_chunkA6ZPAM6Zcjs.LoggerToken).toValue(islandLog);
46
- descriptor.viewLayer.register(container);
47
- const viewModels = (_d = descriptor.viewModels) != null ? _d : [];
48
- const slots = Object.entries((_e = descriptor.slots) != null ? _e : {});
49
- islandLog.info(
50
- `\u043E\u0441\u0442\u0440\u043E\u0432: \u043A\u043E\u043D\u0442\u0435\u0439\u043D\u0435\u0440 \u0441\u043E\u0437\u0434\u0430\u043D (units=${descriptor.units.length}, vms=${viewModels.length}, slots=${slots.length})`
51
- );
52
- for (const unit of descriptor.units) {
53
- unit.register(container);
54
- }
55
- for (const vm of viewModels) {
56
- vm.register(container);
57
- }
58
- for (const [name, slot] of slots) {
59
- slot.register(container, name);
60
- }
64
+ registerIslandParts(container, descriptor, islandLog);
61
65
  return container;
62
66
  } catch (error) {
63
67
  islandLog.error("\u043E\u0441\u0442\u0440\u043E\u0432: \u0441\u0431\u043E\u0440\u043A\u0430 \u043A\u043E\u043D\u0442\u0435\u0439\u043D\u0435\u0440\u0430 \u0443\u043F\u0430\u043B\u0430", error);
@@ -111,6 +115,12 @@ function collectDocumentContribution(container, descriptor) {
111
115
  if (part.head) {
112
116
  contribution.head = _chunkT5QVCXVBcjs.__spreadValues.call(void 0, _chunkT5QVCXVBcjs.__spreadValues.call(void 0, {}, contribution.head), part.head);
113
117
  }
118
+ if (part.data) {
119
+ contribution.data = _chunkT5QVCXVBcjs.__spreadValues.call(void 0, _chunkT5QVCXVBcjs.__spreadValues.call(void 0, {}, contribution.data), part.data);
120
+ }
121
+ if (part.assets && contribution.assets === void 0) {
122
+ contribution.assets = part.assets;
123
+ }
114
124
  const declared = part.status !== void 0 || part.redirect !== void 0;
115
125
  if (declared && contribution.status === void 0) {
116
126
  contribution.status = (_b = part.status) != null ? _b : REDIRECT_FOUND;
@@ -149,7 +159,7 @@ function setupOnce(descriptor) {
149
159
  }
150
160
  function renderApp(descriptor, request) {
151
161
  return _chunkT5QVCXVBcjs.__async.call(void 0, this, null, function* () {
152
- var _a, _b;
162
+ var _a, _b, _c;
153
163
  yield setupOnce(descriptor);
154
164
  const route = {
155
165
  params: (_a = request.params) != null ? _a : {},
@@ -165,16 +175,21 @@ function renderApp(descriptor, request) {
165
175
  container.bind(_chunk6QV4L6R2cjs.RequestContextToken).toValue(request.context);
166
176
  }
167
177
  yield activateIslandViewModels(container, descriptor, route);
168
- const { head, redirect, status = STATUS_OK } = collectDocumentContribution(container, descriptor);
178
+ const contribution = collectDocumentContribution(container, descriptor);
179
+ const { data, head, redirect, status = STATUS_OK } = contribution;
169
180
  if (status !== STATUS_OK) {
170
- return { head, html: "", redirect, state: {}, status };
181
+ return { data, head, html: "", redirect, state: {}, status };
171
182
  }
183
+ const assets = (_c = contribution.assets) != null ? _c : descriptor.assets;
172
184
  const state = collectIslandState(container, descriptor);
185
+ if (descriptor.view === void 0 || descriptor.viewLayer === void 0) {
186
+ return { assets, data, head, html: "", state, status };
187
+ }
173
188
  const rendered = _chunkXOJ2VWX4cjs.resolveViewAdapter.call(void 0, container).renderIsland(descriptor, {
174
189
  container,
175
190
  slots: request.slots
176
191
  });
177
- return { assets: descriptor.assets, head, headHtml: rendered.head, html: rendered.html, state, status };
192
+ return { assets, data, head, headHtml: rendered.head, html: rendered.html, state, status };
178
193
  } finally {
179
194
  yield container.dispose();
180
195
  }
@@ -1,3 +1,6 @@
1
+ import {
2
+ loadAppEnv
3
+ } from "./chunk-RG7ZV5BD.js";
1
4
  import {
2
5
  logger
3
6
  } from "./chunk-TPBI6TOU.js";
@@ -92,7 +95,9 @@ import config from ${JSON.stringify(configPath)}
92
95
  const server = config.server ?? {}
93
96
 
94
97
  async function setup() {
95
- viewAdapterOf(app.viewLayer).installResolver()
98
+ if (app.viewLayer) {
99
+ viewAdapterOf(app.viewLayer).installResolver()
100
+ }
96
101
  server.container?.(getAppContainer())
97
102
  await server.setup?.()
98
103
  }
@@ -109,11 +114,16 @@ export default descriptor
109
114
  function generateServerProcess(configPath, base, mode) {
110
115
  const assets = mode === "development" ? ` assets: { base: ${JSON.stringify(base)}, dir: fileURLToPath(new URL('./client', import.meta.url)) },
111
116
  ` : "";
112
- return `${mode === "development" ? "import { fileURLToPath } from 'node:url'\n\n" : ""}import { serveApp } from '@foxford/den/serve'
117
+ return `import { fileURLToPath } from 'node:url'
118
+
119
+ import { loadAppEnv, serveApp } from '@foxford/den/serve'
113
120
 
114
121
  import app from ${JSON.stringify(VIRTUAL_SERVER_APP)}
115
122
  import config from ${JSON.stringify(configPath)}
116
123
 
124
+ // \u0423\u043C\u043E\u043B\u0447\u0430\u043D\u0438\u044F \u043E\u043A\u0440\u0443\u0436\u0435\u043D\u0438\u044F \u2014 \u0434\u043E \u043F\u0435\u0440\u0432\u043E\u0433\u043E \u0447\u0442\u0435\u043D\u0438\u044F: \u043F\u043E\u0440\u0442 \u0438 \u0430\u0434\u0440\u0435\u0441\u0430 \u0447\u0438\u0442\u0430\u044E\u0442\u0441\u044F \u043D\u0438\u0436\u0435
125
+ loadAppEnv(fileURLToPath(new URL('..', import.meta.url)))
126
+
117
127
  await serveApp(app, {
118
128
  ${assets} configure: config.server?.configure,
119
129
  host: process.env.HOST,
@@ -126,7 +136,14 @@ ${assets} configure: config.server?.configure,
126
136
  function denAppModules(options) {
127
137
  const { assets, base, config, configPath, entry, mode } = options;
128
138
  const sources = {
129
- [VIRTUAL_CLIENT]: () => generateClientEntry(entry, config.preset.clientEntry),
139
+ // Клиентский вход спрашивают только у клиентского выхода, а его без пресета не собирают.
140
+ // Отказ здесь означает, что выход всё-таки запустили, — это ошибка команд, а не приложения
141
+ [VIRTUAL_CLIENT]: () => {
142
+ if (!config.preset) {
143
+ throw new Error("den: \u043A\u043B\u0438\u0435\u043D\u0442\u0441\u043A\u0438\u0439 \u0432\u0445\u043E\u0434 \u0437\u0430\u043F\u0440\u043E\u0448\u0435\u043D, \u043D\u043E \u043F\u0440\u0435\u0441\u0435\u0442 \u043D\u0435 \u043E\u0431\u044A\u044F\u0432\u043B\u0435\u043D \u2014 \u043E\u0436\u0438\u0432\u043B\u044F\u0442\u044C \u043D\u0435\u0447\u0435\u043C");
144
+ }
145
+ return generateClientEntry(entry, config.preset.clientEntry);
146
+ },
130
147
  [VIRTUAL_SERVER_APP]: () => generateServerApp(entry, configPath, assets),
131
148
  [VIRTUAL_SERVER_PROCESS]: () => generateServerProcess(configPath, base, mode)
132
149
  };
@@ -167,8 +184,8 @@ function loadAppConfig(root) {
167
184
  try {
168
185
  const loaded = yield server.ssrLoadModule(configPath);
169
186
  const config = loaded.default;
170
- if (!(config == null ? void 0 : config.preset)) {
171
- throw new Error(`den: ${CONFIG_FILE} \u043D\u0435 \u043E\u0431\u044A\u044F\u0432\u043B\u044F\u0435\u0442 \u043F\u0440\u0435\u0441\u0435\u0442 \u0444\u0440\u0435\u0439\u043C\u0432\u043E\u0440\u043A\u0430 \u2014 \u0441\u043E\u0431\u0438\u0440\u0430\u0442\u044C \u043D\u0435\u0447\u0435\u043C`);
187
+ if (!config) {
188
+ throw new Error(`den: ${CONFIG_FILE} \u043D\u0435 \u043E\u0442\u0434\u0430\u0451\u0442 \u043A\u043E\u043D\u0444\u0438\u0433 \u043F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u044F \u2014 \u0441\u043E\u0431\u0438\u0440\u0430\u0442\u044C \u043D\u0435\u0447\u0435\u043C`);
172
189
  }
173
190
  return { config, configPath };
174
191
  } finally {
@@ -186,7 +203,7 @@ function dependenciesOf(root) {
186
203
  return [...Object.keys((_a = manifest.dependencies) != null ? _a : {}), ...Object.keys((_b = manifest.devDependencies) != null ? _b : {})];
187
204
  }
188
205
  function viteConfigFor(options) {
189
- var _a, _b, _c;
206
+ var _a, _b, _c, _d, _e;
190
207
  const { assets, base, config, configPath, entryFileNames, input, mode, outDir, root, target } = options;
191
208
  const entry = (_a = config.entry) != null ? _a : "src/index.ts";
192
209
  const composed = {
@@ -211,34 +228,43 @@ function viteConfigFor(options) {
211
228
  // программный вызов он иначе не доедет.
212
229
  mode,
213
230
  plugins: [
214
- ...config.preset.plugins,
231
+ ...(_c = (_b = config.preset) == null ? void 0 : _b.plugins) != null ? _c : [],
215
232
  denAppModules({ assets, base, config, configPath, entry: path2.join(root, entry), mode })
216
233
  ],
217
234
  root,
218
- ssr: { noExternal: (_c = (_b = config.server) == null ? void 0 : _b.noExternal) != null ? _c : [] }
235
+ ssr: { noExternal: (_e = (_d = config.server) == null ? void 0 : _d.noExternal) != null ? _e : [] }
219
236
  };
220
237
  return config.vite ? config.vite(composed, target) : composed;
221
238
  }
222
239
  function buildApp(options) {
223
240
  return __async(this, null, function* () {
224
- var _a, _b, _c, _d;
241
+ var _a, _b, _c;
225
242
  const { mode = "production" } = options;
226
243
  process.env["NODE_ENV"] = mode;
227
244
  const root = path2.resolve(options.root);
245
+ loadAppEnv(root);
228
246
  const { config, configPath } = yield loadAppConfig(root);
229
247
  const port = resolvePort((_a = config.server) == null ? void 0 : _a.port);
230
248
  const base = (_b = options.base) != null ? _b : "";
231
- const refusal = (_d = (_c = config.preset).validate) == null ? void 0 : _d.call(_c, dependenciesOf(root));
249
+ const { preset } = config;
250
+ const refusal = (_c = preset == null ? void 0 : preset.validate) == null ? void 0 : _c.call(preset, dependenciesOf(root));
232
251
  if (refusal !== void 0 && refusal !== null) {
233
252
  throw new Error(`den: ${refusal}`);
234
253
  }
235
254
  const { build } = yield import("vite");
236
255
  const shared = { base, config, configPath, mode, root };
237
256
  const empty = { css: [], js: [] };
238
- logger.info(`\u0441\u0431\u043E\u0440\u043A\u0430 \u043A\u043B\u0438\u0435\u043D\u0442\u0441\u043A\u043E\u0433\u043E \u0432\u044B\u0445\u043E\u0434\u0430 (${config.preset.view})`);
239
- yield build(viteConfigFor(__spreadProps(__spreadValues({}, shared), { assets: empty, input: VIRTUAL_CLIENT, outDir: OUT_CLIENT, target: "client" })));
240
- const assets = collectAssets(path2.join(root, OUT_CLIENT), base);
241
- logger.info(`\u0430\u0440\u0442\u0435\u0444\u0430\u043A\u0442\u044B: ${String(assets.js.length)} js, ${String(assets.css.length)} css`);
257
+ let assets = empty;
258
+ if (preset) {
259
+ logger.info(`\u0441\u0431\u043E\u0440\u043A\u0430 \u043A\u043B\u0438\u0435\u043D\u0442\u0441\u043A\u043E\u0433\u043E \u0432\u044B\u0445\u043E\u0434\u0430 (${preset.view})`);
260
+ yield build(
261
+ viteConfigFor(__spreadProps(__spreadValues({}, shared), { assets: empty, input: VIRTUAL_CLIENT, outDir: OUT_CLIENT, target: "client" }))
262
+ );
263
+ assets = collectAssets(path2.join(root, OUT_CLIENT), base);
264
+ logger.info(`\u0430\u0440\u0442\u0435\u0444\u0430\u043A\u0442\u044B: ${String(assets.js.length)} js, ${String(assets.css.length)} css`);
265
+ } else {
266
+ logger.info("\u043A\u043B\u0438\u0435\u043D\u0442\u0441\u043A\u043E\u0433\u043E \u0432\u044B\u0445\u043E\u0434\u0430 \u043D\u0435\u0442: \u043F\u0440\u0435\u0441\u0435\u0442 \u043D\u0435 \u043E\u0431\u044A\u044F\u0432\u043B\u0435\u043D, \u043F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u0435 \u0441\u0432\u043E\u0435\u0433\u043E \u0444\u0440\u0430\u0433\u043C\u0435\u043D\u0442\u0430 \u043D\u0435 \u0440\u0438\u0441\u0443\u0435\u0442");
267
+ }
242
268
  logger.info("\u0441\u0431\u043E\u0440\u043A\u0430 \u0441\u0435\u0440\u0432\u0435\u0440\u043D\u043E\u0433\u043E \u0434\u0435\u0441\u043A\u0440\u0438\u043F\u0442\u043E\u0440\u0430");
243
269
  yield build(
244
270
  viteConfigFor(__spreadProps(__spreadValues({}, shared), {
@@ -309,7 +335,9 @@ function bannerLines(facts2) {
309
335
  `${label("\u043B\u043E\u043A\u0430\u043B\u044C\u043D\u043E")}http://localhost:${port}`,
310
336
  ...network === void 0 ? [] : [`${label("\u0432 \u0441\u0435\u0442\u0438")}http://${network}:${port}`],
311
337
  `${label("\u0440\u0443\u0447\u043A\u0438")}${ENDPOINTS.join(" ")}`,
312
- ...facts2.base === void 0 ? [] : [`${label("\u0430\u0440\u0442\u0435\u0444\u0430\u043A\u0442\u044B")}${facts2.base}`],
338
+ // Префикс печатается, только когда он есть: при относительной сборке артефакты идут с
339
+ // адреса самого приложения, а он уже назван строкой выше
340
+ ...facts2.base === void 0 || facts2.base === "" ? [] : [`${label("\u043F\u0440\u0435\u0444\u0438\u043A\u0441")}${facts2.base}`],
313
341
  `${label("\u0447\u0430\u043D\u043A\u0438")}${assetsSummary(facts2.assets)}`,
314
342
  `${label("\u0432\u044B\u0445\u043E\u0434\u044B")}${OUTPUTS.join(", ")}`,
315
343
  `${label("\u043F\u0440\u0430\u0432\u043A\u0438")}${facts2.watched.join(", ")}`,
@@ -14,6 +14,11 @@ import { Token } from '@foxford/ioc';
14
14
  * шире: у каждого своя часть про оркестрацию — `load`, `routes`, `provides`/`requires`,
15
15
  * вклад в документ. Сводить их в один тип нечем, оркестраторы у хостов разные. Общее
16
16
  * ровно это, и структурная типизация делает остальное: дескриптор хоста подходит сюда как есть.
17
+ *
18
+ * `view` и `viewLayer` объявляются ПАРОЙ либо не объявляются вовсе. Приложение без них —
19
+ * законный случай: оно сообщает документу данные (`describeDocument`), а рисует его кусок
20
+ * кто-то другой, например клиент монолита. Жизненный цикл у такого приложения тот же —
21
+ * контейнер, единицы, активация VM, — поэтому второго вида дескриптора нет.
17
22
  */
18
23
  interface IslandDescriptor {
19
24
  /**
@@ -25,16 +30,16 @@ interface IslandDescriptor {
25
30
  * второго оно обязательное: без него у мест приложения не было бы адреса.
26
31
  */
27
32
  name: string;
28
- /** Непрозрачная ссылка на view острова (компонент view-фреймворка). */
29
- view: unknown;
33
+ /** Непрозрачная ссылка на view острова (компонент view-фреймворка); нет — приложение не рисует. */
34
+ view?: unknown;
30
35
  /**
31
36
  * View-слой — чем остров рисовать: `defineLayer(ViewLayerToken, { adapter: reactAdapter() })`.
32
37
  *
33
- * Обязателен, и дефолта нет ни у ядра, ни у хоста: фреймворк за приложение не выбирают.
34
- * Слой регистрируется в контейнере острова, поэтому слоты внутри по умолчанию рисуются
35
- * им же; слот перекрывает выбор своим `viewLayer`.
38
+ * Дефолта нет ни у ядра, ни у хоста: фреймворк за приложение не выбирают. Слой
39
+ * регистрируется в контейнере острова, поэтому слоты внутри по умолчанию рисуются им же;
40
+ * слот перекрывает выбор своим `viewLayer`.
36
41
  */
37
- viewLayer: LayerDefinition;
42
+ viewLayer?: LayerDefinition;
38
43
  /**
39
44
  * Data/Business единицы (repository/service). Регистрируются в контейнере острова.
40
45
  * Не должны импортировать VM.
@@ -89,10 +94,15 @@ interface IslandDescriptor {
89
94
  *
90
95
  * Ленивые чанки сюда не попадают — их браузер дотянет сам по мере надобности, а хосту
91
96
  * нужно ровно то, без чего страница не оживёт.
97
+ *
98
+ * Вид скриптов объявлен здесь же, потому что знает его СБОРКА: теги печатает хост, но выбрать
99
+ * между `type="module"` и классическим `defer` он может, только зная, чем приложение собрано.
92
100
  */
93
101
  interface AppAssets {
94
102
  css: string[];
95
103
  js: string[];
104
+ /** Чем подключать `js`; не объявлен — `module`. */
105
+ scripts?: 'module' | 'classic';
96
106
  }
97
107
 
98
108
  export type { AppAssets as A, IslandDescriptor as I };
@@ -14,6 +14,11 @@ import { Token } from '@foxford/ioc';
14
14
  * шире: у каждого своя часть про оркестрацию — `load`, `routes`, `provides`/`requires`,
15
15
  * вклад в документ. Сводить их в один тип нечем, оркестраторы у хостов разные. Общее
16
16
  * ровно это, и структурная типизация делает остальное: дескриптор хоста подходит сюда как есть.
17
+ *
18
+ * `view` и `viewLayer` объявляются ПАРОЙ либо не объявляются вовсе. Приложение без них —
19
+ * законный случай: оно сообщает документу данные (`describeDocument`), а рисует его кусок
20
+ * кто-то другой, например клиент монолита. Жизненный цикл у такого приложения тот же —
21
+ * контейнер, единицы, активация VM, — поэтому второго вида дескриптора нет.
17
22
  */
18
23
  interface IslandDescriptor {
19
24
  /**
@@ -25,16 +30,16 @@ interface IslandDescriptor {
25
30
  * второго оно обязательное: без него у мест приложения не было бы адреса.
26
31
  */
27
32
  name: string;
28
- /** Непрозрачная ссылка на view острова (компонент view-фреймворка). */
29
- view: unknown;
33
+ /** Непрозрачная ссылка на view острова (компонент view-фреймворка); нет — приложение не рисует. */
34
+ view?: unknown;
30
35
  /**
31
36
  * View-слой — чем остров рисовать: `defineLayer(ViewLayerToken, { adapter: reactAdapter() })`.
32
37
  *
33
- * Обязателен, и дефолта нет ни у ядра, ни у хоста: фреймворк за приложение не выбирают.
34
- * Слой регистрируется в контейнере острова, поэтому слоты внутри по умолчанию рисуются
35
- * им же; слот перекрывает выбор своим `viewLayer`.
38
+ * Дефолта нет ни у ядра, ни у хоста: фреймворк за приложение не выбирают. Слой
39
+ * регистрируется в контейнере острова, поэтому слоты внутри по умолчанию рисуются им же;
40
+ * слот перекрывает выбор своим `viewLayer`.
36
41
  */
37
- viewLayer: LayerDefinition;
42
+ viewLayer?: LayerDefinition;
38
43
  /**
39
44
  * Data/Business единицы (repository/service). Регистрируются в контейнере острова.
40
45
  * Не должны импортировать VM.
@@ -89,10 +94,15 @@ interface IslandDescriptor {
89
94
  *
90
95
  * Ленивые чанки сюда не попадают — их браузер дотянет сам по мере надобности, а хосту
91
96
  * нужно ровно то, без чего страница не оживёт.
97
+ *
98
+ * Вид скриптов объявлен здесь же, потому что знает его СБОРКА: теги печатает хост, но выбрать
99
+ * между `type="module"` и классическим `defer` он может, только зная, чем приложение собрано.
92
100
  */
93
101
  interface AppAssets {
94
102
  css: string[];
95
103
  js: string[];
104
+ /** Чем подключать `js`; не объявлен — `module`. */
105
+ scripts?: 'module' | 'classic';
96
106
  }
97
107
 
98
108
  export type { AppAssets as A, IslandDescriptor as I };
package/index.d.cts CHANGED
@@ -4,8 +4,8 @@ import { Container, Token } from '@foxford/ioc';
4
4
  import { DefinitionResolver } from './adapter.cjs';
5
5
  export { d as defineRepository, a as defineService, b as defineViewModel } from './define-repository-CwAXed2X.cjs';
6
6
  export { D as DefineSlotOptions, S as SlotDefinition, d as defineSlot } from './define-slot-DYNCLDJY.cjs';
7
- export { V as ViewAdapter, a as ViewLayerDefinition, b as ViewLayerToken } from './view-adapter-DJ7yB6Ml.cjs';
8
- import './descriptor-Dujg_1on.cjs';
7
+ export { V as ViewAdapter, a as ViewLayerDefinition, b as ViewLayerToken } from './view-adapter-Cia9blgH.cjs';
8
+ import './descriptor-BfsBDME1.cjs';
9
9
  import './server-render-CocWXQKC.cjs';
10
10
 
11
11
  /**
package/index.d.ts CHANGED
@@ -4,8 +4,8 @@ import { Container, Token } from '@foxford/ioc';
4
4
  import { DefinitionResolver } from './adapter.js';
5
5
  export { d as defineRepository, a as defineService, b as defineViewModel } from './define-repository-q_T4hqeP.js';
6
6
  export { D as DefineSlotOptions, S as SlotDefinition, d as defineSlot } from './define-slot-Cngzae6i.js';
7
- export { V as ViewAdapter, a as ViewLayerDefinition, b as ViewLayerToken } from './view-adapter-B4gEb-ms.js';
8
- import './descriptor-DQ6Gi7A_.js';
7
+ export { V as ViewAdapter, a as ViewLayerDefinition, b as ViewLayerToken } from './view-adapter-Bzcjotsn.js';
8
+ import './descriptor-D8ocBnTb.js';
9
9
  import './server-render-CocWXQKC.js';
10
10
 
11
11
  /**
package/island/index.cjs CHANGED
@@ -11,7 +11,7 @@
11
11
 
12
12
 
13
13
 
14
- var _chunkZOU6W6ZQcjs = require('../chunk-ZOU6W6ZQ.cjs');
14
+ var _chunkXDM6J4LJcjs = require('../chunk-XDM6J4LJ.cjs');
15
15
 
16
16
 
17
17
 
@@ -122,4 +122,4 @@ function describeIsland(descriptor, denState) {
122
122
 
123
123
 
124
124
 
125
- exports.DOCUMENT_EVENT = DOCUMENT_EVENT; exports.NAVIGATE_EVENT = NAVIGATE_EVENT; exports.ViewLayerToken = _chunkXOJ2VWX4cjs.ViewLayerToken; exports.activateIslandViewModels = _chunkZOU6W6ZQcjs.activateIslandViewModels; exports.collectDocumentContribution = _chunkZOU6W6ZQcjs.collectDocumentContribution; exports.collectIslandState = _chunkZOU6W6ZQcjs.collectIslandState; exports.createIslandContainer = _chunkZOU6W6ZQcjs.createIslandContainer; exports.describeIsland = describeIsland; exports.getAppContainer = _chunkZOU6W6ZQcjs.getAppContainer; exports.getIslandsSnapshot = getIslandsSnapshot; exports.hydrateIslandState = _chunkZOU6W6ZQcjs.hydrateIslandState; exports.isDocumentSource = _chunkZOU6W6ZQcjs.isDocumentSource; exports.matchRoute = _chunkZOU6W6ZQcjs.matchRoute; exports.navigateIsland = navigateIsland; exports.normalizeRoutes = _chunkZOU6W6ZQcjs.normalizeRoutes; exports.onIslandDocument = onIslandDocument; exports.onIslandNavigate = onIslandNavigate; exports.pathPattern = _chunkZOU6W6ZQcjs.pathPattern; exports.publishIslandDocument = publishIslandDocument; exports.registerIsland = registerIsland; exports.renderApp = _chunkZOU6W6ZQcjs.renderApp; exports.resolveEagerUnits = _chunkZOU6W6ZQcjs.resolveEagerUnits; exports.resolveViewAdapter = _chunkXOJ2VWX4cjs.resolveViewAdapter; exports.slotAddress = slotAddress; exports.subscribeIslands = subscribeIslands; exports.viewAdapterOf = _chunkXOJ2VWX4cjs.viewAdapterOf;
125
+ exports.DOCUMENT_EVENT = DOCUMENT_EVENT; exports.NAVIGATE_EVENT = NAVIGATE_EVENT; exports.ViewLayerToken = _chunkXOJ2VWX4cjs.ViewLayerToken; exports.activateIslandViewModels = _chunkXDM6J4LJcjs.activateIslandViewModels; exports.collectDocumentContribution = _chunkXDM6J4LJcjs.collectDocumentContribution; exports.collectIslandState = _chunkXDM6J4LJcjs.collectIslandState; exports.createIslandContainer = _chunkXDM6J4LJcjs.createIslandContainer; exports.describeIsland = describeIsland; exports.getAppContainer = _chunkXDM6J4LJcjs.getAppContainer; exports.getIslandsSnapshot = getIslandsSnapshot; exports.hydrateIslandState = _chunkXDM6J4LJcjs.hydrateIslandState; exports.isDocumentSource = _chunkXDM6J4LJcjs.isDocumentSource; exports.matchRoute = _chunkXDM6J4LJcjs.matchRoute; exports.navigateIsland = navigateIsland; exports.normalizeRoutes = _chunkXDM6J4LJcjs.normalizeRoutes; exports.onIslandDocument = onIslandDocument; exports.onIslandNavigate = onIslandNavigate; exports.pathPattern = _chunkXDM6J4LJcjs.pathPattern; exports.publishIslandDocument = publishIslandDocument; exports.registerIsland = registerIsland; exports.renderApp = _chunkXDM6J4LJcjs.renderApp; exports.resolveEagerUnits = _chunkXDM6J4LJcjs.resolveEagerUnits; exports.resolveViewAdapter = _chunkXOJ2VWX4cjs.resolveViewAdapter; exports.slotAddress = slotAddress; exports.subscribeIslands = subscribeIslands; exports.viewAdapterOf = _chunkXOJ2VWX4cjs.viewAdapterOf;
@@ -1,9 +1,9 @@
1
- import { I as IslandDescriptor, A as AppAssets } from '../descriptor-Dujg_1on.cjs';
1
+ import { A as AppAssets, I as IslandDescriptor } from '../descriptor-BfsBDME1.cjs';
2
2
  import { Container } from '@foxford/ioc';
3
3
  import { R as RequestContext } from '../types-Dx5qGiwk.cjs';
4
4
  export { A as AppRoute, R as RouteDeclaration, a as RouteMatch, m as matchRoute, n as normalizeRoutes, p as pathPattern } from '../routes-B0hDrkiF.cjs';
5
5
  export { R as RenderTree, a as ServerRender, S as ServerRenderResult } from '../server-render-CocWXQKC.cjs';
6
- export { R as RenderIslandOptions, V as ViewAdapter, a as ViewLayerDefinition, b as ViewLayerToken, r as resolveViewAdapter, v as viewAdapterOf } from '../view-adapter-DJ7yB6Ml.cjs';
6
+ export { R as RenderIslandOptions, V as ViewAdapter, a as ViewLayerDefinition, b as ViewLayerToken, r as resolveViewAdapter, v as viewAdapterOf } from '../view-adapter-Cia9blgH.cjs';
7
7
  import '../define-slot-DYNCLDJY.cjs';
8
8
 
9
9
  /** Вклад приложения в `<head>` документа. */
@@ -33,6 +33,10 @@ interface DocumentContribution {
33
33
  status?: number;
34
34
  /** Адрес для 3xx. Объявлен без статуса — исход `302`. */
35
35
  redirect?: string;
36
+ /** Данные документа, состав которых знает только шаблон хоста. */
37
+ data?: Record<string, unknown>;
38
+ /** Артефакты этого адреса; не объявлены — берутся вплавленные сборкой. */
39
+ assets?: AppAssets;
36
40
  }
37
41
  /**
38
42
  * VM, которой есть что сказать про документ.
@@ -138,10 +142,11 @@ declare function collectIslandState(container: Container, descriptor: IslandDesc
138
142
  /**
139
143
  * Собирает вклад острова в документ по всем его VM.
140
144
  *
141
- * Правила слияния два, и они следуют из смысла полей. `head` — набор полей, поэтому
142
- * объявленное позже уточняет объявленное раньше. Исход (`status` вместе с `redirect`)
143
- * ОДНО решение, а не набор: побеждает первое объявленное, и берётся оно целиком, иначе
144
- * статус одной VM склеился бы с адресом другой.
145
+ * Правила слияния два, и они следуют из смысла полей. `head` и `data` наборы полей, поэтому
146
+ * объявленное позже уточняет объявленное раньше. Исход (`status` вместе с `redirect`) и
147
+ * артефакты — ОДНО решение каждое, а не набор: побеждает первое объявленное, и берётся оно
148
+ * целиком. Иначе статус одной VM склеился бы с адресом другой, а списки чанков двух VM —
149
+ * в набор, который не грузил ни один клиент.
145
150
  *
146
151
  * @param container - Контейнер острова
147
152
  * @param descriptor - Дескриптор острова
@@ -241,6 +246,8 @@ interface RenderAppResult {
241
246
  head?: DocumentHead;
242
247
  /** Вклад в `<head>` готовой разметкой — стили, собранные во время рендера. */
243
248
  headHtml?: string;
249
+ /** Данные документа, состав которых знает шаблон хоста. */
250
+ data?: Record<string, unknown>;
244
251
  /** Адрес для 3xx. */
245
252
  redirect?: string;
246
253
  /**
package/island/index.d.ts CHANGED
@@ -1,9 +1,9 @@
1
- import { I as IslandDescriptor, A as AppAssets } from '../descriptor-DQ6Gi7A_.js';
1
+ import { A as AppAssets, I as IslandDescriptor } from '../descriptor-D8ocBnTb.js';
2
2
  import { Container } from '@foxford/ioc';
3
3
  import { R as RequestContext } from '../types-Dx5qGiwk.js';
4
4
  export { A as AppRoute, R as RouteDeclaration, a as RouteMatch, m as matchRoute, n as normalizeRoutes, p as pathPattern } from '../routes-0uXJ0fux.js';
5
5
  export { R as RenderTree, a as ServerRender, S as ServerRenderResult } from '../server-render-CocWXQKC.js';
6
- export { R as RenderIslandOptions, V as ViewAdapter, a as ViewLayerDefinition, b as ViewLayerToken, r as resolveViewAdapter, v as viewAdapterOf } from '../view-adapter-B4gEb-ms.js';
6
+ export { R as RenderIslandOptions, V as ViewAdapter, a as ViewLayerDefinition, b as ViewLayerToken, r as resolveViewAdapter, v as viewAdapterOf } from '../view-adapter-Bzcjotsn.js';
7
7
  import '../define-slot-Cngzae6i.js';
8
8
 
9
9
  /** Вклад приложения в `<head>` документа. */
@@ -33,6 +33,10 @@ interface DocumentContribution {
33
33
  status?: number;
34
34
  /** Адрес для 3xx. Объявлен без статуса — исход `302`. */
35
35
  redirect?: string;
36
+ /** Данные документа, состав которых знает только шаблон хоста. */
37
+ data?: Record<string, unknown>;
38
+ /** Артефакты этого адреса; не объявлены — берутся вплавленные сборкой. */
39
+ assets?: AppAssets;
36
40
  }
37
41
  /**
38
42
  * VM, которой есть что сказать про документ.
@@ -138,10 +142,11 @@ declare function collectIslandState(container: Container, descriptor: IslandDesc
138
142
  /**
139
143
  * Собирает вклад острова в документ по всем его VM.
140
144
  *
141
- * Правила слияния два, и они следуют из смысла полей. `head` — набор полей, поэтому
142
- * объявленное позже уточняет объявленное раньше. Исход (`status` вместе с `redirect`)
143
- * ОДНО решение, а не набор: побеждает первое объявленное, и берётся оно целиком, иначе
144
- * статус одной VM склеился бы с адресом другой.
145
+ * Правила слияния два, и они следуют из смысла полей. `head` и `data` наборы полей, поэтому
146
+ * объявленное позже уточняет объявленное раньше. Исход (`status` вместе с `redirect`) и
147
+ * артефакты — ОДНО решение каждое, а не набор: побеждает первое объявленное, и берётся оно
148
+ * целиком. Иначе статус одной VM склеился бы с адресом другой, а списки чанков двух VM —
149
+ * в набор, который не грузил ни один клиент.
145
150
  *
146
151
  * @param container - Контейнер острова
147
152
  * @param descriptor - Дескриптор острова
@@ -241,6 +246,8 @@ interface RenderAppResult {
241
246
  head?: DocumentHead;
242
247
  /** Вклад в `<head>` готовой разметкой — стили, собранные во время рендера. */
243
248
  headHtml?: string;
249
+ /** Данные документа, состав которых знает шаблон хоста. */
250
+ data?: Record<string, unknown>;
244
251
  /** Адрес для 3xx. */
245
252
  redirect?: string;
246
253
  /**
package/island/index.js CHANGED
@@ -11,7 +11,7 @@ import {
11
11
  pathPattern,
12
12
  renderApp,
13
13
  resolveEagerUnits
14
- } from "../chunk-TPHJUDHG.js";
14
+ } from "../chunk-SOH2TL2N.js";
15
15
  import {
16
16
  ViewLayerToken,
17
17
  resolveViewAdapter,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@foxford/den",
3
- "version": "3.2.0",
3
+ "version": "3.2.2",
4
4
  "description": "Den — декларативный метафреймворк Foxford (core runtime)",
5
5
  "keywords": [
6
6
  "foxford",
@@ -24,8 +24,8 @@
24
24
  },
25
25
  "dependencies": {
26
26
  "fastify": "^5.11.2",
27
- "@foxford/logger": "^1.2.2",
28
- "@foxford/ioc": "^1.1.2"
27
+ "@foxford/ioc": "^1.1.2",
28
+ "@foxford/logger": "^1.2.2"
29
29
  },
30
30
  "peerDependencies": {
31
31
  "vite": ">=5.0.0"
@@ -83,21 +83,23 @@
83
83
  "bin",
84
84
  "builder",
85
85
  "chunk-3RX2OSUL.js",
86
+ "chunk-3X4WVHUU.cjs",
86
87
  "chunk-6FNC3XMI.js",
87
88
  "chunk-6QV4L6R2.cjs",
88
89
  "chunk-A6ZPAM6Z.cjs",
89
- "chunk-BMI5HNGE.js",
90
90
  "chunk-C7BM4DGX.cjs",
91
91
  "chunk-E23E2VUC.js",
92
+ "chunk-KQAB6C6Q.cjs",
92
93
  "chunk-MCN4GFEQ.js",
93
- "chunk-RRR2DJMN.cjs",
94
+ "chunk-RG7ZV5BD.js",
95
+ "chunk-SOH2TL2N.js",
94
96
  "chunk-T5QVCXVB.cjs",
95
97
  "chunk-TPBI6TOU.js",
96
- "chunk-TPHJUDHG.js",
97
98
  "chunk-WBHHHICS.js",
99
+ "chunk-XDM6J4LJ.cjs",
98
100
  "chunk-XOJ2VWX4.cjs",
101
+ "chunk-XU5HUIPK.js",
99
102
  "chunk-Z5BL4DJA.cjs",
100
- "chunk-ZOU6W6ZQ.cjs",
101
103
  "define-repository-CwAXed2X.d.cts",
102
104
  "define-repository-q_T4hqeP.d.ts",
103
105
  "define-slot-Cngzae6i.d.ts",
@@ -107,8 +109,8 @@
107
109
  "define.d.ts",
108
110
  "define.js",
109
111
  "den.js",
110
- "descriptor-DQ6Gi7A_.d.ts",
111
- "descriptor-Dujg_1on.d.cts",
112
+ "descriptor-BfsBDME1.d.cts",
113
+ "descriptor-D8ocBnTb.d.ts",
112
114
  "index.cjs",
113
115
  "index.d.cts",
114
116
  "index.d.ts",
@@ -122,9 +124,9 @@
122
124
  "server-render-CocWXQKC.d.ts",
123
125
  "types-Dx5qGiwk.d.cts",
124
126
  "types-Dx5qGiwk.d.ts",
125
- "view-adapter-B4gEb-ms.d.ts",
126
- "view-adapter-DJ7yB6Ml.d.cts"
127
+ "view-adapter-Bzcjotsn.d.ts",
128
+ "view-adapter-Cia9blgH.d.cts"
127
129
  ],
128
- "sha": "c382a3d",
130
+ "sha": "7fa6f3e",
129
131
  "scripts": {}
130
132
  }
package/serve/index.cjs CHANGED
@@ -1,8 +1,11 @@
1
1
  "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
2
2
 
3
+ var _chunkKQAB6C6Qcjs = require('../chunk-KQAB6C6Q.cjs');
3
4
 
4
5
 
5
- var _chunkZOU6W6ZQcjs = require('../chunk-ZOU6W6ZQ.cjs');
6
+
7
+
8
+ var _chunkXDM6J4LJcjs = require('../chunk-XDM6J4LJ.cjs');
6
9
  require('../chunk-XOJ2VWX4.cjs');
7
10
  require('../chunk-6QV4L6R2.cjs');
8
11
 
@@ -66,14 +69,14 @@ function registerAssets(instance, source) {
66
69
 
67
70
  var DEFAULT_SLOW_RENDER_MS = 1e3;
68
71
  function claim(app) {
69
- return _chunkZOU6W6ZQcjs.normalizeRoutes.call(void 0, app.routes).map((route) => route.source);
72
+ return _chunkXDM6J4LJcjs.normalizeRoutes.call(void 0, app.routes).map((route) => route.source);
70
73
  }
71
74
  function matchClaim(app, pathname) {
72
75
  var _a, _b;
73
76
  if (claim(app).length === 0) {
74
77
  return {};
75
78
  }
76
- return (_b = (_a = _chunkZOU6W6ZQcjs.matchRoute.call(void 0, app.routes, pathname)) == null ? void 0 : _a.params) != null ? _b : null;
79
+ return (_b = (_a = _chunkXDM6J4LJcjs.matchRoute.call(void 0, app.routes, pathname)) == null ? void 0 : _a.params) != null ? _b : null;
77
80
  }
78
81
  function parseCookies(header) {
79
82
  const cookies = {};
@@ -129,7 +132,7 @@ var appPlugin = (instance, options) => {
129
132
  }
130
133
  const startedAt = Date.now();
131
134
  try {
132
- const result = yield _chunkZOU6W6ZQcjs.renderApp.call(void 0, app, { context: browserContext(req), params, pathname, search });
135
+ const result = yield _chunkXDM6J4LJcjs.renderApp.call(void 0, app, { context: browserContext(req), params, pathname, search });
133
136
  const elapsed = Date.now() - startedAt;
134
137
  if (elapsed >= slowRenderMs) {
135
138
  serveLog.warn(`\u043C\u0435\u0434\u043B\u0435\u043D\u043D\u044B\u0439 \u0440\u0435\u043D\u0434\u0435\u0440 \xAB${pathname}\xBB: ${String(elapsed)}\u043C\u0441`);
@@ -184,4 +187,5 @@ function serveApp(_0) {
184
187
 
185
188
 
186
189
 
187
- exports.appPlugin = appPlugin; exports.registerAssets = registerAssets; exports.serveApp = serveApp;
190
+
191
+ exports.appPlugin = appPlugin; exports.loadAppEnv = _chunkKQAB6C6Qcjs.loadAppEnv; exports.registerAssets = registerAssets; exports.serveApp = serveApp;
package/serve/index.d.cts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { FastifyInstance, FastifyPluginAsync } from 'fastify';
2
- import { I as IslandDescriptor } from '../descriptor-Dujg_1on.cjs';
2
+ import { I as IslandDescriptor } from '../descriptor-BfsBDME1.cjs';
3
3
  import { A as AppRoute } from '../routes-B0hDrkiF.cjs';
4
4
  import '../define-slot-DYNCLDJY.cjs';
5
5
  import '../types-Dx5qGiwk.cjs';
@@ -41,6 +41,15 @@ interface AppPluginOptions {
41
41
  */
42
42
  declare const appPlugin: FastifyPluginAsync<AppPluginOptions>;
43
43
 
44
+ /**
45
+ * Кладёт умолчания из `<root>/.env` в окружение процесса.
46
+ *
47
+ * Отсутствие файла — норма, а не отказ: он локальный и в репозиторий не попадает.
48
+ *
49
+ * @param root - Корень приложения
50
+ */
51
+ declare function loadAppEnv(root: string): void;
52
+
44
53
  /** Настройки инстанса. */
45
54
  interface ServeAppOptions {
46
55
  /** Порт; `0` — свободный, фактический смотри в `server.addresses()`. */
@@ -79,4 +88,4 @@ interface ServeAppOptions {
79
88
  */
80
89
  declare function serveApp(app: ServableApp, options?: ServeAppOptions): Promise<FastifyInstance>;
81
90
 
82
- export { type AppAssetsSource, type AppPluginOptions, type ServableApp, type ServeAppOptions, appPlugin, registerAssets, serveApp };
91
+ export { type AppAssetsSource, type AppPluginOptions, type ServableApp, type ServeAppOptions, appPlugin, loadAppEnv, registerAssets, serveApp };
package/serve/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { FastifyInstance, FastifyPluginAsync } from 'fastify';
2
- import { I as IslandDescriptor } from '../descriptor-DQ6Gi7A_.js';
2
+ import { I as IslandDescriptor } from '../descriptor-D8ocBnTb.js';
3
3
  import { A as AppRoute } from '../routes-0uXJ0fux.js';
4
4
  import '../define-slot-Cngzae6i.js';
5
5
  import '../types-Dx5qGiwk.js';
@@ -41,6 +41,15 @@ interface AppPluginOptions {
41
41
  */
42
42
  declare const appPlugin: FastifyPluginAsync<AppPluginOptions>;
43
43
 
44
+ /**
45
+ * Кладёт умолчания из `<root>/.env` в окружение процесса.
46
+ *
47
+ * Отсутствие файла — норма, а не отказ: он локальный и в репозиторий не попадает.
48
+ *
49
+ * @param root - Корень приложения
50
+ */
51
+ declare function loadAppEnv(root: string): void;
52
+
44
53
  /** Настройки инстанса. */
45
54
  interface ServeAppOptions {
46
55
  /** Порт; `0` — свободный, фактический смотри в `server.addresses()`. */
@@ -79,4 +88,4 @@ interface ServeAppOptions {
79
88
  */
80
89
  declare function serveApp(app: ServableApp, options?: ServeAppOptions): Promise<FastifyInstance>;
81
90
 
82
- export { type AppAssetsSource, type AppPluginOptions, type ServableApp, type ServeAppOptions, appPlugin, registerAssets, serveApp };
91
+ export { type AppAssetsSource, type AppPluginOptions, type ServableApp, type ServeAppOptions, appPlugin, loadAppEnv, registerAssets, serveApp };
package/serve/index.js CHANGED
@@ -1,8 +1,11 @@
1
+ import {
2
+ loadAppEnv
3
+ } from "../chunk-RG7ZV5BD.js";
1
4
  import {
2
5
  matchRoute,
3
6
  normalizeRoutes,
4
7
  renderApp
5
- } from "../chunk-TPHJUDHG.js";
8
+ } from "../chunk-SOH2TL2N.js";
6
9
  import "../chunk-E23E2VUC.js";
7
10
  import "../chunk-3RX2OSUL.js";
8
11
  import {
@@ -182,6 +185,7 @@ function serveApp(_0) {
182
185
  }
183
186
  export {
184
187
  appPlugin,
188
+ loadAppEnv,
185
189
  registerAssets,
186
190
  serveApp
187
191
  };
@@ -1,4 +1,4 @@
1
- import { I as IslandDescriptor } from './descriptor-DQ6Gi7A_.js';
1
+ import { I as IslandDescriptor } from './descriptor-D8ocBnTb.js';
2
2
  import { S as ServerRenderResult } from './server-render-CocWXQKC.js';
3
3
  import { L as LayerDefinition, h as LayerConfig } from './types-Dx5qGiwk.js';
4
4
  import { Container, Token } from '@foxford/ioc';
@@ -1,4 +1,4 @@
1
- import { I as IslandDescriptor } from './descriptor-Dujg_1on.cjs';
1
+ import { I as IslandDescriptor } from './descriptor-BfsBDME1.cjs';
2
2
  import { S as ServerRenderResult } from './server-render-CocWXQKC.cjs';
3
3
  import { L as LayerDefinition, h as LayerConfig } from './types-Dx5qGiwk.cjs';
4
4
  import { Container, Token } from '@foxford/ioc';