@foxford/den 3.0.0 → 3.1.1

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 (57) hide show
  1. package/README.mdx +266 -2
  2. package/adapter.cjs +1 -1
  3. package/adapter.d.cts +1 -1
  4. package/adapter.d.ts +1 -1
  5. package/adapter.js +1 -1
  6. package/bin/den.cjs +88 -0
  7. package/bin/den.d.cts +2 -0
  8. package/bin/den.d.ts +2 -0
  9. package/bin/den.js +88 -0
  10. package/builder/index.cjs +12 -0
  11. package/builder/index.d.cts +120 -0
  12. package/builder/index.d.ts +120 -0
  13. package/builder/index.js +12 -0
  14. package/chunk-3RX2OSUL.js +21 -0
  15. package/{chunk-HJO26HIQ.js → chunk-6FNC3XMI.js} +4 -0
  16. package/chunk-6QV4L6R2.cjs +21 -0
  17. package/{chunk-WRQC6BVJ.js → chunk-MCN4GFEQ.js} +4 -19
  18. package/chunk-OKRO4G4L.cjs +354 -0
  19. package/chunk-REFU7UMG.js +354 -0
  20. package/{chunk-XHYR3SGG.cjs → chunk-T5QVCXVB.cjs} +5 -1
  21. package/chunk-TPHJUDHG.js +230 -0
  22. package/{chunk-5DJZZML5.cjs → chunk-Z5BL4DJA.cjs} +10 -25
  23. package/chunk-ZOU6W6ZQ.cjs +230 -0
  24. package/{define-repository-MDEWHqM7.d.cts → define-repository-CwAXed2X.d.cts} +1 -1
  25. package/{define-repository-Ca62_YCy.d.ts → define-repository-q_T4hqeP.d.ts} +1 -1
  26. package/{define-slot-Dil8Kr1A.d.ts → define-slot-Cngzae6i.d.ts} +1 -1
  27. package/{define-slot-BKvArA02.d.cts → define-slot-DYNCLDJY.d.cts} +1 -1
  28. package/define.cjs +6 -4
  29. package/define.d.cts +3 -3
  30. package/define.d.ts +3 -3
  31. package/define.js +5 -3
  32. package/den.js +60 -0
  33. package/descriptor-DQ6Gi7A_.d.ts +98 -0
  34. package/descriptor-Dujg_1on.d.cts +98 -0
  35. package/index.cjs +25 -23
  36. package/index.d.cts +7 -5
  37. package/index.d.ts +7 -5
  38. package/index.js +12 -10
  39. package/island/index.cjs +12 -157
  40. package/island/index.d.cts +40 -30
  41. package/island/index.d.ts +40 -30
  42. package/island/index.js +19 -164
  43. package/package.json +59 -16
  44. package/routes-0uXJ0fux.d.ts +64 -0
  45. package/routes-B0hDrkiF.d.cts +64 -0
  46. package/serve/index.cjs +137 -0
  47. package/serve/index.d.cts +60 -0
  48. package/serve/index.d.ts +60 -0
  49. package/serve/index.js +137 -0
  50. package/server-render-CocWXQKC.d.cts +48 -0
  51. package/server-render-CocWXQKC.d.ts +48 -0
  52. package/{types-COwVwgzE.d.cts → types-Dx5qGiwk.d.cts} +1 -1
  53. package/{types-COwVwgzE.d.ts → types-Dx5qGiwk.d.ts} +1 -1
  54. package/view-adapter-B4gEb-ms.d.ts +77 -0
  55. package/view-adapter-DJ7yB6Ml.d.cts +77 -0
  56. package/view-adapter-BvDC5O4y.d.cts +0 -188
  57. package/view-adapter-DtNgyjIf.d.ts +0 -188
