@foxford/den 3.2.3 → 3.2.5-beta-3141d32-20260904
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 +24 -20
- package/bin/den.cjs +3 -3
- package/bin/den.js +1 -1
- package/builder/index.cjs +2 -2
- package/builder/index.d.cts +10 -0
- package/builder/index.d.ts +10 -0
- package/builder/index.js +1 -1
- package/{chunk-XU5HUIPK.js → chunk-4XR6EUOW.js} +8 -4
- package/{chunk-3X4WVHUU.cjs → chunk-G6RT4EBO.cjs} +8 -4
- package/island/index.cjs +48 -1
- package/island/index.d.cts +46 -1
- package/island/index.d.ts +46 -1
- package/island/index.js +47 -0
- package/package.json +4 -4
- package/serve/index.cjs +1 -1
- package/serve/index.js +1 -1
package/README.mdx
CHANGED
|
@@ -177,16 +177,18 @@ describeDocument(): DocumentContribution {
|
|
|
177
177
|
|
|
178
178
|
Рантайм вокруг дескриптора отдаёт три ручки:
|
|
179
179
|
|
|
180
|
-
| Ручка
|
|
181
|
-
|
|
|
182
|
-
| `GET /_render?url=<path+search>` | `200` + ответ `renderApp` телом
|
|
183
|
-
| `GET /_routes`
|
|
184
|
-
| `GET /_health`
|
|
180
|
+
| Ручка | Ответ |
|
|
181
|
+
| -------------------------------- | ------------------------------------------- |
|
|
182
|
+
| `GET /_render?url=<path+search>` | `200` + ответ `renderApp` телом |
|
|
183
|
+
| `GET /_routes` | `200` + массив regex-строк заявки на адреса |
|
|
184
|
+
| `GET /_health` | `200 ok` |
|
|
185
185
|
|
|
186
186
|
**Статус HTTP описывает транспорт, а исход страницы едет полем `status` в теле.** `200` значит
|
|
187
187
|
«приложение ответило» — хоть `404`, хоть редиректом. Всё остальное значит «до приложения не
|
|
188
188
|
дошли»: `400` — сломанный запрос, `404` — неизвестная ручка или путь вне заявки, `500` — рендер
|
|
189
|
-
упал. Так `404` от приложения не путается с `404` от неверно настроенного прокси.
|
|
189
|
+
упал. Так `404` от приложения не путается с `404` от неверно настроенного прокси. Упавший
|
|
190
|
+
рендер уходит как `reply.send(error)`: Fastify зовёт `onError` у потребителя с исходным
|
|
191
|
+
исключением; тело — стандартный JSON ошибки Fastify.
|
|
190
192
|
|
|
191
193
|
Браузерные заголовки рантайм читает только из `X-Forwarded-*` и кладёт в `RequestContextToken`;
|
|
192
194
|
`Cookie` — обычным именем.
|
|
@@ -262,6 +264,7 @@ export default defineAppConfig({
|
|
|
262
264
|
render: (tree, render) => ({ head: sheet.getStyleTags(), html: render(sheet.collectStyles(tree)) }),
|
|
263
265
|
container: (c) => c.bind(HttpConfigToken).toValue({ baseUrl: process.env.API_ORIGIN }),
|
|
264
266
|
configure: (instance) => Sentry.setupFastifyErrorHandler(instance),
|
|
267
|
+
external: ['@foxford/den-sentry'],
|
|
265
268
|
noExternal: ['@foxford/ui', 'styled-components'],
|
|
266
269
|
},
|
|
267
270
|
})
|
|
@@ -270,11 +273,11 @@ export default defineAppConfig({
|
|
|
270
273
|
`den build [--root <каталог>] [--base <префикс>]` выпускает три выхода, `den dev` — то же плюс
|
|
271
274
|
подъём процесса и пересборка на изменение исходников:
|
|
272
275
|
|
|
273
|
-
| Выход
|
|
274
|
-
|
|
|
275
|
-
| `build/client`
|
|
276
|
-
| `build/server`
|
|
277
|
-
| `build/server.mjs` | тот же дескриптор под сетевым рантаймом
|
|
276
|
+
| Выход | Что внутри | Кто потребляет |
|
|
277
|
+
| ------------------ | ---------------------------------------------------- | ------------------------------------- |
|
|
278
|
+
| `build/client` | браузерный бандл и манифест | браузер, по ссылкам из ответа рендера |
|
|
279
|
+
| `build/server` | дескриптор с вплавленными серверными кусками конфига | хост, импортом в своём процессе |
|
|
280
|
+
| `build/server.mjs` | тот же дескриптор под сетевым рантаймом | `node build/server.mjs` |
|
|
278
281
|
|
|
279
282
|
**Про фреймворк команда не знает.** Чем собирать и чем оживлять, приносит пресет из пакета
|
|
280
283
|
адаптера, поэтому приложение на другом фреймворке подключается своим пресетом и в командах не
|
|
@@ -297,12 +300,13 @@ export default defineAppConfig({
|
|
|
297
300
|
|
|
298
301
|
## Подпути
|
|
299
302
|
|
|
300
|
-
| Подпуть
|
|
301
|
-
|
|
|
302
|
-
| `@foxford/den`
|
|
303
|
-
| `@foxford/den/define`
|
|
304
|
-
| `@foxford/den/adapter` | шов резолва для адаптеров хостов
|
|
305
|
-
| `@foxford/den/island`
|
|
306
|
-
|
|
307
|
-
| `@foxford/den/
|
|
308
|
-
| `@foxford/den/
|
|
303
|
+
| Подпуть | Что там |
|
|
304
|
+
| ---------------------- | ----------------------------------------------------------- |
|
|
305
|
+
| `@foxford/den` | рантайм, `define*`, менеджеры контейнеров и состояния |
|
|
306
|
+
| `@foxford/den/define` | те же `define*` без сайд-эффекта установки резолвера |
|
|
307
|
+
| `@foxford/den/adapter` | шов резолва для адаптеров хостов |
|
|
308
|
+
| `@foxford/den/island` | остров: жизненный цикл, шов `renderApp`, порт `ViewAdapter`, очередь клиентских падений (`reportIslandError` / `installIslandErrorDrain`) |
|
|
309
|
+
|
|
310
|
+
| `@foxford/den/serve` | сетевой транспорт: `serveApp`, `appPlugin` |
|
|
311
|
+
| `@foxford/den/build` | конфиг приложения и сборка: `defineAppConfig`, `buildApp` |
|
|
312
|
+
| `@foxford/den/bin` | точка входа команды `den` |
|
package/bin/den.cjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
3
|
|
|
4
|
-
var
|
|
4
|
+
var _chunkG6RT4EBOcjs = require('../chunk-G6RT4EBO.cjs');
|
|
5
5
|
require('../chunk-KQAB6C6Q.cjs');
|
|
6
6
|
require('../chunk-A6ZPAM6Z.cjs');
|
|
7
7
|
|
|
@@ -70,11 +70,11 @@ function run(argv) {
|
|
|
70
70
|
}
|
|
71
71
|
const target = { base: options["base"], root: (_a = options["root"]) != null ? _a : process.cwd() };
|
|
72
72
|
if (command === "build") {
|
|
73
|
-
yield
|
|
73
|
+
yield _chunkG6RT4EBOcjs.buildApp.call(void 0, target);
|
|
74
74
|
return;
|
|
75
75
|
}
|
|
76
76
|
if (command === "dev") {
|
|
77
|
-
yield
|
|
77
|
+
yield _chunkG6RT4EBOcjs.devApp.call(void 0, target);
|
|
78
78
|
return;
|
|
79
79
|
}
|
|
80
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
package/builder/index.cjs
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
|
|
4
4
|
|
|
5
|
-
var
|
|
5
|
+
var _chunkG6RT4EBOcjs = require('../chunk-G6RT4EBO.cjs');
|
|
6
6
|
require('../chunk-KQAB6C6Q.cjs');
|
|
7
7
|
require('../chunk-A6ZPAM6Z.cjs');
|
|
8
8
|
require('../chunk-T5QVCXVB.cjs');
|
|
@@ -10,4 +10,4 @@ require('../chunk-T5QVCXVB.cjs');
|
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
|
|
13
|
-
exports.buildApp =
|
|
13
|
+
exports.buildApp = _chunkG6RT4EBOcjs.buildApp; exports.defineAppConfig = _chunkG6RT4EBOcjs.defineAppConfig; exports.devApp = _chunkG6RT4EBOcjs.devApp;
|
package/builder/index.d.cts
CHANGED
|
@@ -75,6 +75,16 @@ interface AppServerConfig {
|
|
|
75
75
|
* default-экспорта разъезжается, и вместо фабрики приходит пространство имён модуля.
|
|
76
76
|
*/
|
|
77
77
|
noExternal?: Array<string | RegExp>;
|
|
78
|
+
/**
|
|
79
|
+
* Зависимости, которые серверный выход обязан оставить ВНЕШНИМИ, а не вплавить в бандл.
|
|
80
|
+
*
|
|
81
|
+
* Типичный случай — `@foxford/den-sentry`: вместе с ним в бандл уезжают `@sentry/node` и
|
|
82
|
+
* хуки `require`, а внутри `server.mjs` с top-level await их голый `require` даёт
|
|
83
|
+
* `ERR_AMBIGUOUS_MODULE_SYNTAX`. Пакет из npm остаётся снаружи целиком — транзитив
|
|
84
|
+
* грузится уже из его `node_modules`. Должен резолвиться из корня приложения (прямая
|
|
85
|
+
* зависимость в `package.json`).
|
|
86
|
+
*/
|
|
87
|
+
external?: Array<string | RegExp>;
|
|
78
88
|
}
|
|
79
89
|
/** Конфиг приложения. */
|
|
80
90
|
interface AppConfig {
|
package/builder/index.d.ts
CHANGED
|
@@ -75,6 +75,16 @@ interface AppServerConfig {
|
|
|
75
75
|
* default-экспорта разъезжается, и вместо фабрики приходит пространство имён модуля.
|
|
76
76
|
*/
|
|
77
77
|
noExternal?: Array<string | RegExp>;
|
|
78
|
+
/**
|
|
79
|
+
* Зависимости, которые серверный выход обязан оставить ВНЕШНИМИ, а не вплавить в бандл.
|
|
80
|
+
*
|
|
81
|
+
* Типичный случай — `@foxford/den-sentry`: вместе с ним в бандл уезжают `@sentry/node` и
|
|
82
|
+
* хуки `require`, а внутри `server.mjs` с top-level await их голый `require` даёт
|
|
83
|
+
* `ERR_AMBIGUOUS_MODULE_SYNTAX`. Пакет из npm остаётся снаружи целиком — транзитив
|
|
84
|
+
* грузится уже из его `node_modules`. Должен резолвиться из корня приложения (прямая
|
|
85
|
+
* зависимость в `package.json`).
|
|
86
|
+
*/
|
|
87
|
+
external?: Array<string | RegExp>;
|
|
78
88
|
}
|
|
79
89
|
/** Конфиг приложения. */
|
|
80
90
|
interface AppConfig {
|
package/builder/index.js
CHANGED
|
@@ -203,9 +203,10 @@ function dependenciesOf(root) {
|
|
|
203
203
|
return [...Object.keys((_a = manifest.dependencies) != null ? _a : {}), ...Object.keys((_b = manifest.devDependencies) != null ? _b : {})];
|
|
204
204
|
}
|
|
205
205
|
function viteConfigFor(options) {
|
|
206
|
-
var _a, _b, _c, _d, _e;
|
|
206
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
207
207
|
const { assets, base, config, configPath, entryFileNames, input, mode, outDir, root, target } = options;
|
|
208
208
|
const entry = (_a = config.entry) != null ? _a : "src/index.ts";
|
|
209
|
+
const external = (_c = (_b = config.server) == null ? void 0 : _b.external) != null ? _c : [];
|
|
209
210
|
const composed = {
|
|
210
211
|
// Сборщику относительный префикс, если своего нет: ленивые чанки тогда грузятся от адреса
|
|
211
212
|
// уже загруженного чанка, и подмена CDN уводит за собой и их
|
|
@@ -220,7 +221,7 @@ function viteConfigFor(options) {
|
|
|
220
221
|
// к файлу, а входы у нас виртуальные — их резолвит плагин
|
|
221
222
|
rollupOptions: __spreadValues(__spreadValues({
|
|
222
223
|
input
|
|
223
|
-
}, target === "server" ? { external: [ENGINE] } : {}), entryFileNames === void 0 ? {} : { output: { entryFileNames } }),
|
|
224
|
+
}, target === "server" ? { external: [ENGINE, ...external] } : {}), entryFileNames === void 0 ? {} : { output: { entryFileNames } }),
|
|
224
225
|
ssr: target === "server"
|
|
225
226
|
},
|
|
226
227
|
configFile: false,
|
|
@@ -228,11 +229,14 @@ function viteConfigFor(options) {
|
|
|
228
229
|
// программный вызов он иначе не доедет.
|
|
229
230
|
mode,
|
|
230
231
|
plugins: [
|
|
231
|
-
...(
|
|
232
|
+
...(_e = (_d = config.preset) == null ? void 0 : _d.plugins) != null ? _e : [],
|
|
232
233
|
denAppModules({ assets, base, config, configPath, entry: path2.join(root, entry), mode })
|
|
233
234
|
],
|
|
234
235
|
root,
|
|
235
|
-
ssr: {
|
|
236
|
+
ssr: {
|
|
237
|
+
external,
|
|
238
|
+
noExternal: (_g = (_f = config.server) == null ? void 0 : _f.noExternal) != null ? _g : []
|
|
239
|
+
}
|
|
236
240
|
};
|
|
237
241
|
return config.vite ? config.vite(composed, target) : composed;
|
|
238
242
|
}
|
|
@@ -203,9 +203,10 @@ function dependenciesOf(root) {
|
|
|
203
203
|
return [...Object.keys((_a = manifest.dependencies) != null ? _a : {}), ...Object.keys((_b = manifest.devDependencies) != null ? _b : {})];
|
|
204
204
|
}
|
|
205
205
|
function viteConfigFor(options) {
|
|
206
|
-
var _a, _b, _c, _d, _e;
|
|
206
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
207
207
|
const { assets, base, config, configPath, entryFileNames, input, mode, outDir, root, target } = options;
|
|
208
208
|
const entry = (_a = config.entry) != null ? _a : "src/index.ts";
|
|
209
|
+
const external = (_c = (_b = config.server) == null ? void 0 : _b.external) != null ? _c : [];
|
|
209
210
|
const composed = {
|
|
210
211
|
// Сборщику относительный префикс, если своего нет: ленивые чанки тогда грузятся от адреса
|
|
211
212
|
// уже загруженного чанка, и подмена CDN уводит за собой и их
|
|
@@ -220,7 +221,7 @@ function viteConfigFor(options) {
|
|
|
220
221
|
// к файлу, а входы у нас виртуальные — их резолвит плагин
|
|
221
222
|
rollupOptions: _chunkT5QVCXVBcjs.__spreadValues.call(void 0, _chunkT5QVCXVBcjs.__spreadValues.call(void 0, {
|
|
222
223
|
input
|
|
223
|
-
}, target === "server" ? { external: [ENGINE] } : {}), entryFileNames === void 0 ? {} : { output: { entryFileNames } }),
|
|
224
|
+
}, target === "server" ? { external: [ENGINE, ...external] } : {}), entryFileNames === void 0 ? {} : { output: { entryFileNames } }),
|
|
224
225
|
ssr: target === "server"
|
|
225
226
|
},
|
|
226
227
|
configFile: false,
|
|
@@ -228,11 +229,14 @@ function viteConfigFor(options) {
|
|
|
228
229
|
// программный вызов он иначе не доедет.
|
|
229
230
|
mode,
|
|
230
231
|
plugins: [
|
|
231
|
-
...(
|
|
232
|
+
...(_e = (_d = config.preset) == null ? void 0 : _d.plugins) != null ? _e : [],
|
|
232
233
|
denAppModules({ assets, base, config, configPath, entry: _path2.default.join(root, entry), mode })
|
|
233
234
|
],
|
|
234
235
|
root,
|
|
235
|
-
ssr: {
|
|
236
|
+
ssr: {
|
|
237
|
+
external,
|
|
238
|
+
noExternal: (_g = (_f = config.server) == null ? void 0 : _f.noExternal) != null ? _g : []
|
|
239
|
+
}
|
|
236
240
|
};
|
|
237
241
|
return config.vite ? config.vite(composed, target) : composed;
|
|
238
242
|
}
|
package/island/index.cjs
CHANGED
|
@@ -23,6 +23,50 @@ require('../chunk-A6ZPAM6Z.cjs');
|
|
|
23
23
|
|
|
24
24
|
var _chunkT5QVCXVBcjs = require('../chunk-T5QVCXVB.cjs');
|
|
25
25
|
|
|
26
|
+
// src/island/errors.ts
|
|
27
|
+
var ISLAND_ERRORS_KEY = "__DEN_ISLAND_ERRORS__";
|
|
28
|
+
function denWindow() {
|
|
29
|
+
return typeof window === "undefined" ? void 0 : window;
|
|
30
|
+
}
|
|
31
|
+
function errorsSlot(w) {
|
|
32
|
+
const existing = w[ISLAND_ERRORS_KEY];
|
|
33
|
+
if (existing !== void 0) {
|
|
34
|
+
return existing;
|
|
35
|
+
}
|
|
36
|
+
const created = [];
|
|
37
|
+
w[ISLAND_ERRORS_KEY] = created;
|
|
38
|
+
return created;
|
|
39
|
+
}
|
|
40
|
+
function reportIslandError(error, context) {
|
|
41
|
+
const w = denWindow();
|
|
42
|
+
if (w === void 0) {
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
errorsSlot(w).push({ context, error });
|
|
46
|
+
}
|
|
47
|
+
function installIslandErrorDrain(drain) {
|
|
48
|
+
const w = denWindow();
|
|
49
|
+
if (w === void 0) {
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
if (drain === null) {
|
|
53
|
+
const empty = [];
|
|
54
|
+
w[ISLAND_ERRORS_KEY] = empty;
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
const current = w[ISLAND_ERRORS_KEY];
|
|
58
|
+
const pending = Array.isArray(current) ? current : [];
|
|
59
|
+
for (const report of pending) {
|
|
60
|
+
drain(report.error, report.context);
|
|
61
|
+
}
|
|
62
|
+
w[ISLAND_ERRORS_KEY] = {
|
|
63
|
+
push(report) {
|
|
64
|
+
drain(report.error, report.context);
|
|
65
|
+
return 1;
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
|
|
26
70
|
// src/island/navigate.ts
|
|
27
71
|
var NAVIGATE_EVENT = "den:navigate";
|
|
28
72
|
function navigateIsland(node, route) {
|
|
@@ -122,4 +166,7 @@ function describeIsland(descriptor, denState) {
|
|
|
122
166
|
|
|
123
167
|
|
|
124
168
|
|
|
125
|
-
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
exports.DOCUMENT_EVENT = DOCUMENT_EVENT; exports.ISLAND_ERRORS_KEY = ISLAND_ERRORS_KEY; 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.installIslandErrorDrain = installIslandErrorDrain; 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.reportIslandError = reportIslandError; exports.resolveEagerUnits = _chunkXDM6J4LJcjs.resolveEagerUnits; exports.resolveViewAdapter = _chunkXOJ2VWX4cjs.resolveViewAdapter; exports.slotAddress = slotAddress; exports.subscribeIslands = subscribeIslands; exports.viewAdapterOf = _chunkXOJ2VWX4cjs.viewAdapterOf;
|
package/island/index.d.cts
CHANGED
|
@@ -69,6 +69,51 @@ interface DocumentSource {
|
|
|
69
69
|
*/
|
|
70
70
|
declare function isDocumentSource(value: unknown): value is DocumentSource;
|
|
71
71
|
|
|
72
|
+
/**
|
|
73
|
+
* Клиентские падения острова между бандлами.
|
|
74
|
+
*
|
|
75
|
+
* Сетевое приложение оживает своим бандлом, хост — своим. У каждого своя копия модулей,
|
|
76
|
+
* module-level очередь в одной не видна в другой. Шина — один ключ на `window`.
|
|
77
|
+
*
|
|
78
|
+
* Остров всегда делает `.push({ error, context })` ({@link reportIslandError}). Пока хоста
|
|
79
|
+
* нет — это обычный массив. Хост после Sentry.init зовёт {@link installIslandErrorDrain}:
|
|
80
|
+
* сливает backlog и подменяет значение объектом с тем же `.push` — дальше ошибки идут
|
|
81
|
+
* сразу в приёмник. Обёртки нет: стек и тип остаются приложению.
|
|
82
|
+
*/
|
|
83
|
+
/** Фаза, на которой остров упал. */
|
|
84
|
+
type IslandErrorPhase = 'render' | 'hydrate' | 'navigate';
|
|
85
|
+
/** Контекст падения острова для приёмника. */
|
|
86
|
+
interface IslandErrorContext {
|
|
87
|
+
/** Имя приложения из дескриптора. */
|
|
88
|
+
appName: string;
|
|
89
|
+
phase: IslandErrorPhase;
|
|
90
|
+
}
|
|
91
|
+
/** Одна запись: исходное исключение и контекст. */
|
|
92
|
+
interface IslandErrorReport {
|
|
93
|
+
error: unknown;
|
|
94
|
+
context: IslandErrorContext;
|
|
95
|
+
}
|
|
96
|
+
/** Приёмник: хост шлёт в Sentry (или куда угодно). */
|
|
97
|
+
type IslandErrorDrain = (error: unknown, context: IslandErrorContext) => void;
|
|
98
|
+
/**
|
|
99
|
+
* Ключ на `window` — обязан совпадать у хоста и remote.
|
|
100
|
+
*
|
|
101
|
+
* Значение: массив (до приёмника) либо объект с `push` (после {@link installIslandErrorDrain}).
|
|
102
|
+
*/
|
|
103
|
+
declare const ISLAND_ERRORS_KEY = "__DEN_ISLAND_ERRORS__";
|
|
104
|
+
/**
|
|
105
|
+
* Кладёт исходное исключение в шину на `window`.
|
|
106
|
+
*
|
|
107
|
+
* До приёмника — в массив; после — сразу в drain (тот же `.push`).
|
|
108
|
+
*/
|
|
109
|
+
declare function reportIslandError(error: unknown, context: IslandErrorContext): void;
|
|
110
|
+
/**
|
|
111
|
+
* Сливает накопленное и подменяет слот drain-объектом с `.push`.
|
|
112
|
+
*
|
|
113
|
+
* Зовёт хост после инициализации наблюдаемости. `null` — снимает: снова обычный массив.
|
|
114
|
+
*/
|
|
115
|
+
declare function installIslandErrorDrain(drain: IslandErrorDrain | null): void;
|
|
116
|
+
|
|
72
117
|
/**
|
|
73
118
|
* Адрес, по которому рендерится остров.
|
|
74
119
|
*
|
|
@@ -324,4 +369,4 @@ declare function getAppContainer(): Container;
|
|
|
324
369
|
*/
|
|
325
370
|
declare function slotAddress(app: string, slot: string): string;
|
|
326
371
|
|
|
327
|
-
export { AppAssets, type CreateIslandContainerOptions, DOCUMENT_EVENT, type DocumentContribution, type DocumentHead, type DocumentSource, IslandDescriptor, type IslandRecord, type IslandRoute, NAVIGATE_EVENT, type RenderAppRequest, type RenderAppResult, activateIslandViewModels, collectDocumentContribution, collectIslandState, createIslandContainer, describeIsland, getAppContainer, getIslandsSnapshot, hydrateIslandState, isDocumentSource, navigateIsland, onIslandDocument, onIslandNavigate, publishIslandDocument, registerIsland, renderApp, resolveEagerUnits, slotAddress, subscribeIslands };
|
|
372
|
+
export { AppAssets, type CreateIslandContainerOptions, DOCUMENT_EVENT, type DocumentContribution, type DocumentHead, type DocumentSource, ISLAND_ERRORS_KEY, IslandDescriptor, type IslandErrorContext, type IslandErrorDrain, type IslandErrorPhase, type IslandErrorReport, type IslandRecord, type IslandRoute, NAVIGATE_EVENT, type RenderAppRequest, type RenderAppResult, activateIslandViewModels, collectDocumentContribution, collectIslandState, createIslandContainer, describeIsland, getAppContainer, getIslandsSnapshot, hydrateIslandState, installIslandErrorDrain, isDocumentSource, navigateIsland, onIslandDocument, onIslandNavigate, publishIslandDocument, registerIsland, renderApp, reportIslandError, resolveEagerUnits, slotAddress, subscribeIslands };
|
package/island/index.d.ts
CHANGED
|
@@ -69,6 +69,51 @@ interface DocumentSource {
|
|
|
69
69
|
*/
|
|
70
70
|
declare function isDocumentSource(value: unknown): value is DocumentSource;
|
|
71
71
|
|
|
72
|
+
/**
|
|
73
|
+
* Клиентские падения острова между бандлами.
|
|
74
|
+
*
|
|
75
|
+
* Сетевое приложение оживает своим бандлом, хост — своим. У каждого своя копия модулей,
|
|
76
|
+
* module-level очередь в одной не видна в другой. Шина — один ключ на `window`.
|
|
77
|
+
*
|
|
78
|
+
* Остров всегда делает `.push({ error, context })` ({@link reportIslandError}). Пока хоста
|
|
79
|
+
* нет — это обычный массив. Хост после Sentry.init зовёт {@link installIslandErrorDrain}:
|
|
80
|
+
* сливает backlog и подменяет значение объектом с тем же `.push` — дальше ошибки идут
|
|
81
|
+
* сразу в приёмник. Обёртки нет: стек и тип остаются приложению.
|
|
82
|
+
*/
|
|
83
|
+
/** Фаза, на которой остров упал. */
|
|
84
|
+
type IslandErrorPhase = 'render' | 'hydrate' | 'navigate';
|
|
85
|
+
/** Контекст падения острова для приёмника. */
|
|
86
|
+
interface IslandErrorContext {
|
|
87
|
+
/** Имя приложения из дескриптора. */
|
|
88
|
+
appName: string;
|
|
89
|
+
phase: IslandErrorPhase;
|
|
90
|
+
}
|
|
91
|
+
/** Одна запись: исходное исключение и контекст. */
|
|
92
|
+
interface IslandErrorReport {
|
|
93
|
+
error: unknown;
|
|
94
|
+
context: IslandErrorContext;
|
|
95
|
+
}
|
|
96
|
+
/** Приёмник: хост шлёт в Sentry (или куда угодно). */
|
|
97
|
+
type IslandErrorDrain = (error: unknown, context: IslandErrorContext) => void;
|
|
98
|
+
/**
|
|
99
|
+
* Ключ на `window` — обязан совпадать у хоста и remote.
|
|
100
|
+
*
|
|
101
|
+
* Значение: массив (до приёмника) либо объект с `push` (после {@link installIslandErrorDrain}).
|
|
102
|
+
*/
|
|
103
|
+
declare const ISLAND_ERRORS_KEY = "__DEN_ISLAND_ERRORS__";
|
|
104
|
+
/**
|
|
105
|
+
* Кладёт исходное исключение в шину на `window`.
|
|
106
|
+
*
|
|
107
|
+
* До приёмника — в массив; после — сразу в drain (тот же `.push`).
|
|
108
|
+
*/
|
|
109
|
+
declare function reportIslandError(error: unknown, context: IslandErrorContext): void;
|
|
110
|
+
/**
|
|
111
|
+
* Сливает накопленное и подменяет слот drain-объектом с `.push`.
|
|
112
|
+
*
|
|
113
|
+
* Зовёт хост после инициализации наблюдаемости. `null` — снимает: снова обычный массив.
|
|
114
|
+
*/
|
|
115
|
+
declare function installIslandErrorDrain(drain: IslandErrorDrain | null): void;
|
|
116
|
+
|
|
72
117
|
/**
|
|
73
118
|
* Адрес, по которому рендерится остров.
|
|
74
119
|
*
|
|
@@ -324,4 +369,4 @@ declare function getAppContainer(): Container;
|
|
|
324
369
|
*/
|
|
325
370
|
declare function slotAddress(app: string, slot: string): string;
|
|
326
371
|
|
|
327
|
-
export { AppAssets, type CreateIslandContainerOptions, DOCUMENT_EVENT, type DocumentContribution, type DocumentHead, type DocumentSource, IslandDescriptor, type IslandRecord, type IslandRoute, NAVIGATE_EVENT, type RenderAppRequest, type RenderAppResult, activateIslandViewModels, collectDocumentContribution, collectIslandState, createIslandContainer, describeIsland, getAppContainer, getIslandsSnapshot, hydrateIslandState, isDocumentSource, navigateIsland, onIslandDocument, onIslandNavigate, publishIslandDocument, registerIsland, renderApp, resolveEagerUnits, slotAddress, subscribeIslands };
|
|
372
|
+
export { AppAssets, type CreateIslandContainerOptions, DOCUMENT_EVENT, type DocumentContribution, type DocumentHead, type DocumentSource, ISLAND_ERRORS_KEY, IslandDescriptor, type IslandErrorContext, type IslandErrorDrain, type IslandErrorPhase, type IslandErrorReport, type IslandRecord, type IslandRoute, NAVIGATE_EVENT, type RenderAppRequest, type RenderAppResult, activateIslandViewModels, collectDocumentContribution, collectIslandState, createIslandContainer, describeIsland, getAppContainer, getIslandsSnapshot, hydrateIslandState, installIslandErrorDrain, isDocumentSource, navigateIsland, onIslandDocument, onIslandNavigate, publishIslandDocument, registerIsland, renderApp, reportIslandError, resolveEagerUnits, slotAddress, subscribeIslands };
|
package/island/index.js
CHANGED
|
@@ -23,6 +23,50 @@ import {
|
|
|
23
23
|
__spreadValues
|
|
24
24
|
} from "../chunk-6FNC3XMI.js";
|
|
25
25
|
|
|
26
|
+
// src/island/errors.ts
|
|
27
|
+
var ISLAND_ERRORS_KEY = "__DEN_ISLAND_ERRORS__";
|
|
28
|
+
function denWindow() {
|
|
29
|
+
return typeof window === "undefined" ? void 0 : window;
|
|
30
|
+
}
|
|
31
|
+
function errorsSlot(w) {
|
|
32
|
+
const existing = w[ISLAND_ERRORS_KEY];
|
|
33
|
+
if (existing !== void 0) {
|
|
34
|
+
return existing;
|
|
35
|
+
}
|
|
36
|
+
const created = [];
|
|
37
|
+
w[ISLAND_ERRORS_KEY] = created;
|
|
38
|
+
return created;
|
|
39
|
+
}
|
|
40
|
+
function reportIslandError(error, context) {
|
|
41
|
+
const w = denWindow();
|
|
42
|
+
if (w === void 0) {
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
errorsSlot(w).push({ context, error });
|
|
46
|
+
}
|
|
47
|
+
function installIslandErrorDrain(drain) {
|
|
48
|
+
const w = denWindow();
|
|
49
|
+
if (w === void 0) {
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
if (drain === null) {
|
|
53
|
+
const empty = [];
|
|
54
|
+
w[ISLAND_ERRORS_KEY] = empty;
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
const current = w[ISLAND_ERRORS_KEY];
|
|
58
|
+
const pending = Array.isArray(current) ? current : [];
|
|
59
|
+
for (const report of pending) {
|
|
60
|
+
drain(report.error, report.context);
|
|
61
|
+
}
|
|
62
|
+
w[ISLAND_ERRORS_KEY] = {
|
|
63
|
+
push(report) {
|
|
64
|
+
drain(report.error, report.context);
|
|
65
|
+
return 1;
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
|
|
26
70
|
// src/island/navigate.ts
|
|
27
71
|
var NAVIGATE_EVENT = "den:navigate";
|
|
28
72
|
function navigateIsland(node, route) {
|
|
@@ -97,6 +141,7 @@ function describeIsland(descriptor, denState) {
|
|
|
97
141
|
}
|
|
98
142
|
export {
|
|
99
143
|
DOCUMENT_EVENT,
|
|
144
|
+
ISLAND_ERRORS_KEY,
|
|
100
145
|
NAVIGATE_EVENT,
|
|
101
146
|
ViewLayerToken,
|
|
102
147
|
activateIslandViewModels,
|
|
@@ -107,6 +152,7 @@ export {
|
|
|
107
152
|
getAppContainer,
|
|
108
153
|
getIslandsSnapshot,
|
|
109
154
|
hydrateIslandState,
|
|
155
|
+
installIslandErrorDrain,
|
|
110
156
|
isDocumentSource,
|
|
111
157
|
matchRoute,
|
|
112
158
|
navigateIsland,
|
|
@@ -117,6 +163,7 @@ export {
|
|
|
117
163
|
publishIslandDocument,
|
|
118
164
|
registerIsland,
|
|
119
165
|
renderApp,
|
|
166
|
+
reportIslandError,
|
|
120
167
|
resolveEagerUnits,
|
|
121
168
|
resolveViewAdapter,
|
|
122
169
|
slotAddress,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@foxford/den",
|
|
3
|
-
"version": "3.2.
|
|
3
|
+
"version": "3.2.5-beta-3141d32-20260904",
|
|
4
4
|
"description": "Den — декларативный метафреймворк Foxford (core runtime)",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"foxford",
|
|
@@ -90,13 +90,14 @@
|
|
|
90
90
|
"bin",
|
|
91
91
|
"builder",
|
|
92
92
|
"chunk-3RX2OSUL.js",
|
|
93
|
-
"chunk-
|
|
93
|
+
"chunk-4XR6EUOW.js",
|
|
94
94
|
"chunk-6FNC3XMI.js",
|
|
95
95
|
"chunk-6QV4L6R2.cjs",
|
|
96
96
|
"chunk-A6ZPAM6Z.cjs",
|
|
97
97
|
"chunk-C7BM4DGX.cjs",
|
|
98
98
|
"chunk-E23E2VUC.js",
|
|
99
99
|
"chunk-E56BKBQZ.js",
|
|
100
|
+
"chunk-G6RT4EBO.cjs",
|
|
100
101
|
"chunk-H5DKRHHQ.cjs",
|
|
101
102
|
"chunk-KQAB6C6Q.cjs",
|
|
102
103
|
"chunk-RG7ZV5BD.js",
|
|
@@ -106,7 +107,6 @@
|
|
|
106
107
|
"chunk-WBHHHICS.js",
|
|
107
108
|
"chunk-XDM6J4LJ.cjs",
|
|
108
109
|
"chunk-XOJ2VWX4.cjs",
|
|
109
|
-
"chunk-XU5HUIPK.js",
|
|
110
110
|
"define-repository-CwAXed2X.d.cts",
|
|
111
111
|
"define-repository-q_T4hqeP.d.ts",
|
|
112
112
|
"define-slot-DouHW8RK.d.cts",
|
|
@@ -134,6 +134,6 @@
|
|
|
134
134
|
"view-adapter-BV62bdhM.d.ts",
|
|
135
135
|
"view-adapter-DF1me25J.d.cts"
|
|
136
136
|
],
|
|
137
|
-
"sha": "
|
|
137
|
+
"sha": "3141d32",
|
|
138
138
|
"scripts": {}
|
|
139
139
|
}
|
package/serve/index.cjs
CHANGED
|
@@ -140,7 +140,7 @@ var appPlugin = (instance, options) => {
|
|
|
140
140
|
return result;
|
|
141
141
|
} catch (error) {
|
|
142
142
|
serveLog.error(`\u0440\u0435\u043D\u0434\u0435\u0440 \xAB${pathname}\xBB \u0443\u043F\u0430\u043B \u0437\u0430 ${String(Date.now() - startedAt)}\u043C\u0441`, error);
|
|
143
|
-
return reply.
|
|
143
|
+
return reply.send(error instanceof Error ? error : new Error(String(error)));
|
|
144
144
|
}
|
|
145
145
|
}));
|
|
146
146
|
return Promise.resolve();
|
package/serve/index.js
CHANGED
|
@@ -140,7 +140,7 @@ var appPlugin = (instance, options) => {
|
|
|
140
140
|
return result;
|
|
141
141
|
} catch (error) {
|
|
142
142
|
serveLog.error(`\u0440\u0435\u043D\u0434\u0435\u0440 \xAB${pathname}\xBB \u0443\u043F\u0430\u043B \u0437\u0430 ${String(Date.now() - startedAt)}\u043C\u0441`, error);
|
|
143
|
-
return reply.
|
|
143
|
+
return reply.send(error instanceof Error ? error : new Error(String(error)));
|
|
144
144
|
}
|
|
145
145
|
}));
|
|
146
146
|
return Promise.resolve();
|