@@ -0,0 +1,230 @@
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true});
2
+
3
+ var _chunkXOJ2VWX4cjs = require('./chunk-XOJ2VWX4.cjs');
4
+
5
+
6
+ var _chunk6QV4L6R2cjs = require('./chunk-6QV4L6R2.cjs');
7
+
8
+
9
+
10
+
11
+ var _chunkA6ZPAM6Zcjs = require('./chunk-A6ZPAM6Z.cjs');
12
+
13
+
14
+
15
+ var _chunkT5QVCXVBcjs = require('./chunk-T5QVCXVB.cjs');
16
+
17
+ // src/island/document.ts
18
+ function isDocumentSource(value) {
19
+ return typeof value === "object" && value !== null && typeof value.describeDocument === "function";
20
+ }
21
+
22
+ // src/island/runtime.ts
23
+ var _ioc = require('@foxford/ioc');
24
+ var appContainer = null;
25
+ function getAppContainer() {
26
+ var _a;
27
+ if (appContainer === null) {
28
+ appContainer = new (0, _ioc.Container)();
29
+ ((_a = _chunkA6ZPAM6Zcjs.logger.getLogger("island:runtime")) != null ? _a : _chunkA6ZPAM6Zcjs.logger).debug("app-\u043A\u043E\u043D\u0442\u0435\u0439\u043D\u0435\u0440 \u0441\u043E\u0437\u0434\u0430\u043D");
30
+ }
31
+ return appContainer;
32
+ }
33
+
34
+ // src/island/lifecycle.ts
35
+ var _logger = require('@foxford/logger');
36
+ var EMPTY_ROUTE = { params: {}, pathname: "", search: "" };
37
+ var REDIRECT_FOUND = 302;
38
+ function createIslandContainer(descriptor, options = {}) {
39
+ var _a, _b, _c, _d, _e;
40
+ const { parent = getAppContainer() } = options;
41
+ const appLog = (_b = _logger.log.getLogger((_a = descriptor.name) != null ? _a : "app")) != null ? _b : _chunkA6ZPAM6Zcjs.logger;
42
+ const islandLog = (_c = appLog.getLogger("client")) != null ? _c : appLog;
43
+ try {
44
+ const container = parent.createChild();
45
+ 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
+ }
61
+ return container;
62
+ } catch (error) {
63
+ 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);
64
+ return null;
65
+ }
66
+ }
67
+ function hydrateIslandState(container, descriptor, denState) {
68
+ var _a, _b;
69
+ for (const vm of (_a = descriptor.viewModels) != null ? _a : []) {
70
+ const instance = container.resolve(vm.token);
71
+ const saved = denState[vm.token.description];
72
+ if (saved !== void 0 && typeof ((_b = instance.state) == null ? void 0 : _b.set) === "function") {
73
+ _chunkA6ZPAM6Zcjs.unitLogger.call(void 0, container, vm.token.description).debug("\u0433\u0438\u0434\u0440\u0430\u0446\u0438\u044F VM \u0438\u0437 denState");
74
+ instance.state.set(saved);
75
+ }
76
+ }
77
+ }
78
+ function activateIslandViewModels(_0, _1) {
79
+ return _chunkT5QVCXVBcjs.__async.call(void 0, this, arguments, function* (container, descriptor, route = EMPTY_ROUTE) {
80
+ var _a;
81
+ yield Promise.all(
82
+ ((_a = descriptor.viewModels) != null ? _a : []).map((vm) => _chunkT5QVCXVBcjs.__async.call(void 0, null, null, function* () {
83
+ var _a2;
84
+ const instance = container.resolve(vm.token);
85
+ _chunkA6ZPAM6Zcjs.unitLogger.call(void 0, container, vm.token.description).debug(`\u0430\u043A\u0442\u0438\u0432\u0430\u0446\u0438\u044F VM (${route.pathname || "\u2014"})`);
86
+ yield (_a2 = instance.activate) == null ? void 0 : _a2.call(instance, route);
87
+ }))
88
+ );
89
+ });
90
+ }
91
+ function collectIslandState(container, descriptor) {
92
+ var _a, _b;
93
+ const state = {};
94
+ for (const vm of (_a = descriptor.viewModels) != null ? _a : []) {
95
+ const instance = container.resolve(vm.token);
96
+ if (typeof ((_b = instance.state) == null ? void 0 : _b.get) === "function") {
97
+ state[vm.token.description] = instance.state.get();
98
+ }
99
+ }
100
+ return state;
101
+ }
102
+ function collectDocumentContribution(container, descriptor) {
103
+ var _a, _b;
104
+ const contribution = {};
105
+ for (const vm of (_a = descriptor.viewModels) != null ? _a : []) {
106
+ const instance = container.resolve(vm.token);
107
+ if (!isDocumentSource(instance)) {
108
+ continue;
109
+ }
110
+ const part = instance.describeDocument();
111
+ if (part.head) {
112
+ contribution.head = _chunkT5QVCXVBcjs.__spreadValues.call(void 0, _chunkT5QVCXVBcjs.__spreadValues.call(void 0, {}, contribution.head), part.head);
113
+ }
114
+ const declared = part.status !== void 0 || part.redirect !== void 0;
115
+ if (declared && contribution.status === void 0) {
116
+ contribution.status = (_b = part.status) != null ? _b : REDIRECT_FOUND;
117
+ contribution.redirect = part.redirect;
118
+ }
119
+ }
120
+ return contribution;
121
+ }
122
+ function resolveEagerUnits(container, descriptor) {
123
+ var _a;
124
+ for (const token of (_a = descriptor.eager) != null ? _a : []) {
125
+ const instance = container.resolve(token);
126
+ _chunkA6ZPAM6Zcjs.unitLogger.call(void 0, container, token.description).debug("eager: \u0435\u0434\u0438\u043D\u0438\u0446\u0430 \u043F\u043E\u0434\u043D\u044F\u0442\u0430 \u043F\u0440\u0438 \u043C\u043E\u043D\u0442\u0438\u0440\u043E\u0432\u0430\u043D\u0438\u0438");
127
+ if (instance instanceof Promise) {
128
+ instance.catch(
129
+ (error) => _chunkA6ZPAM6Zcjs.unitLogger.call(void 0, container, token.description).error("eager: \u0435\u0434\u0438\u043D\u0438\u0446\u0430 \u043D\u0435 \u043F\u043E\u0434\u043D\u044F\u043B\u0430\u0441\u044C", error)
130
+ );
131
+ }
132
+ }
133
+ }
134
+
135
+ // src/island/render.ts
136
+ var STATUS_OK = 200;
137
+ var processSetup = /* @__PURE__ */ new WeakMap();
138
+ function setupOnce(descriptor) {
139
+ const { setup } = descriptor;
140
+ if (!setup) {
141
+ return Promise.resolve();
142
+ }
143
+ let started = processSetup.get(descriptor);
144
+ if (!started) {
145
+ started = Promise.resolve(setup());
146
+ processSetup.set(descriptor, started);
147
+ }
148
+ return started;
149
+ }
150
+ function renderApp(descriptor, request) {
151
+ return _chunkT5QVCXVBcjs.__async.call(void 0, this, null, function* () {
152
+ var _a, _b;
153
+ yield setupOnce(descriptor);
154
+ const route = {
155
+ params: (_a = request.params) != null ? _a : {},
156
+ pathname: request.pathname,
157
+ search: (_b = request.search) != null ? _b : ""
158
+ };
159
+ const container = createIslandContainer(descriptor, { parent: request.parent });
160
+ if (container === null) {
161
+ throw new Error(`den/island: \u043F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u0435 \xAB${descriptor.name}\xBB \u043D\u0435 \u0441\u043E\u0431\u0440\u0430\u043B\u043E\u0441\u044C \u2014 \u0440\u0435\u043D\u0434\u0435\u0440 \u043D\u0435\u0432\u043E\u0437\u043C\u043E\u0436\u0435\u043D`);
162
+ }
163
+ try {
164
+ if (request.context) {
165
+ container.bind(_chunk6QV4L6R2cjs.RequestContextToken).toValue(request.context);
166
+ }
167
+ yield activateIslandViewModels(container, descriptor, route);
168
+ const { head, redirect, status = STATUS_OK } = collectDocumentContribution(container, descriptor);
169
+ if (status !== STATUS_OK) {
170
+ return { head, html: "", redirect, state: {}, status };
171
+ }
172
+ const state = collectIslandState(container, descriptor);
173
+ const rendered = _chunkXOJ2VWX4cjs.resolveViewAdapter.call(void 0, container).renderIsland(descriptor, {
174
+ container,
175
+ slots: request.slots
176
+ });
177
+ return { assets: descriptor.assets, head, headHtml: rendered.head, html: rendered.html, state, status };
178
+ } finally {
179
+ yield container.dispose();
180
+ }
181
+ });
182
+ }
183
+
184
+ // src/island/routes.ts
185
+ function normalizeRoutes(routes = []) {
186
+ return routes.map((route) => typeof route === "string" ? { source: route } : route);
187
+ }
188
+ function matchRoute(routes = [], pathname) {
189
+ for (const route of normalizeRoutes(routes)) {
190
+ const found = new RegExp(route.source).exec(pathname);
191
+ if (found !== null) {
192
+ return { params: decodeParams(found.groups), route };
193
+ }
194
+ }
195
+ return null;
196
+ }
197
+ function decodeParams(groups) {
198
+ const params = {};
199
+ for (const [name, value] of Object.entries(groups != null ? groups : {})) {
200
+ if (value === void 0) {
201
+ continue;
202
+ }
203
+ try {
204
+ params[name] = decodeURIComponent(value);
205
+ } catch (e) {
206
+ params[name] = value;
207
+ }
208
+ }
209
+ return params;
210
+ }
211
+ var REGEXP_SPECIALS = /[.*+?^${}()|[\]\\]/g;
212
+ var PARAM_SEGMENT = /\/:([A-Za-z_$][\w$]*)/g;
213
+ function pathPattern(pattern) {
214
+ return `^${pattern.replace(REGEXP_SPECIALS, "\\$&").replace(PARAM_SEGMENT, "/(?<$1>[^/]+)")}$`;
215
+ }
216
+
217
+
218
+
219
+
220
+
221
+
222
+
223
+
224
+
225
+
226
+
227
+
228
+
229
+
230
+ exports.isDocumentSource = isDocumentSource; exports.getAppContainer = getAppContainer; exports.createIslandContainer = createIslandContainer; exports.hydrateIslandState = hydrateIslandState; exports.activateIslandViewModels = activateIslandViewModels; exports.collectIslandState = collectIslandState; exports.collectDocumentContribution = collectDocumentContribution; exports.resolveEagerUnits = resolveEagerUnits; exports.renderApp = renderApp; exports.normalizeRoutes = normalizeRoutes; exports.matchRoute = matchRoute; exports.pathPattern = pathPattern;
@@ -1,4 +1,4 @@
1
- import { v as DefineServiceOptions, H as ServiceDefinition, w as DefineViewModelOptions, V as ViewModelDefinition, u as DefineRepositoryOptions, B as RepositoryDefinition } from './types-COwVwgzE.cjs';
1
+ import { a as DefineServiceOptions, S as ServiceDefinition, b as DefineViewModelOptions, V as ViewModelDefinition, D as DefineRepositoryOptions, d as RepositoryDefinition } from './types-Dx5qGiwk.cjs';
2
2
  import { Token } from '@foxford/ioc';
3
3
 
4
4
  /**
@@ -1,4 +1,4 @@
1
- import { v as DefineServiceOptions, H as ServiceDefinition, w as DefineViewModelOptions, V as ViewModelDefinition, u as DefineRepositoryOptions, B as RepositoryDefinition } from './types-COwVwgzE.js';
1
+ import { a as DefineServiceOptions, S as ServiceDefinition, b as DefineViewModelOptions, V as ViewModelDefinition, D as DefineRepositoryOptions, d as RepositoryDefinition } from './types-Dx5qGiwk.js';
2
2
  import { Token } from '@foxford/ioc';
3
3
 
4
4
  /**
@@ -1,4 +1,4 @@
1
- import { x as Definition, q as LayerDefinition } from './types-COwVwgzE.js';
1
+ import { c as Definition, L as LayerDefinition } from './types-Dx5qGiwk.js';
2
2
  import { Container } from '@foxford/ioc';
3
3
 
4
4
  /**
@@ -1,4 +1,4 @@
1
- import { x as Definition, q as LayerDefinition } from './types-COwVwgzE.cjs';
1
+ import { c as Definition, L as LayerDefinition } from './types-Dx5qGiwk.cjs';
2
2
  import { Container } from '@foxford/ioc';
3
3
 
4
4
  /**
package/define.cjs CHANGED
@@ -4,15 +4,17 @@
4
4
 
5
5
 
6
6
 
7
-
8
- var _chunk5DJZZML5cjs = require('./chunk-5DJZZML5.cjs');
7
+ var _chunkZ5BL4DJAcjs = require('./chunk-Z5BL4DJA.cjs');
9
8
  require('./chunk-C7BM4DGX.cjs');
10
9
 
11
10
 
11
+ var _chunk6QV4L6R2cjs = require('./chunk-6QV4L6R2.cjs');
12
+
13
+
12
14
 
13
15
 
14
16
  var _chunkA6ZPAM6Zcjs = require('./chunk-A6ZPAM6Z.cjs');
15
- require('./chunk-XHYR3SGG.cjs');
17
+ require('./chunk-T5QVCXVB.cjs');
16
18
 
17
19
 
18
20
 
@@ -23,4 +25,4 @@ require('./chunk-XHYR3SGG.cjs');
23
25
 
24
26
 
25
27
 
26
- exports.LoggerToken = _chunkA6ZPAM6Zcjs.LoggerToken; exports.SlotRegistry = _chunk5DJZZML5cjs.SlotRegistry; exports.SlotRegistryToken = _chunk5DJZZML5cjs.SlotRegistryToken; exports.defineRepository = _chunk5DJZZML5cjs.defineRepository; exports.defineService = _chunk5DJZZML5cjs.defineService; exports.defineSlot = _chunk5DJZZML5cjs.defineSlot; exports.defineViewModel = _chunk5DJZZML5cjs.defineViewModel; exports.logger = _chunkA6ZPAM6Zcjs.logger; exports.scopeLogger = _chunkA6ZPAM6Zcjs.scopeLogger;
28
+ exports.LoggerToken = _chunkA6ZPAM6Zcjs.LoggerToken; exports.SlotRegistry = _chunkZ5BL4DJAcjs.SlotRegistry; exports.SlotRegistryToken = _chunk6QV4L6R2cjs.SlotRegistryToken; exports.defineRepository = _chunkZ5BL4DJAcjs.defineRepository; exports.defineService = _chunkZ5BL4DJAcjs.defineService; exports.defineSlot = _chunkZ5BL4DJAcjs.defineSlot; exports.defineViewModel = _chunkZ5BL4DJAcjs.defineViewModel; exports.logger = _chunkA6ZPAM6Zcjs.logger; exports.scopeLogger = _chunkA6ZPAM6Zcjs.scopeLogger;
package/define.d.cts CHANGED
@@ -1,6 +1,6 @@
1
- export { d as defineRepository, a as defineService, b as defineViewModel } from './define-repository-MDEWHqM7.cjs';
2
- export { D as DefineSlotOptions, S as SlotDefinition, d as defineSlot } from './define-slot-BKvArA02.cjs';
3
- export { u as DefineRepositoryOptions, v as DefineServiceOptions, w as DefineViewModelOptions, x as Definition, B as RepositoryDefinition, H as ServiceDefinition, I as SlotEntry, J as SlotRegistry, K as SlotRegistryToken, V as ViewModelDefinition } from './types-COwVwgzE.cjs';
1
+ export { d as defineRepository, a as defineService, b as defineViewModel } from './define-repository-CwAXed2X.cjs';
2
+ export { D as DefineSlotOptions, S as SlotDefinition, d as defineSlot } from './define-slot-DYNCLDJY.cjs';
3
+ export { D as DefineRepositoryOptions, a as DefineServiceOptions, b as DefineViewModelOptions, c as Definition, d as RepositoryDefinition, S as ServiceDefinition, e as SlotEntry, f as SlotRegistry, g as SlotRegistryToken, V as ViewModelDefinition } from './types-Dx5qGiwk.cjs';
4
4
  import { Token, Container } from '@foxford/ioc';
5
5
  import { HierarchicalLogger } from '@foxford/logger';
6
6
 
package/define.d.ts CHANGED
@@ -1,6 +1,6 @@
1
- export { d as defineRepository, a as defineService, b as defineViewModel } from './define-repository-Ca62_YCy.js';
2
- export { D as DefineSlotOptions, S as SlotDefinition, d as defineSlot } from './define-slot-Dil8Kr1A.js';
3
- export { u as DefineRepositoryOptions, v as DefineServiceOptions, w as DefineViewModelOptions, x as Definition, B as RepositoryDefinition, H as ServiceDefinition, I as SlotEntry, J as SlotRegistry, K as SlotRegistryToken, V as ViewModelDefinition } from './types-COwVwgzE.js';
1
+ export { d as defineRepository, a as defineService, b as defineViewModel } from './define-repository-q_T4hqeP.js';
2
+ export { D as DefineSlotOptions, S as SlotDefinition, d as defineSlot } from './define-slot-Cngzae6i.js';
3
+ export { D as DefineRepositoryOptions, a as DefineServiceOptions, b as DefineViewModelOptions, c as Definition, d as RepositoryDefinition, S as ServiceDefinition, e as SlotEntry, f as SlotRegistry, g as SlotRegistryToken, V as ViewModelDefinition } from './types-Dx5qGiwk.js';
4
4
  import { Token, Container } from '@foxford/ioc';
5
5
  import { HierarchicalLogger } from '@foxford/logger';
6
6
 
package/define.js CHANGED
@@ -1,18 +1,20 @@
1
1
  import {
2
2
  SlotRegistry,
3
- SlotRegistryToken,
4
3
  defineRepository,
5
4
  defineService,
6
5
  defineSlot,
7
6
  defineViewModel
8
- } from "./chunk-WRQC6BVJ.js";
7
+ } from "./chunk-MCN4GFEQ.js";
9
8
  import "./chunk-WBHHHICS.js";
9
+ import {
10
+ SlotRegistryToken
11
+ } from "./chunk-3RX2OSUL.js";
10
12
  import {
11
13
  LoggerToken,
12
14
  logger,
13
15
  scopeLogger
14
16
  } from "./chunk-TPBI6TOU.js";
15
- import "./chunk-HJO26HIQ.js";
17
+ import "./chunk-6FNC3XMI.js";
16
18
  export {
17
19
  LoggerToken,
18
20
  SlotRegistry,
package/den.js ADDED
@@ -0,0 +1,60 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { spawn } from 'node:child_process'
4
+ import { existsSync } from 'node:fs'
5
+ import { createRequire } from 'node:module'
6
+ import path from 'node:path'
7
+ import { fileURLToPath } from 'node:url'
8
+
9
+ // Запускатель команды: выбирает, что исполнять, и больше ничего не делает.
10
+ //
11
+ // Сама команда лежит в `src/bin/den.ts` и проходит гейты пакета — разбор аргументов это код,
12
+ // и типизироваться он обязан. Здесь только развилка, и нужна она потому, что пакет живёт в двух
13
+ // видах: в воркспейсе `exports` ведут на `src/*.ts`, и команде нужен TS-рантайм; в артефакте
14
+ // рядом лежит собранный модуль.
15
+ //
16
+ // Развилка по НАЛИЧИЮ исходника, а не по переменной окружения: спрашивать у человека, в каком
17
+ // виде стоит пакет, незачем — это видно.
18
+ //
19
+ // Шебанг один на оба вида (`node`), TS-рантайм подключается аргументом. Поэтому переписывать
20
+ // шебанг на сборке не нужно: файл уезжает в артефакт как есть.
21
+ //
22
+ // Сигналы пробрасываются вниз, и запуск асинхронный именно ради этого: `den dev` держит поднятым
23
+ // дочерний процесс приложения, а синхронный запуск обработчиков не пускает — сигнал убил бы
24
+ // запускатель, оставив команду и её процесс висеть на порту. В терминале это незаметно (Ctrl+C
25
+ // идёт всей группе), а супервизор шлёт сигнал по одному PID.
26
+
27
+ const dir = path.dirname(fileURLToPath(import.meta.url))
28
+ const source = path.join(dir, '..', 'src', 'bin', 'den.ts')
29
+
30
+ const target = existsSync(source)
31
+ ? ['--import', createRequire(import.meta.url).resolve('tsx'), source]
32
+ : [path.join(dir, 'bin', 'den.js')]
33
+
34
+ // `detached` — не для отвязки, а ради ГРУППЫ процессов: ребёнок становится её лидером, и сигнал
35
+ // уходит всей группе, включая внуков. Убивать одного прямого ребёнка недостаточно — между нами
36
+ // и командой может встать ещё один процесс (TS-рантайм), и тогда внук переживает сигнал,
37
+ // продолжая держать наблюдателей файловой системы.
38
+ const child = spawn(process.execPath, [...target, ...process.argv.slice(2)], {
39
+ detached: true,
40
+ stdio: 'inherit',
41
+ })
42
+
43
+ function killGroup(signal) {
44
+ try {
45
+ process.kill(-child.pid, signal)
46
+ } catch {
47
+ // Группы уже нет — значит и убивать некого
48
+ }
49
+ }
50
+
51
+ for (const signal of ['SIGINT', 'SIGTERM']) {
52
+ process.on(signal, () => killGroup(signal))
53
+ }
54
+
55
+ // Последняя сетка: выход не по сигналу тоже не должен оставлять группу висеть
56
+ process.on('exit', () => killGroup('SIGTERM'))
57
+
58
+ child.on('exit', (code, signal) => {
59
+ process.exit(signal === null ? code ?? 1 : 1)
60
+ })
@@ -0,0 +1,98 @@
1
+ import { S as SlotDefinition } from './define-slot-Cngzae6i.js';
2
+ import { L as LayerDefinition, c as Definition } from './types-Dx5qGiwk.js';
3
+ import { Token } from '@foxford/ioc';
4
+
5
+ /**
6
+ * Дескриптор острова — что ядру нужно, чтобы остров прожил свой цикл.
7
+ *
8
+ * `view` типизирован как `unknown` по той же причине, что и в `defineSlot`: остров не
9
+ * прибит к React. Ядро собирает контейнер, гидрирует VM и ведёт учёт, ни разу не заглянув
10
+ * во view; красит его view-адаптер (`ViewAdapter.createIsland`), и он же знает, дерево
11
+ * какого фреймворка перед ним.
12
+ *
13
+ * Дескрипторы страниц у хостов (`PageDescriptor` в `@foxford/den-vike` и `@foxford/den-next`)
14
+ * шире: у каждого своя часть про оркестрацию — `load`, `routes`, `provides`/`requires`,
15
+ * вклад в документ. Сводить их в один тип нечем, оркестраторы у хостов разные. Общее
16
+ * ровно это, и структурная типизация делает остальное: дескриптор хоста подходит сюда как есть.
17
+ */
18
+ interface IslandDescriptor {
19
+ /**
20
+ * Имя мини-приложения (`market-app`).
21
+ *
22
+ * Работает дважды: корень именованного иерархического логгера (ядро биндит по нему
23
+ * scope-логгер в контейнер острова, поэтому регистрация единиц логируется под именем
24
+ * приложения) и пространство имён для адресов его слотов (см. `slotAddress`). Из-за
25
+ * второго оно обязательное: без него у мест приложения не было бы адреса.
26
+ */
27
+ name: string;
28
+ /** Непрозрачная ссылка на view острова (компонент view-фреймворка). */
29
+ view: unknown;
30
+ /**
31
+ * View-слой — чем остров рисовать: `defineLayer(ViewLayerToken, { adapter: reactAdapter() })`.
32
+ *
33
+ * Обязателен, и дефолта нет ни у ядра, ни у хоста: фреймворк за приложение не выбирают.
34
+ * Слой регистрируется в контейнере острова, поэтому слоты внутри по умолчанию рисуются
35
+ * им же; слот перекрывает выбор своим `viewLayer`.
36
+ */
37
+ viewLayer: LayerDefinition;
38
+ /**
39
+ * Data/Business единицы (repository/service). Регистрируются в контейнере острова.
40
+ * Не должны импортировать VM.
41
+ */
42
+ units: ReadonlyArray<Definition<unknown>>;
43
+ /**
44
+ * VM-единицы: регистрируются только в острове — оркестратор их не читает, состояние
45
+ * ездит через denState.
46
+ */
47
+ viewModels?: ReadonlyArray<Definition<unknown>>;
48
+ /**
49
+ * Единицы, которые обязаны жить с загрузки, а не с первого резолва.
50
+ *
51
+ * Регистрация ленива по природе: не спросили — не создали. Обычно это то, что нужно, но
52
+ * сервису, который сам на что-то подписывается или греет кэш, ждать первого потребителя
53
+ * незачем. Токены — из тех же `units`.
54
+ *
55
+ * Поднимаются при МОНТИРОВАНИИ острова, а не при сборке контейнера: контейнер строится и на
56
+ * сервере, а сервер живёт один рендер — открытая там WS-подписка это не «eager», а утечка.
57
+ * Серверное состояние приложения считает активация VM.
58
+ */
59
+ eager?: ReadonlyArray<Token<unknown>>;
60
+ /**
61
+ * Места расширения приложения: ключ — локальное имя слота, значение — `defineSlot`.
62
+ *
63
+ * Карта, а не массив, потому что имя даёт хозяин места: адрес получается `app:key`
64
+ * (см. `slotAddress`), занять чужое пространство имён нечем, а руками писать префикс
65
+ * не нужно. Единицы слотов регистрируются в контейнере острова вместе с его собственными.
66
+ */
67
+ slots?: Readonly<Record<string, SlotDefinition>>;
68
+ /**
69
+ * Настройка процесса перед первым рендером: разбор разметки, полифиллы, всё, что ставится
70
+ * один раз и нужно раньше приложения.
71
+ *
72
+ * В исходниках приложения этого поля нет — оно объявляется конфигом и вплавляется сборкой
73
+ * в СЕРВЕРНЫЙ выход, поэтому дескриптор остаётся декларацией. Гарантию даёт движок
74
+ * ({@link renderApp} зовёт настройку один раз на процесс и дожидается её), а не приложение:
75
+ * ждать её в активации VM пришлось бы каждому, а у приложения в процессе хоста и вовсе
76
+ * нет обвязки, которая могла бы дождаться.
77
+ */
78
+ setup?: () => void | Promise<void>;
79
+ /**
80
+ * Ссылки на клиентские артефакты приложения — их подключает к документу хост.
81
+ *
82
+ * Тоже вплавляются сборкой: адреса известны только ей. Байты приложение не отдаёт ни в
83
+ * одном транспорте — за доставку отвечает CDN.
84
+ */
85
+ assets?: AppAssets;
86
+ }
87
+ /**
88
+ * Клиентские артефакты приложения: только initial-чанки, в порядке подключения.
89
+ *
90
+ * Ленивые чанки сюда не попадают — их браузер дотянет сам по мере надобности, а хосту
91
+ * нужно ровно то, без чего страница не оживёт.
92
+ */
93
+ interface AppAssets {
94
+ css: string[];
95
+ js: string[];
96
+ }
97
+
98
+ export type { AppAssets as A, IslandDescriptor as I };
@@ -0,0 +1,98 @@
1
+ import { S as SlotDefinition } from './define-slot-DYNCLDJY.cjs';
2
+ import { L as LayerDefinition, c as Definition } from './types-Dx5qGiwk.cjs';
3
+ import { Token } from '@foxford/ioc';
4
+
5
+ /**
6
+ * Дескриптор острова — что ядру нужно, чтобы остров прожил свой цикл.
7
+ *
8
+ * `view` типизирован как `unknown` по той же причине, что и в `defineSlot`: остров не
9
+ * прибит к React. Ядро собирает контейнер, гидрирует VM и ведёт учёт, ни разу не заглянув
10
+ * во view; красит его view-адаптер (`ViewAdapter.createIsland`), и он же знает, дерево
11
+ * какого фреймворка перед ним.
12
+ *
13
+ * Дескрипторы страниц у хостов (`PageDescriptor` в `@foxford/den-vike` и `@foxford/den-next`)
14
+ * шире: у каждого своя часть про оркестрацию — `load`, `routes`, `provides`/`requires`,
15
+ * вклад в документ. Сводить их в один тип нечем, оркестраторы у хостов разные. Общее
16
+ * ровно это, и структурная типизация делает остальное: дескриптор хоста подходит сюда как есть.
17
+ */
18
+ interface IslandDescriptor {
19
+ /**
20
+ * Имя мини-приложения (`market-app`).
21
+ *
22
+ * Работает дважды: корень именованного иерархического логгера (ядро биндит по нему
23
+ * scope-логгер в контейнер острова, поэтому регистрация единиц логируется под именем
24
+ * приложения) и пространство имён для адресов его слотов (см. `slotAddress`). Из-за
25
+ * второго оно обязательное: без него у мест приложения не было бы адреса.
26
+ */
27
+ name: string;
28
+ /** Непрозрачная ссылка на view острова (компонент view-фреймворка). */
29
+ view: unknown;
30
+ /**
31
+ * View-слой — чем остров рисовать: `defineLayer(ViewLayerToken, { adapter: reactAdapter() })`.
32
+ *
33
+ * Обязателен, и дефолта нет ни у ядра, ни у хоста: фреймворк за приложение не выбирают.
34
+ * Слой регистрируется в контейнере острова, поэтому слоты внутри по умолчанию рисуются
35
+ * им же; слот перекрывает выбор своим `viewLayer`.
36
+ */
37
+ viewLayer: LayerDefinition;
38
+ /**
39
+ * Data/Business единицы (repository/service). Регистрируются в контейнере острова.
40
+ * Не должны импортировать VM.
41
+ */
42
+ units: ReadonlyArray<Definition<unknown>>;
43
+ /**
44
+ * VM-единицы: регистрируются только в острове — оркестратор их не читает, состояние
45
+ * ездит через denState.
46
+ */
47
+ viewModels?: ReadonlyArray<Definition<unknown>>;
48
+ /**
49
+ * Единицы, которые обязаны жить с загрузки, а не с первого резолва.
50
+ *
51
+ * Регистрация ленива по природе: не спросили — не создали. Обычно это то, что нужно, но
52
+ * сервису, который сам на что-то подписывается или греет кэш, ждать первого потребителя
53
+ * незачем. Токены — из тех же `units`.
54
+ *
55
+ * Поднимаются при МОНТИРОВАНИИ острова, а не при сборке контейнера: контейнер строится и на
56
+ * сервере, а сервер живёт один рендер — открытая там WS-подписка это не «eager», а утечка.
57
+ * Серверное состояние приложения считает активация VM.
58
+ */
59
+ eager?: ReadonlyArray<Token<unknown>>;
60
+ /**
61
+ * Места расширения приложения: ключ — локальное имя слота, значение — `defineSlot`.
62
+ *
63
+ * Карта, а не массив, потому что имя даёт хозяин места: адрес получается `app:key`
64
+ * (см. `slotAddress`), занять чужое пространство имён нечем, а руками писать префикс
65
+ * не нужно. Единицы слотов регистрируются в контейнере острова вместе с его собственными.
66
+ */
67
+ slots?: Readonly<Record<string, SlotDefinition>>;
68
+ /**
69
+ * Настройка процесса перед первым рендером: разбор разметки, полифиллы, всё, что ставится
70
+ * один раз и нужно раньше приложения.
71
+ *
72
+ * В исходниках приложения этого поля нет — оно объявляется конфигом и вплавляется сборкой
73
+ * в СЕРВЕРНЫЙ выход, поэтому дескриптор остаётся декларацией. Гарантию даёт движок
74
+ * ({@link renderApp} зовёт настройку один раз на процесс и дожидается её), а не приложение:
75
+ * ждать её в активации VM пришлось бы каждому, а у приложения в процессе хоста и вовсе
76
+ * нет обвязки, которая могла бы дождаться.
77
+ */
78
+ setup?: () => void | Promise<void>;
79
+ /**
80
+ * Ссылки на клиентские артефакты приложения — их подключает к документу хост.
81
+ *
82
+ * Тоже вплавляются сборкой: адреса известны только ей. Байты приложение не отдаёт ни в
83
+ * одном транспорте — за доставку отвечает CDN.
84
+ */
85
+ assets?: AppAssets;
86
+ }
87
+ /**
88
+ * Клиентские артефакты приложения: только initial-чанки, в порядке подключения.
89
+ *
90
+ * Ленивые чанки сюда не попадают — их браузер дотянет сам по мере надобности, а хосту
91
+ * нужно ровно то, без чего страница не оживёт.
92
+ */
93
+ interface AppAssets {
94
+ css: string[];
95
+ js: string[];
96
+ }
97
+
98
+ export type { AppAssets as A, IslandDescriptor as I };