@agimon-ai/po 0.0.0-alpha.20 → 0.0.0-alpha.21
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/dist/catalog/index.json +55 -51
- package/dist/src/builders/web/bundle.cjs.map +1 -1
- package/dist/src/builders/web/bundle.d.mts +3 -3
- package/dist/src/builders/web/bundle.mjs.map +1 -1
- package/dist/src/builders/web/index.cjs +1 -1
- package/dist/src/builders/web/index.cjs.map +1 -1
- package/dist/src/builders/web/index.mjs +1 -1
- package/dist/src/builders/web/index.mjs.map +1 -1
- package/dist/src/builders/web/webPluginGenerate.cjs +4 -15
- package/dist/src/builders/web/webPluginGenerate.cjs.map +1 -1
- package/dist/src/builders/web/webPluginGenerate.d.mts +5 -9
- package/dist/src/builders/web/webPluginGenerate.d.mts.map +1 -1
- package/dist/src/builders/web/webPluginGenerate.mjs +4 -15
- package/dist/src/builders/web/webPluginGenerate.mjs.map +1 -1
- package/package.json +20 -20
package/dist/catalog/index.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"version": 1,
|
|
3
3
|
"packages": {
|
|
4
4
|
"@agimon-ai/po": {
|
|
5
|
-
"version": "0.0.0-alpha.
|
|
5
|
+
"version": "0.0.0-alpha.21",
|
|
6
6
|
"dependencies": [
|
|
7
7
|
"@agimon-ai/po-artifact",
|
|
8
8
|
"@agimon-ai/po-autostop",
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
]
|
|
24
24
|
},
|
|
25
25
|
"@agimon-ai/po-artifact": {
|
|
26
|
-
"version": "0.0.0-alpha.
|
|
26
|
+
"version": "0.0.0-alpha.19",
|
|
27
27
|
"dependencies": [
|
|
28
28
|
"@agimon-ai/po-core",
|
|
29
29
|
"@agimon-ai/po-web-components",
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
]
|
|
32
32
|
},
|
|
33
33
|
"@agimon-ai/po-author": {
|
|
34
|
-
"version": "0.0.0-alpha.
|
|
34
|
+
"version": "0.0.0-alpha.20",
|
|
35
35
|
"dependencies": [
|
|
36
36
|
"@agimon-ai/po-artifact",
|
|
37
37
|
"@agimon-ai/po-core",
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
]
|
|
42
42
|
},
|
|
43
43
|
"@agimon-ai/po-autocompact": {
|
|
44
|
-
"version": "0.0.0-alpha.
|
|
44
|
+
"version": "0.0.0-alpha.20",
|
|
45
45
|
"dependencies": [
|
|
46
46
|
"@agimon-ai/po-artifact",
|
|
47
47
|
"@agimon-ai/po-config",
|
|
@@ -50,33 +50,37 @@
|
|
|
50
50
|
]
|
|
51
51
|
},
|
|
52
52
|
"@agimon-ai/po-autostop": {
|
|
53
|
-
"version": "0.0.0-alpha.
|
|
53
|
+
"version": "0.0.0-alpha.20",
|
|
54
54
|
"dependencies": [
|
|
55
55
|
"@agimon-ai/po-core",
|
|
56
56
|
"@agimon-ai/po-telemetry"
|
|
57
57
|
]
|
|
58
58
|
},
|
|
59
59
|
"@agimon-ai/po-browse": {
|
|
60
|
-
"version": "0.0.1-alpha.
|
|
60
|
+
"version": "0.0.1-alpha.18",
|
|
61
61
|
"dependencies": [
|
|
62
62
|
"@agimon-ai/po-core",
|
|
63
63
|
"@agimon-ai/po-web-components"
|
|
64
64
|
]
|
|
65
65
|
},
|
|
66
66
|
"@agimon-ai/po-build": {
|
|
67
|
-
"version": "0.0.0-alpha.
|
|
67
|
+
"version": "0.0.0-alpha.20",
|
|
68
68
|
"dependencies": []
|
|
69
69
|
},
|
|
70
70
|
"@agimon-ai/po-cache": {
|
|
71
|
-
"version": "0.0.0-alpha.
|
|
71
|
+
"version": "0.0.0-alpha.20",
|
|
72
72
|
"dependencies": [
|
|
73
73
|
"@agimon-ai/po-config",
|
|
74
74
|
"@agimon-ai/po-core",
|
|
75
75
|
"@agimon-ai/po-minor-mode"
|
|
76
76
|
]
|
|
77
77
|
},
|
|
78
|
+
"@agimon-ai/po-claude-bridge": {
|
|
79
|
+
"version": "0.1.1-alpha.0",
|
|
80
|
+
"dependencies": []
|
|
81
|
+
},
|
|
78
82
|
"@agimon-ai/po-computer-use": {
|
|
79
|
-
"version": "0.0.0-alpha.
|
|
83
|
+
"version": "0.0.0-alpha.20",
|
|
80
84
|
"dependencies": [
|
|
81
85
|
"@agimon-ai/po-config",
|
|
82
86
|
"@agimon-ai/po-core",
|
|
@@ -86,7 +90,7 @@
|
|
|
86
90
|
]
|
|
87
91
|
},
|
|
88
92
|
"@agimon-ai/po-config": {
|
|
89
|
-
"version": "0.0.0-alpha.
|
|
93
|
+
"version": "0.0.0-alpha.20",
|
|
90
94
|
"dependencies": [
|
|
91
95
|
"@agimon-ai/po-core",
|
|
92
96
|
"@agimon-ai/po-telemetry",
|
|
@@ -94,14 +98,14 @@
|
|
|
94
98
|
]
|
|
95
99
|
},
|
|
96
100
|
"@agimon-ai/po-core": {
|
|
97
|
-
"version": "0.0.0-alpha.
|
|
101
|
+
"version": "0.0.0-alpha.20",
|
|
98
102
|
"dependencies": [
|
|
99
103
|
"@agimon-ai/po-telemetry",
|
|
100
104
|
"@agimon-ai/po-web-security"
|
|
101
105
|
]
|
|
102
106
|
},
|
|
103
107
|
"@agimon-ai/po-domain": {
|
|
104
|
-
"version": "0.0.0-alpha.
|
|
108
|
+
"version": "0.0.0-alpha.20",
|
|
105
109
|
"dependencies": [
|
|
106
110
|
"@agimon-ai/po-config",
|
|
107
111
|
"@agimon-ai/po-core",
|
|
@@ -110,7 +114,7 @@
|
|
|
110
114
|
]
|
|
111
115
|
},
|
|
112
116
|
"@agimon-ai/po-edit": {
|
|
113
|
-
"version": "0.0.0-alpha.
|
|
117
|
+
"version": "0.0.0-alpha.20",
|
|
114
118
|
"dependencies": [
|
|
115
119
|
"@agimon-ai/po-core",
|
|
116
120
|
"@agimon-ai/po-hashline",
|
|
@@ -119,7 +123,7 @@
|
|
|
119
123
|
]
|
|
120
124
|
},
|
|
121
125
|
"@agimon-ai/po-file-edit": {
|
|
122
|
-
"version": "0.0.0-alpha.
|
|
126
|
+
"version": "0.0.0-alpha.20",
|
|
123
127
|
"dependencies": [
|
|
124
128
|
"@agimon-ai/po-config",
|
|
125
129
|
"@agimon-ai/po-core",
|
|
@@ -130,7 +134,7 @@
|
|
|
130
134
|
]
|
|
131
135
|
},
|
|
132
136
|
"@agimon-ai/po-git": {
|
|
133
|
-
"version": "0.0.0-alpha.
|
|
137
|
+
"version": "0.0.0-alpha.20",
|
|
134
138
|
"dependencies": [
|
|
135
139
|
"@agimon-ai/po-core",
|
|
136
140
|
"@agimon-ai/po-session",
|
|
@@ -139,7 +143,7 @@
|
|
|
139
143
|
]
|
|
140
144
|
},
|
|
141
145
|
"@agimon-ai/po-goal": {
|
|
142
|
-
"version": "0.0.0-alpha.
|
|
146
|
+
"version": "0.0.0-alpha.20",
|
|
143
147
|
"dependencies": [
|
|
144
148
|
"@agimon-ai/po-core",
|
|
145
149
|
"@agimon-ai/po-minor-mode",
|
|
@@ -148,7 +152,7 @@
|
|
|
148
152
|
]
|
|
149
153
|
},
|
|
150
154
|
"@agimon-ai/po-grep": {
|
|
151
|
-
"version": "0.0.0-alpha.
|
|
155
|
+
"version": "0.0.0-alpha.20",
|
|
152
156
|
"dependencies": [
|
|
153
157
|
"@agimon-ai/po-core",
|
|
154
158
|
"@agimon-ai/po-hashline",
|
|
@@ -157,11 +161,11 @@
|
|
|
157
161
|
]
|
|
158
162
|
},
|
|
159
163
|
"@agimon-ai/po-hashline": {
|
|
160
|
-
"version": "0.0.0-alpha.
|
|
164
|
+
"version": "0.0.0-alpha.20",
|
|
161
165
|
"dependencies": []
|
|
162
166
|
},
|
|
163
167
|
"@agimon-ai/po-help": {
|
|
164
|
-
"version": "0.0.0-alpha.
|
|
168
|
+
"version": "0.0.0-alpha.20",
|
|
165
169
|
"dependencies": [
|
|
166
170
|
"@agimon-ai/po-core",
|
|
167
171
|
"@agimon-ai/po-minor-mode",
|
|
@@ -169,7 +173,7 @@
|
|
|
169
173
|
]
|
|
170
174
|
},
|
|
171
175
|
"@agimon-ai/po-hook": {
|
|
172
|
-
"version": "0.0.0-alpha.
|
|
176
|
+
"version": "0.0.0-alpha.20",
|
|
173
177
|
"dependencies": [
|
|
174
178
|
"@agimon-ai/po-config",
|
|
175
179
|
"@agimon-ai/po-core",
|
|
@@ -177,7 +181,7 @@
|
|
|
177
181
|
]
|
|
178
182
|
},
|
|
179
183
|
"@agimon-ai/po-image-gen": {
|
|
180
|
-
"version": "0.0.0-alpha.
|
|
184
|
+
"version": "0.0.0-alpha.19",
|
|
181
185
|
"dependencies": [
|
|
182
186
|
"@agimon-ai/po-core",
|
|
183
187
|
"@agimon-ai/po-web-components",
|
|
@@ -185,7 +189,7 @@
|
|
|
185
189
|
]
|
|
186
190
|
},
|
|
187
191
|
"@agimon-ai/po-log": {
|
|
188
|
-
"version": "0.0.0-alpha.
|
|
192
|
+
"version": "0.0.0-alpha.20",
|
|
189
193
|
"dependencies": [
|
|
190
194
|
"@agimon-ai/po-core",
|
|
191
195
|
"@agimon-ai/po-telemetry",
|
|
@@ -194,7 +198,7 @@
|
|
|
194
198
|
]
|
|
195
199
|
},
|
|
196
200
|
"@agimon-ai/po-loop": {
|
|
197
|
-
"version": "0.0.0-alpha.
|
|
201
|
+
"version": "0.0.0-alpha.20",
|
|
198
202
|
"dependencies": [
|
|
199
203
|
"@agimon-ai/po-core",
|
|
200
204
|
"@agimon-ai/po-minor-mode",
|
|
@@ -203,7 +207,7 @@
|
|
|
203
207
|
]
|
|
204
208
|
},
|
|
205
209
|
"@agimon-ai/po-major-mode": {
|
|
206
|
-
"version": "0.0.0-alpha.
|
|
210
|
+
"version": "0.0.0-alpha.20",
|
|
207
211
|
"dependencies": [
|
|
208
212
|
"@agimon-ai/po-config",
|
|
209
213
|
"@agimon-ai/po-core",
|
|
@@ -212,7 +216,7 @@
|
|
|
212
216
|
]
|
|
213
217
|
},
|
|
214
218
|
"@agimon-ai/po-mcp": {
|
|
215
|
-
"version": "0.0.0-alpha.
|
|
219
|
+
"version": "0.0.0-alpha.20",
|
|
216
220
|
"dependencies": [
|
|
217
221
|
"@agimon-ai/po-config",
|
|
218
222
|
"@agimon-ai/po-core",
|
|
@@ -221,33 +225,33 @@
|
|
|
221
225
|
]
|
|
222
226
|
},
|
|
223
227
|
"@agimon-ai/po-minor-mode": {
|
|
224
|
-
"version": "0.0.0-alpha.
|
|
228
|
+
"version": "0.0.0-alpha.20",
|
|
225
229
|
"dependencies": [
|
|
226
230
|
"@agimon-ai/po-core",
|
|
227
231
|
"@agimon-ai/po-telemetry"
|
|
228
232
|
]
|
|
229
233
|
},
|
|
230
234
|
"@agimon-ai/po-model-guidance": {
|
|
231
|
-
"version": "0.0.0-alpha.
|
|
235
|
+
"version": "0.0.0-alpha.20",
|
|
232
236
|
"dependencies": [
|
|
233
237
|
"@agimon-ai/po-config",
|
|
234
238
|
"@agimon-ai/po-core"
|
|
235
239
|
]
|
|
236
240
|
},
|
|
237
241
|
"@agimon-ai/po-native-components": {
|
|
238
|
-
"version": "0.0.0-alpha.
|
|
242
|
+
"version": "0.0.0-alpha.20",
|
|
239
243
|
"dependencies": [
|
|
240
244
|
"@agimon-ai/po-web-components"
|
|
241
245
|
]
|
|
242
246
|
},
|
|
243
247
|
"@agimon-ai/po-notification": {
|
|
244
|
-
"version": "0.0.0-alpha.
|
|
248
|
+
"version": "0.0.0-alpha.20",
|
|
245
249
|
"dependencies": [
|
|
246
250
|
"@agimon-ai/po-core"
|
|
247
251
|
]
|
|
248
252
|
},
|
|
249
253
|
"@agimon-ai/po-plan": {
|
|
250
|
-
"version": "0.0.0-alpha.
|
|
254
|
+
"version": "0.0.0-alpha.20",
|
|
251
255
|
"dependencies": [
|
|
252
256
|
"@agimon-ai/po-config",
|
|
253
257
|
"@agimon-ai/po-core",
|
|
@@ -258,7 +262,7 @@
|
|
|
258
262
|
]
|
|
259
263
|
},
|
|
260
264
|
"@agimon-ai/po-profile": {
|
|
261
|
-
"version": "0.0.0-alpha.
|
|
265
|
+
"version": "0.0.0-alpha.20",
|
|
262
266
|
"dependencies": [
|
|
263
267
|
"@agimon-ai/po-config",
|
|
264
268
|
"@agimon-ai/po-core",
|
|
@@ -267,7 +271,7 @@
|
|
|
267
271
|
]
|
|
268
272
|
},
|
|
269
273
|
"@agimon-ai/po-prompt": {
|
|
270
|
-
"version": "0.0.0-alpha.
|
|
274
|
+
"version": "0.0.0-alpha.20",
|
|
271
275
|
"dependencies": [
|
|
272
276
|
"@agimon-ai/po-core",
|
|
273
277
|
"@agimon-ai/po-web-components",
|
|
@@ -275,7 +279,7 @@
|
|
|
275
279
|
]
|
|
276
280
|
},
|
|
277
281
|
"@agimon-ai/po-read": {
|
|
278
|
-
"version": "0.0.0-alpha.
|
|
282
|
+
"version": "0.0.0-alpha.20",
|
|
279
283
|
"dependencies": [
|
|
280
284
|
"@agimon-ai/po-config",
|
|
281
285
|
"@agimon-ai/po-core",
|
|
@@ -285,7 +289,7 @@
|
|
|
285
289
|
]
|
|
286
290
|
},
|
|
287
291
|
"@agimon-ai/po-runner": {
|
|
288
|
-
"version": "0.0.0-alpha.
|
|
292
|
+
"version": "0.0.0-alpha.20",
|
|
289
293
|
"dependencies": [
|
|
290
294
|
"@agimon-ai/po-core",
|
|
291
295
|
"@agimon-ai/po-telemetry",
|
|
@@ -295,19 +299,19 @@
|
|
|
295
299
|
]
|
|
296
300
|
},
|
|
297
301
|
"@agimon-ai/po-sandbox": {
|
|
298
|
-
"version": "0.0.0-alpha.
|
|
302
|
+
"version": "0.0.0-alpha.20",
|
|
299
303
|
"dependencies": [
|
|
300
304
|
"@agimon-ai/po-core"
|
|
301
305
|
]
|
|
302
306
|
},
|
|
303
307
|
"@agimon-ai/po-session": {
|
|
304
|
-
"version": "0.0.0-alpha.
|
|
308
|
+
"version": "0.0.0-alpha.20",
|
|
305
309
|
"dependencies": [
|
|
306
310
|
"@agimon-ai/po-core"
|
|
307
311
|
]
|
|
308
312
|
},
|
|
309
313
|
"@agimon-ai/po-skill": {
|
|
310
|
-
"version": "0.0.0-alpha.
|
|
314
|
+
"version": "0.0.0-alpha.20",
|
|
311
315
|
"dependencies": [
|
|
312
316
|
"@agimon-ai/po-config",
|
|
313
317
|
"@agimon-ai/po-core",
|
|
@@ -317,7 +321,7 @@
|
|
|
317
321
|
]
|
|
318
322
|
},
|
|
319
323
|
"@agimon-ai/po-style-system": {
|
|
320
|
-
"version": "0.0.0-alpha.
|
|
324
|
+
"version": "0.0.0-alpha.20",
|
|
321
325
|
"dependencies": [
|
|
322
326
|
"@agimon-ai/po-core",
|
|
323
327
|
"@agimon-ai/po-web-components",
|
|
@@ -325,7 +329,7 @@
|
|
|
325
329
|
]
|
|
326
330
|
},
|
|
327
331
|
"@agimon-ai/po-task": {
|
|
328
|
-
"version": "0.0.0-alpha.
|
|
332
|
+
"version": "0.0.0-alpha.20",
|
|
329
333
|
"dependencies": [
|
|
330
334
|
"@agimon-ai/po-core",
|
|
331
335
|
"@agimon-ai/po-telemetry",
|
|
@@ -334,7 +338,7 @@
|
|
|
334
338
|
]
|
|
335
339
|
},
|
|
336
340
|
"@agimon-ai/po-team": {
|
|
337
|
-
"version": "0.0.0-alpha.
|
|
341
|
+
"version": "0.0.0-alpha.20",
|
|
338
342
|
"dependencies": [
|
|
339
343
|
"@agimon-ai/po-cache",
|
|
340
344
|
"@agimon-ai/po-config",
|
|
@@ -347,11 +351,11 @@
|
|
|
347
351
|
]
|
|
348
352
|
},
|
|
349
353
|
"@agimon-ai/po-telemetry": {
|
|
350
|
-
"version": "0.0.0-alpha.
|
|
354
|
+
"version": "0.0.0-alpha.20",
|
|
351
355
|
"dependencies": []
|
|
352
356
|
},
|
|
353
357
|
"@agimon-ai/po-template-advanced": {
|
|
354
|
-
"version": "0.0.0-alpha.
|
|
358
|
+
"version": "0.0.0-alpha.20",
|
|
355
359
|
"dependencies": [
|
|
356
360
|
"@agimon-ai/po-core",
|
|
357
361
|
"@agimon-ai/po-web-components",
|
|
@@ -359,7 +363,7 @@
|
|
|
359
363
|
]
|
|
360
364
|
},
|
|
361
365
|
"@agimon-ai/po-template-elegant": {
|
|
362
|
-
"version": "0.0.0-alpha.
|
|
366
|
+
"version": "0.0.0-alpha.20",
|
|
363
367
|
"dependencies": [
|
|
364
368
|
"@agimon-ai/po-core",
|
|
365
369
|
"@agimon-ai/po-web-components",
|
|
@@ -367,7 +371,7 @@
|
|
|
367
371
|
]
|
|
368
372
|
},
|
|
369
373
|
"@agimon-ai/po-ui": {
|
|
370
|
-
"version": "0.0.0-alpha.
|
|
374
|
+
"version": "0.0.0-alpha.20",
|
|
371
375
|
"dependencies": [
|
|
372
376
|
"@agimon-ai/po-core",
|
|
373
377
|
"@agimon-ai/po-hashline",
|
|
@@ -377,7 +381,7 @@
|
|
|
377
381
|
]
|
|
378
382
|
},
|
|
379
383
|
"@agimon-ai/po-user-feedback": {
|
|
380
|
-
"version": "0.0.0-alpha.
|
|
384
|
+
"version": "0.0.0-alpha.20",
|
|
381
385
|
"dependencies": [
|
|
382
386
|
"@agimon-ai/po-core",
|
|
383
387
|
"@agimon-ai/po-minor-mode",
|
|
@@ -385,7 +389,7 @@
|
|
|
385
389
|
]
|
|
386
390
|
},
|
|
387
391
|
"@agimon-ai/po-voice": {
|
|
388
|
-
"version": "0.0.0-alpha.
|
|
392
|
+
"version": "0.0.0-alpha.20",
|
|
389
393
|
"dependencies": [
|
|
390
394
|
"@agimon-ai/po-config",
|
|
391
395
|
"@agimon-ai/po-core",
|
|
@@ -398,11 +402,11 @@
|
|
|
398
402
|
]
|
|
399
403
|
},
|
|
400
404
|
"@agimon-ai/po-web-components": {
|
|
401
|
-
"version": "0.0.0-alpha.
|
|
405
|
+
"version": "0.0.0-alpha.20",
|
|
402
406
|
"dependencies": []
|
|
403
407
|
},
|
|
404
408
|
"@agimon-ai/po-web-dashboard": {
|
|
405
|
-
"version": "0.0.0-alpha.
|
|
409
|
+
"version": "0.0.0-alpha.20",
|
|
406
410
|
"dependencies": [
|
|
407
411
|
"@agimon-ai/po-core",
|
|
408
412
|
"@agimon-ai/po-web-components",
|
|
@@ -410,18 +414,18 @@
|
|
|
410
414
|
]
|
|
411
415
|
},
|
|
412
416
|
"@agimon-ai/po-web-search": {
|
|
413
|
-
"version": "0.0.0-alpha.
|
|
417
|
+
"version": "0.0.0-alpha.19",
|
|
414
418
|
"dependencies": [
|
|
415
419
|
"@agimon-ai/po-core",
|
|
416
420
|
"@agimon-ai/po-web-components"
|
|
417
421
|
]
|
|
418
422
|
},
|
|
419
423
|
"@agimon-ai/po-web-security": {
|
|
420
|
-
"version": "0.0.0-alpha.
|
|
424
|
+
"version": "0.0.0-alpha.20",
|
|
421
425
|
"dependencies": []
|
|
422
426
|
},
|
|
423
427
|
"@agimon-ai/po-workflow": {
|
|
424
|
-
"version": "0.0.0-alpha.
|
|
428
|
+
"version": "0.0.0-alpha.20",
|
|
425
429
|
"dependencies": [
|
|
426
430
|
"@agimon-ai/po-core",
|
|
427
431
|
"@agimon-ai/po-minor-mode",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bundle.cjs","names":["scanWebPlugins","path","writeSyncWebPluginModules","PLUGIN_ROOTS_FILE","webPluginOverridePlugin","bundleAssetPolicyPlugin","webPluginRuntimeAliases","webPluginCssAlias","webPluginRuntimeFacadePlugin","isWebPluginFacade","WEB_PLUGIN_FACADE_CHUNK","fs","writeWebPluginStyleEntries","BASE_STYLE_ENTRY","pluginStyleEntryName"],"sources":["../../../../src/builders/web/bundle.ts"],"sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\n\nimport { bundleAssetPolicyPlugin } from './bundleAssetPolicy';\nimport { PLUGIN_ROOTS_FILE } from './webDevRoots';\nimport {\n BASE_STYLE_ENTRY,\n pluginStyleEntryName,\n writeSyncWebPluginModules,\n writeWebPluginStyleEntries,\n} from './webPluginGenerate';\nimport { scanWebPlugins } from './webPluginScan';\nimport {\n isWebPluginFacade,\n WEB_PLUGIN_FACADE_CHUNK,\n webPluginCssAlias,\n webPluginOverridePlugin,\n webPluginRuntimeAliases,\n webPluginRuntimeFacadePlugin,\n} from './webPluginVite';\n\n/** Shared runtime packages every plugin's client code must resolve to the host copies. */\nconst DEDUPED_RUNTIMES = [\n 'react',\n 'react-dom',\n '@tanstack/store',\n '@tanstack/react-store',\n '@tanstack/react-router',\n '@agimon-ai/po-core',\n '@agimon-ai/po-web-dashboard',\n '@agimon-ai/po-web-components',\n // The sealed transport is a module singleton whose nonce counters every\n // plugin shares; a second copy would start counting at zero.\n '@agimon-ai/po-web-security',\n // CodeMirror's state and view are singletons in all but name: a document\n // built against one copy is rejected by an editor from the other, and a\n // grammar chunk resolved separately from the editor is exactly how a second\n // copy appears.\n '@codemirror/state',\n '@codemirror/view',\n];\n\nexport interface BundleCockpitWebOptions {\n hostRoot: string;\n /** Package roots to scan for poWeb manifests, usually the installed composition. */\n pluginRoots: readonly string[];\n /** Output directory; receives web/, plugins/, generated/, and pluginRoots.json. */\n outDir: string;\n onNotice?: (message: string) => void;\n}\n\nexport interface BundleCockpitWebResult {\n /** The cockpit shell with the host's own plugins; every other plugin ships in pluginsDir. */\n assetsDir: string;\n /** Standalone session plugin composition artifacts. */\n pluginsDir: string;\n pluginIds: string[];\n /** Every declared plugin copy with its slot and the files its slot entry reaches. */\n plugins: WebPluginBuildRecord[];\n /** One content-hashed stylesheet per slot below pluginsDir; host plugins share the base. */\n pluginStyles: Record<string, string>;\n /** The shell-only stylesheet below pluginsDir. */\n baseStyle: string;\n}\n\n/** One plugin copy in a build: its slot entry and the emitted files that entry can load. */\nexport interface WebPluginBuildRecord {\n pluginId: string;\n slot: string;\n packageDir: string;\n isHost: boolean;\n /** The slot entry module below pluginsDir. */\n entry: string;\n /** Every file the entry reaches through static and dynamic imports, plus the slot stylesheet and its assets. */\n files: string[];\n /** The stylesheets of the entry's static import closure, which a mount loads up front. */\n css: string[];\n}\n\ninterface ManifestChunk {\n file: string;\n imports?: string[];\n dynamicImports?: string[];\n css?: string[];\n assets?: string[];\n}\n\n/**\n * The files one manifest entry reaches: `files` follows static and dynamic imports and holds\n * every chunk, asset, and stylesheet on the way; `css` follows static imports only.\n */\nexport function manifestClosure(\n manifest: Readonly<Record<string, ManifestChunk>>,\n entryKey: string,\n): { entry: string; files: string[]; css: string[] } {\n const chunkOf = (key: string): ManifestChunk => {\n const chunk = manifest[key];\n if (chunk === undefined) throw new Error(`The composition manifest has no chunk '${key}'.`);\n return chunk;\n };\n const files = new Set<string>();\n const visited = new Set<string>();\n const walk = (key: string): void => {\n if (visited.has(key)) return;\n visited.add(key);\n const chunk = chunkOf(key);\n files.add(chunk.file);\n for (const file of [...(chunk.css ?? []), ...(chunk.assets ?? [])]) files.add(file);\n for (const next of [...(chunk.imports ?? []), ...(chunk.dynamicImports ?? [])]) walk(next);\n };\n walk(entryKey);\n const css = new Set<string>();\n const staticVisited = new Set<string>();\n const walkStatic = (key: string): void => {\n if (staticVisited.has(key)) return;\n staticVisited.add(key);\n const chunk = chunkOf(key);\n for (const file of chunk.css ?? []) css.add(file);\n for (const next of chunk.imports ?? []) walkStatic(next);\n };\n walkStatic(entryKey);\n return { entry: chunkOf(entryKey).file, files: [...files], css: [...css] };\n}\n\n/**\n * Builds the cockpit SPA against the installed plugin set.\n *\n * This is what `po sync` calls: manifests come from the given package\n * roots (no hardcoded plugin dependencies anywhere), the generated entry\n * modules are aliased over the committed builtin registry, and Vite compiles\n * the host shell and, separately, each plugin's shipped web source into one\n * ES-module composition with an entry per slot, so each plugin and its assets ship once.\n * The roots file beside\n * the bundle lets the dev server serve the same composition with hot reload.\n */\nexport async function bundleCockpitWeb(options: BundleCockpitWebOptions): Promise<BundleCockpitWebResult> {\n const notice = options.onNotice ?? ((): void => {});\n const hostRoot = options.hostRoot;\n const plugins = scanWebPlugins(hostRoot, options.pluginRoots, notice);\n notice(\n `bundling the cockpit with ${String(plugins.length)} web plugin(s): ${plugins.map((p) => p.pluginId).join(', ')}`,\n );\n\n const generatedDir = path.join(options.outDir, 'generated');\n const assetsDir = path.join(options.outDir, 'web');\n const pluginsDir = path.join(options.outDir, 'plugins');\n const generated = writeSyncWebPluginModules(plugins, generatedDir, hostRoot);\n const roots = [...new Set(options.pluginRoots.map((root) => path.resolve(root)))];\n fs.writeFileSync(path.join(options.outDir, PLUGIN_ROOTS_FILE), `${JSON.stringify(roots, null, 2)}\\n`);\n\n // Vite and its plugins are regular dependencies so this works from an\n // installed package, not only the workspace.\n const { build } = await import('vite');\n const { default: react } = await import('@vitejs/plugin-react');\n const { default: tailwindcss } = await import('@tailwindcss/vite');\n\n const clientRoot = path.join(hostRoot, 'src', 'web');\n\n // The shell carries only the host's own plugins; the rest load from the composition below.\n await build({\n configFile: false,\n envDir: false,\n logLevel: 'warn',\n root: clientRoot,\n plugins: [webPluginOverridePlugin(clientRoot, generated.shell), react(), tailwindcss(), bundleAssetPolicyPlugin()],\n resolve: {\n dedupe: [...DEDUPED_RUNTIMES],\n alias: [...webPluginRuntimeAliases(clientRoot), webPluginCssAlias(generated.shell)],\n },\n build: {\n outDir: assetsDir,\n emptyOutDir: true,\n sourcemap: false,\n },\n });\n\n // One ES-module composition: an entry per slot exporting its webPlugin, shared chunks\n // between them, and the shared runtime modules read from the page through facades.\n await build({\n configFile: false,\n envDir: false,\n logLevel: 'warn',\n base: './',\n plugins: [webPluginRuntimeFacadePlugin(hostRoot), react(), tailwindcss()],\n resolve: {\n dedupe: [...DEDUPED_RUNTIMES],\n alias: [webPluginCssAlias(generated)],\n },\n build: {\n cssCodeSplit: true,\n modulePreload: false,\n outDir: pluginsDir,\n emptyOutDir: true,\n sourcemap: false,\n assetsInlineLimit: 0,\n manifest: 'manifest.json',\n rollupOptions: {\n input: generated.slotEntryPaths,\n // Each slot entry is loaded for its webPlugin export, so it must keep it.\n preserveEntrySignatures: 'exports-only',\n output: {\n format: 'es',\n entryFileNames: 'slots/[name]-[hash].js',\n chunkFileNames: 'chunks/[name]-[hash].js',\n assetFileNames: 'assets/[name]-[hash][extname]',\n manualChunks: (id) => (isWebPluginFacade(id) ? WEB_PLUGIN_FACADE_CHUNK : undefined),\n },\n },\n },\n });\n const manifest = JSON.parse(fs.readFileSync(path.join(pluginsDir, 'manifest.json'), 'utf8')) as Record<\n string,\n ManifestChunk\n >;\n // Manifest keys are the entry sources relative to the build root, which is the working directory.\n const manifestRoot = fs.realpathSync(process.cwd());\n const closures = new Map(\n plugins.map((plugin) => {\n const entryPath = fs.realpathSync(generated.slotEntryPaths[plugin.slot]);\n return [plugin.slot, manifestClosure(manifest, path.relative(manifestRoot, entryPath).split(path.sep).join('/'))];\n }),\n );\n\n // One content-hashed stylesheet per plugin, compiled from its own sources over the shell's.\n const styles = writeWebPluginStyleEntries(plugins, generatedDir, hostRoot);\n const stylesRoot = path.dirname(styles.baseEntryPath);\n await build({\n configFile: false,\n envDir: false,\n logLevel: 'warn',\n root: stylesRoot,\n base: './',\n plugins: [tailwindcss()],\n resolve: {\n alias: [{ find: /^\\.\\/webPluginSources\\.generated\\.css$/, replacement: styles.baseSourcesPath }],\n },\n build: {\n cssCodeSplit: true,\n outDir: pluginsDir,\n emptyOutDir: false,\n sourcemap: false,\n assetsInlineLimit: 0,\n manifest: 'styles.manifest.json',\n rollupOptions: {\n input: Object.fromEntries([\n [BASE_STYLE_ENTRY, styles.baseEntryPath],\n ...[...styles.pluginEntryPaths].map(([slot, entry]) => [pluginStyleEntryName(slot), entry]),\n ]),\n output: { assetFileNames: 'assets/[name]-[hash][extname]' },\n },\n },\n });\n const styleManifest = JSON.parse(fs.readFileSync(path.join(pluginsDir, 'styles.manifest.json'), 'utf8')) as Record<\n string,\n { file?: string; css?: string[]; assets?: string[] }\n >;\n fs.rmSync(path.join(pluginsDir, 'styles.manifest.json'));\n const emitted = (entry: string): { file: string; assets: string[] } => {\n const record = styleManifest[path.relative(stylesRoot, entry).split(path.sep).join('/')];\n const file = record?.file?.endsWith('.css') ? record.file : record?.css?.[0];\n if (file === undefined) throw new Error(`The stylesheet entry '${entry}' emitted no CSS file.`);\n return { file, assets: record?.assets ?? [] };\n };\n const base = emitted(styles.baseEntryPath);\n const slotStyles = new Map(\n plugins.map((plugin) => {\n const entry = styles.pluginEntryPaths.get(plugin.slot);\n return [plugin.slot, entry === undefined ? base : emitted(entry)];\n }),\n );\n const pluginStyles = Object.fromEntries([...slotStyles].map(([slot, style]) => [slot, style.file]));\n fs.writeFileSync(path.join(pluginsDir, 'index.html'), '<!doctype html>\\n<title>Po plugin composition</title>\\n');\n\n return {\n assetsDir,\n pluginsDir,\n pluginIds: plugins.map((plugin) => plugin.pluginId),\n plugins: plugins.map((plugin) => {\n const closure = closures.get(plugin.slot);\n const style = slotStyles.get(plugin.slot);\n if (closure === undefined || style === undefined) throw new Error(`The slot '${plugin.slot}' emitted no entry.`);\n return {\n pluginId: plugin.pluginId,\n slot: plugin.slot,\n packageDir: plugin.packageDir,\n isHost: plugin.isHost,\n entry: closure.entry,\n // The slot's own stylesheet and the fonts it references load with it.\n files: [...new Set([...closure.files, style.file, ...style.assets])],\n css: closure.css,\n };\n }),\n pluginStyles,\n baseStyle: base.file,\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"bundle.cjs","names":["scanWebPlugins","path","writeSyncWebPluginModules","PLUGIN_ROOTS_FILE","webPluginOverridePlugin","bundleAssetPolicyPlugin","webPluginRuntimeAliases","webPluginCssAlias","webPluginRuntimeFacadePlugin","isWebPluginFacade","WEB_PLUGIN_FACADE_CHUNK","fs","writeWebPluginStyleEntries","BASE_STYLE_ENTRY","pluginStyleEntryName"],"sources":["../../../../src/builders/web/bundle.ts"],"sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\n\nimport { bundleAssetPolicyPlugin } from './bundleAssetPolicy';\nimport { PLUGIN_ROOTS_FILE } from './webDevRoots';\nimport {\n BASE_STYLE_ENTRY,\n pluginStyleEntryName,\n writeSyncWebPluginModules,\n writeWebPluginStyleEntries,\n} from './webPluginGenerate';\nimport { scanWebPlugins } from './webPluginScan';\nimport {\n isWebPluginFacade,\n WEB_PLUGIN_FACADE_CHUNK,\n webPluginCssAlias,\n webPluginOverridePlugin,\n webPluginRuntimeAliases,\n webPluginRuntimeFacadePlugin,\n} from './webPluginVite';\n\n/** Shared runtime packages every plugin's client code must resolve to the host copies. */\nconst DEDUPED_RUNTIMES = [\n 'react',\n 'react-dom',\n '@tanstack/store',\n '@tanstack/react-store',\n '@tanstack/react-router',\n '@agimon-ai/po-core',\n '@agimon-ai/po-web-dashboard',\n '@agimon-ai/po-web-components',\n // The sealed transport is a module singleton whose nonce counters every\n // plugin shares; a second copy would start counting at zero.\n '@agimon-ai/po-web-security',\n // CodeMirror's state and view are singletons in all but name: a document\n // built against one copy is rejected by an editor from the other, and a\n // grammar chunk resolved separately from the editor is exactly how a second\n // copy appears.\n '@codemirror/state',\n '@codemirror/view',\n];\n\nexport interface BundleCockpitWebOptions {\n hostRoot: string;\n /** Package roots to scan for poWeb manifests, usually the installed composition. */\n pluginRoots: readonly string[];\n /** Output directory; receives web/, plugins/, generated/, and pluginRoots.json. */\n outDir: string;\n onNotice?: (message: string) => void;\n}\n\nexport interface BundleCockpitWebResult {\n /** The cockpit shell with the host's own plugins; every other plugin ships in pluginsDir. */\n assetsDir: string;\n /** Standalone session plugin composition artifacts. */\n pluginsDir: string;\n pluginIds: string[];\n /** Every declared plugin copy with its slot and the files its slot entry reaches. */\n plugins: WebPluginBuildRecord[];\n /** The shared content-hashed Tailwind stylesheet below pluginsDir, keyed by slot. */\n pluginStyles: Record<string, string>;\n /** The same shared stylesheet, also loaded by mounts without plugins. */\n baseStyle: string;\n}\n\n/** One plugin copy in a build: its slot entry and the emitted files that entry can load. */\nexport interface WebPluginBuildRecord {\n pluginId: string;\n slot: string;\n packageDir: string;\n isHost: boolean;\n /** The slot entry module below pluginsDir. */\n entry: string;\n /** Every file the entry reaches through imports, plus the shared stylesheet and its assets. */\n files: string[];\n /** The stylesheets of the entry's static import closure, which a mount loads up front. */\n css: string[];\n}\n\ninterface ManifestChunk {\n file: string;\n imports?: string[];\n dynamicImports?: string[];\n css?: string[];\n assets?: string[];\n}\n\n/**\n * The files one manifest entry reaches: `files` follows static and dynamic imports and holds\n * every chunk, asset, and stylesheet on the way; `css` follows static imports only.\n */\nexport function manifestClosure(\n manifest: Readonly<Record<string, ManifestChunk>>,\n entryKey: string,\n): { entry: string; files: string[]; css: string[] } {\n const chunkOf = (key: string): ManifestChunk => {\n const chunk = manifest[key];\n if (chunk === undefined) throw new Error(`The composition manifest has no chunk '${key}'.`);\n return chunk;\n };\n const files = new Set<string>();\n const visited = new Set<string>();\n const walk = (key: string): void => {\n if (visited.has(key)) return;\n visited.add(key);\n const chunk = chunkOf(key);\n files.add(chunk.file);\n for (const file of [...(chunk.css ?? []), ...(chunk.assets ?? [])]) files.add(file);\n for (const next of [...(chunk.imports ?? []), ...(chunk.dynamicImports ?? [])]) walk(next);\n };\n walk(entryKey);\n const css = new Set<string>();\n const staticVisited = new Set<string>();\n const walkStatic = (key: string): void => {\n if (staticVisited.has(key)) return;\n staticVisited.add(key);\n const chunk = chunkOf(key);\n for (const file of chunk.css ?? []) css.add(file);\n for (const next of chunk.imports ?? []) walkStatic(next);\n };\n walkStatic(entryKey);\n return { entry: chunkOf(entryKey).file, files: [...files], css: [...css] };\n}\n\n/**\n * Builds the cockpit SPA against the installed plugin set.\n *\n * This is what `po sync` calls: manifests come from the given package\n * roots (no hardcoded plugin dependencies anywhere), the generated entry\n * modules are aliased over the committed builtin registry, and Vite compiles\n * the host shell and, separately, each plugin's shipped web source into one\n * ES-module composition with an entry per slot, so each plugin and its assets ship once.\n * The roots file beside\n * the bundle lets the dev server serve the same composition with hot reload.\n */\nexport async function bundleCockpitWeb(options: BundleCockpitWebOptions): Promise<BundleCockpitWebResult> {\n const notice = options.onNotice ?? ((): void => {});\n const hostRoot = options.hostRoot;\n const plugins = scanWebPlugins(hostRoot, options.pluginRoots, notice);\n notice(\n `bundling the cockpit with ${String(plugins.length)} web plugin(s): ${plugins.map((p) => p.pluginId).join(', ')}`,\n );\n\n const generatedDir = path.join(options.outDir, 'generated');\n const assetsDir = path.join(options.outDir, 'web');\n const pluginsDir = path.join(options.outDir, 'plugins');\n const generated = writeSyncWebPluginModules(plugins, generatedDir, hostRoot);\n const roots = [...new Set(options.pluginRoots.map((root) => path.resolve(root)))];\n fs.writeFileSync(path.join(options.outDir, PLUGIN_ROOTS_FILE), `${JSON.stringify(roots, null, 2)}\\n`);\n\n // Vite and its plugins are regular dependencies so this works from an\n // installed package, not only the workspace.\n const { build } = await import('vite');\n const { default: react } = await import('@vitejs/plugin-react');\n const { default: tailwindcss } = await import('@tailwindcss/vite');\n\n const clientRoot = path.join(hostRoot, 'src', 'web');\n\n // The shell carries only the host's own plugins; the rest load from the composition below.\n await build({\n configFile: false,\n envDir: false,\n logLevel: 'warn',\n root: clientRoot,\n plugins: [webPluginOverridePlugin(clientRoot, generated.shell), react(), tailwindcss(), bundleAssetPolicyPlugin()],\n resolve: {\n dedupe: [...DEDUPED_RUNTIMES],\n alias: [...webPluginRuntimeAliases(clientRoot), webPluginCssAlias(generated.shell)],\n },\n build: {\n outDir: assetsDir,\n emptyOutDir: true,\n sourcemap: false,\n },\n });\n\n // One ES-module composition: an entry per slot exporting its webPlugin, shared chunks\n // between them, and the shared runtime modules read from the page through facades.\n await build({\n configFile: false,\n envDir: false,\n logLevel: 'warn',\n base: './',\n plugins: [webPluginRuntimeFacadePlugin(hostRoot), react(), tailwindcss()],\n resolve: {\n dedupe: [...DEDUPED_RUNTIMES],\n alias: [webPluginCssAlias(generated)],\n },\n build: {\n cssCodeSplit: true,\n modulePreload: false,\n outDir: pluginsDir,\n emptyOutDir: true,\n sourcemap: false,\n assetsInlineLimit: 0,\n manifest: 'manifest.json',\n rollupOptions: {\n input: generated.slotEntryPaths,\n // Each slot entry is loaded for its webPlugin export, so it must keep it.\n preserveEntrySignatures: 'exports-only',\n output: {\n format: 'es',\n entryFileNames: 'slots/[name]-[hash].js',\n chunkFileNames: 'chunks/[name]-[hash].js',\n assetFileNames: 'assets/[name]-[hash][extname]',\n manualChunks: (id) => (isWebPluginFacade(id) ? WEB_PLUGIN_FACADE_CHUNK : undefined),\n },\n },\n },\n });\n const manifest = JSON.parse(fs.readFileSync(path.join(pluginsDir, 'manifest.json'), 'utf8')) as Record<\n string,\n ManifestChunk\n >;\n // Manifest keys are the entry sources relative to the build root, which is the working directory.\n const manifestRoot = fs.realpathSync(process.cwd());\n const closures = new Map(\n plugins.map((plugin) => {\n const entryPath = fs.realpathSync(generated.slotEntryPaths[plugin.slot]);\n return [plugin.slot, manifestClosure(manifest, path.relative(manifestRoot, entryPath).split(path.sep).join('/'))];\n }),\n );\n\n // One content-hashed Tailwind stylesheet for the shell, fallback template, and all plugins.\n const styles = writeWebPluginStyleEntries(plugins, generatedDir, hostRoot);\n const stylesRoot = path.dirname(styles.baseEntryPath);\n await build({\n configFile: false,\n envDir: false,\n logLevel: 'warn',\n root: stylesRoot,\n base: './',\n plugins: [tailwindcss()],\n resolve: {\n alias: [{ find: /^\\.\\/webPluginSources\\.generated\\.css$/, replacement: styles.baseSourcesPath }],\n },\n build: {\n cssCodeSplit: true,\n outDir: pluginsDir,\n emptyOutDir: false,\n sourcemap: false,\n assetsInlineLimit: 0,\n manifest: 'styles.manifest.json',\n rollupOptions: {\n input: Object.fromEntries([\n [BASE_STYLE_ENTRY, styles.baseEntryPath],\n ...[...styles.pluginEntryPaths].map(([slot, entry]) => [pluginStyleEntryName(slot), entry]),\n ]),\n output: { assetFileNames: 'assets/[name]-[hash][extname]' },\n },\n },\n });\n const styleManifest = JSON.parse(fs.readFileSync(path.join(pluginsDir, 'styles.manifest.json'), 'utf8')) as Record<\n string,\n { file?: string; css?: string[]; assets?: string[] }\n >;\n fs.rmSync(path.join(pluginsDir, 'styles.manifest.json'));\n const emitted = (entry: string): { file: string; assets: string[] } => {\n const record = styleManifest[path.relative(stylesRoot, entry).split(path.sep).join('/')];\n const file = record?.file?.endsWith('.css') ? record.file : record?.css?.[0];\n if (file === undefined) throw new Error(`The stylesheet entry '${entry}' emitted no CSS file.`);\n return { file, assets: record?.assets ?? [] };\n };\n const base = emitted(styles.baseEntryPath);\n const slotStyles = new Map(\n plugins.map((plugin) => {\n const entry = styles.pluginEntryPaths.get(plugin.slot);\n return [plugin.slot, entry === undefined ? base : emitted(entry)];\n }),\n );\n const pluginStyles = Object.fromEntries([...slotStyles].map(([slot, style]) => [slot, style.file]));\n fs.writeFileSync(path.join(pluginsDir, 'index.html'), '<!doctype html>\\n<title>Po plugin composition</title>\\n');\n\n return {\n assetsDir,\n pluginsDir,\n pluginIds: plugins.map((plugin) => plugin.pluginId),\n plugins: plugins.map((plugin) => {\n const closure = closures.get(plugin.slot);\n const style = slotStyles.get(plugin.slot);\n if (closure === undefined || style === undefined) throw new Error(`The slot '${plugin.slot}' emitted no entry.`);\n return {\n pluginId: plugin.pluginId,\n slot: plugin.slot,\n packageDir: plugin.packageDir,\n isHost: plugin.isHost,\n entry: closure.entry,\n // The shared stylesheet and its fonts must be verified for every slot.\n files: [...new Set([...closure.files, style.file, ...style.assets])],\n css: closure.css,\n };\n }),\n pluginStyles,\n baseStyle: base.file,\n };\n}\n"],"mappings":"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"}
|
|
@@ -15,9 +15,9 @@ export interface BundleCockpitWebResult {
|
|
|
15
15
|
pluginIds: string[];
|
|
16
16
|
/** Every declared plugin copy with its slot and the files its slot entry reaches. */
|
|
17
17
|
plugins: WebPluginBuildRecord[];
|
|
18
|
-
/**
|
|
18
|
+
/** The shared content-hashed Tailwind stylesheet below pluginsDir, keyed by slot. */
|
|
19
19
|
pluginStyles: Record<string, string>;
|
|
20
|
-
/** The
|
|
20
|
+
/** The same shared stylesheet, also loaded by mounts without plugins. */
|
|
21
21
|
baseStyle: string;
|
|
22
22
|
}
|
|
23
23
|
/** One plugin copy in a build: its slot entry and the emitted files that entry can load. */
|
|
@@ -28,7 +28,7 @@ export interface WebPluginBuildRecord {
|
|
|
28
28
|
isHost: boolean;
|
|
29
29
|
/** The slot entry module below pluginsDir. */
|
|
30
30
|
entry: string;
|
|
31
|
-
/** Every file the entry reaches through
|
|
31
|
+
/** Every file the entry reaches through imports, plus the shared stylesheet and its assets. */
|
|
32
32
|
files: string[];
|
|
33
33
|
/** The stylesheets of the entry's static import closure, which a mount loads up front. */
|
|
34
34
|
css: string[];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bundle.mjs","names":[],"sources":["../../../../src/builders/web/bundle.ts"],"sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\n\nimport { bundleAssetPolicyPlugin } from './bundleAssetPolicy';\nimport { PLUGIN_ROOTS_FILE } from './webDevRoots';\nimport {\n BASE_STYLE_ENTRY,\n pluginStyleEntryName,\n writeSyncWebPluginModules,\n writeWebPluginStyleEntries,\n} from './webPluginGenerate';\nimport { scanWebPlugins } from './webPluginScan';\nimport {\n isWebPluginFacade,\n WEB_PLUGIN_FACADE_CHUNK,\n webPluginCssAlias,\n webPluginOverridePlugin,\n webPluginRuntimeAliases,\n webPluginRuntimeFacadePlugin,\n} from './webPluginVite';\n\n/** Shared runtime packages every plugin's client code must resolve to the host copies. */\nconst DEDUPED_RUNTIMES = [\n 'react',\n 'react-dom',\n '@tanstack/store',\n '@tanstack/react-store',\n '@tanstack/react-router',\n '@agimon-ai/po-core',\n '@agimon-ai/po-web-dashboard',\n '@agimon-ai/po-web-components',\n // The sealed transport is a module singleton whose nonce counters every\n // plugin shares; a second copy would start counting at zero.\n '@agimon-ai/po-web-security',\n // CodeMirror's state and view are singletons in all but name: a document\n // built against one copy is rejected by an editor from the other, and a\n // grammar chunk resolved separately from the editor is exactly how a second\n // copy appears.\n '@codemirror/state',\n '@codemirror/view',\n];\n\nexport interface BundleCockpitWebOptions {\n hostRoot: string;\n /** Package roots to scan for poWeb manifests, usually the installed composition. */\n pluginRoots: readonly string[];\n /** Output directory; receives web/, plugins/, generated/, and pluginRoots.json. */\n outDir: string;\n onNotice?: (message: string) => void;\n}\n\nexport interface BundleCockpitWebResult {\n /** The cockpit shell with the host's own plugins; every other plugin ships in pluginsDir. */\n assetsDir: string;\n /** Standalone session plugin composition artifacts. */\n pluginsDir: string;\n pluginIds: string[];\n /** Every declared plugin copy with its slot and the files its slot entry reaches. */\n plugins: WebPluginBuildRecord[];\n /** One content-hashed stylesheet per slot below pluginsDir; host plugins share the base. */\n pluginStyles: Record<string, string>;\n /** The shell-only stylesheet below pluginsDir. */\n baseStyle: string;\n}\n\n/** One plugin copy in a build: its slot entry and the emitted files that entry can load. */\nexport interface WebPluginBuildRecord {\n pluginId: string;\n slot: string;\n packageDir: string;\n isHost: boolean;\n /** The slot entry module below pluginsDir. */\n entry: string;\n /** Every file the entry reaches through static and dynamic imports, plus the slot stylesheet and its assets. */\n files: string[];\n /** The stylesheets of the entry's static import closure, which a mount loads up front. */\n css: string[];\n}\n\ninterface ManifestChunk {\n file: string;\n imports?: string[];\n dynamicImports?: string[];\n css?: string[];\n assets?: string[];\n}\n\n/**\n * The files one manifest entry reaches: `files` follows static and dynamic imports and holds\n * every chunk, asset, and stylesheet on the way; `css` follows static imports only.\n */\nexport function manifestClosure(\n manifest: Readonly<Record<string, ManifestChunk>>,\n entryKey: string,\n): { entry: string; files: string[]; css: string[] } {\n const chunkOf = (key: string): ManifestChunk => {\n const chunk = manifest[key];\n if (chunk === undefined) throw new Error(`The composition manifest has no chunk '${key}'.`);\n return chunk;\n };\n const files = new Set<string>();\n const visited = new Set<string>();\n const walk = (key: string): void => {\n if (visited.has(key)) return;\n visited.add(key);\n const chunk = chunkOf(key);\n files.add(chunk.file);\n for (const file of [...(chunk.css ?? []), ...(chunk.assets ?? [])]) files.add(file);\n for (const next of [...(chunk.imports ?? []), ...(chunk.dynamicImports ?? [])]) walk(next);\n };\n walk(entryKey);\n const css = new Set<string>();\n const staticVisited = new Set<string>();\n const walkStatic = (key: string): void => {\n if (staticVisited.has(key)) return;\n staticVisited.add(key);\n const chunk = chunkOf(key);\n for (const file of chunk.css ?? []) css.add(file);\n for (const next of chunk.imports ?? []) walkStatic(next);\n };\n walkStatic(entryKey);\n return { entry: chunkOf(entryKey).file, files: [...files], css: [...css] };\n}\n\n/**\n * Builds the cockpit SPA against the installed plugin set.\n *\n * This is what `po sync` calls: manifests come from the given package\n * roots (no hardcoded plugin dependencies anywhere), the generated entry\n * modules are aliased over the committed builtin registry, and Vite compiles\n * the host shell and, separately, each plugin's shipped web source into one\n * ES-module composition with an entry per slot, so each plugin and its assets ship once.\n * The roots file beside\n * the bundle lets the dev server serve the same composition with hot reload.\n */\nexport async function bundleCockpitWeb(options: BundleCockpitWebOptions): Promise<BundleCockpitWebResult> {\n const notice = options.onNotice ?? ((): void => {});\n const hostRoot = options.hostRoot;\n const plugins = scanWebPlugins(hostRoot, options.pluginRoots, notice);\n notice(\n `bundling the cockpit with ${String(plugins.length)} web plugin(s): ${plugins.map((p) => p.pluginId).join(', ')}`,\n );\n\n const generatedDir = path.join(options.outDir, 'generated');\n const assetsDir = path.join(options.outDir, 'web');\n const pluginsDir = path.join(options.outDir, 'plugins');\n const generated = writeSyncWebPluginModules(plugins, generatedDir, hostRoot);\n const roots = [...new Set(options.pluginRoots.map((root) => path.resolve(root)))];\n fs.writeFileSync(path.join(options.outDir, PLUGIN_ROOTS_FILE), `${JSON.stringify(roots, null, 2)}\\n`);\n\n // Vite and its plugins are regular dependencies so this works from an\n // installed package, not only the workspace.\n const { build } = await import('vite');\n const { default: react } = await import('@vitejs/plugin-react');\n const { default: tailwindcss } = await import('@tailwindcss/vite');\n\n const clientRoot = path.join(hostRoot, 'src', 'web');\n\n // The shell carries only the host's own plugins; the rest load from the composition below.\n await build({\n configFile: false,\n envDir: false,\n logLevel: 'warn',\n root: clientRoot,\n plugins: [webPluginOverridePlugin(clientRoot, generated.shell), react(), tailwindcss(), bundleAssetPolicyPlugin()],\n resolve: {\n dedupe: [...DEDUPED_RUNTIMES],\n alias: [...webPluginRuntimeAliases(clientRoot), webPluginCssAlias(generated.shell)],\n },\n build: {\n outDir: assetsDir,\n emptyOutDir: true,\n sourcemap: false,\n },\n });\n\n // One ES-module composition: an entry per slot exporting its webPlugin, shared chunks\n // between them, and the shared runtime modules read from the page through facades.\n await build({\n configFile: false,\n envDir: false,\n logLevel: 'warn',\n base: './',\n plugins: [webPluginRuntimeFacadePlugin(hostRoot), react(), tailwindcss()],\n resolve: {\n dedupe: [...DEDUPED_RUNTIMES],\n alias: [webPluginCssAlias(generated)],\n },\n build: {\n cssCodeSplit: true,\n modulePreload: false,\n outDir: pluginsDir,\n emptyOutDir: true,\n sourcemap: false,\n assetsInlineLimit: 0,\n manifest: 'manifest.json',\n rollupOptions: {\n input: generated.slotEntryPaths,\n // Each slot entry is loaded for its webPlugin export, so it must keep it.\n preserveEntrySignatures: 'exports-only',\n output: {\n format: 'es',\n entryFileNames: 'slots/[name]-[hash].js',\n chunkFileNames: 'chunks/[name]-[hash].js',\n assetFileNames: 'assets/[name]-[hash][extname]',\n manualChunks: (id) => (isWebPluginFacade(id) ? WEB_PLUGIN_FACADE_CHUNK : undefined),\n },\n },\n },\n });\n const manifest = JSON.parse(fs.readFileSync(path.join(pluginsDir, 'manifest.json'), 'utf8')) as Record<\n string,\n ManifestChunk\n >;\n // Manifest keys are the entry sources relative to the build root, which is the working directory.\n const manifestRoot = fs.realpathSync(process.cwd());\n const closures = new Map(\n plugins.map((plugin) => {\n const entryPath = fs.realpathSync(generated.slotEntryPaths[plugin.slot]);\n return [plugin.slot, manifestClosure(manifest, path.relative(manifestRoot, entryPath).split(path.sep).join('/'))];\n }),\n );\n\n // One content-hashed stylesheet per plugin, compiled from its own sources over the shell's.\n const styles = writeWebPluginStyleEntries(plugins, generatedDir, hostRoot);\n const stylesRoot = path.dirname(styles.baseEntryPath);\n await build({\n configFile: false,\n envDir: false,\n logLevel: 'warn',\n root: stylesRoot,\n base: './',\n plugins: [tailwindcss()],\n resolve: {\n alias: [{ find: /^\\.\\/webPluginSources\\.generated\\.css$/, replacement: styles.baseSourcesPath }],\n },\n build: {\n cssCodeSplit: true,\n outDir: pluginsDir,\n emptyOutDir: false,\n sourcemap: false,\n assetsInlineLimit: 0,\n manifest: 'styles.manifest.json',\n rollupOptions: {\n input: Object.fromEntries([\n [BASE_STYLE_ENTRY, styles.baseEntryPath],\n ...[...styles.pluginEntryPaths].map(([slot, entry]) => [pluginStyleEntryName(slot), entry]),\n ]),\n output: { assetFileNames: 'assets/[name]-[hash][extname]' },\n },\n },\n });\n const styleManifest = JSON.parse(fs.readFileSync(path.join(pluginsDir, 'styles.manifest.json'), 'utf8')) as Record<\n string,\n { file?: string; css?: string[]; assets?: string[] }\n >;\n fs.rmSync(path.join(pluginsDir, 'styles.manifest.json'));\n const emitted = (entry: string): { file: string; assets: string[] } => {\n const record = styleManifest[path.relative(stylesRoot, entry).split(path.sep).join('/')];\n const file = record?.file?.endsWith('.css') ? record.file : record?.css?.[0];\n if (file === undefined) throw new Error(`The stylesheet entry '${entry}' emitted no CSS file.`);\n return { file, assets: record?.assets ?? [] };\n };\n const base = emitted(styles.baseEntryPath);\n const slotStyles = new Map(\n plugins.map((plugin) => {\n const entry = styles.pluginEntryPaths.get(plugin.slot);\n return [plugin.slot, entry === undefined ? base : emitted(entry)];\n }),\n );\n const pluginStyles = Object.fromEntries([...slotStyles].map(([slot, style]) => [slot, style.file]));\n fs.writeFileSync(path.join(pluginsDir, 'index.html'), '<!doctype html>\\n<title>Po plugin composition</title>\\n');\n\n return {\n assetsDir,\n pluginsDir,\n pluginIds: plugins.map((plugin) => plugin.pluginId),\n plugins: plugins.map((plugin) => {\n const closure = closures.get(plugin.slot);\n const style = slotStyles.get(plugin.slot);\n if (closure === undefined || style === undefined) throw new Error(`The slot '${plugin.slot}' emitted no entry.`);\n return {\n pluginId: plugin.pluginId,\n slot: plugin.slot,\n packageDir: plugin.packageDir,\n isHost: plugin.isHost,\n entry: closure.entry,\n // The slot's own stylesheet and the fonts it references load with it.\n files: [...new Set([...closure.files, style.file, ...style.assets])],\n css: closure.css,\n };\n }),\n pluginStyles,\n baseStyle: base.file,\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"bundle.mjs","names":[],"sources":["../../../../src/builders/web/bundle.ts"],"sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\n\nimport { bundleAssetPolicyPlugin } from './bundleAssetPolicy';\nimport { PLUGIN_ROOTS_FILE } from './webDevRoots';\nimport {\n BASE_STYLE_ENTRY,\n pluginStyleEntryName,\n writeSyncWebPluginModules,\n writeWebPluginStyleEntries,\n} from './webPluginGenerate';\nimport { scanWebPlugins } from './webPluginScan';\nimport {\n isWebPluginFacade,\n WEB_PLUGIN_FACADE_CHUNK,\n webPluginCssAlias,\n webPluginOverridePlugin,\n webPluginRuntimeAliases,\n webPluginRuntimeFacadePlugin,\n} from './webPluginVite';\n\n/** Shared runtime packages every plugin's client code must resolve to the host copies. */\nconst DEDUPED_RUNTIMES = [\n 'react',\n 'react-dom',\n '@tanstack/store',\n '@tanstack/react-store',\n '@tanstack/react-router',\n '@agimon-ai/po-core',\n '@agimon-ai/po-web-dashboard',\n '@agimon-ai/po-web-components',\n // The sealed transport is a module singleton whose nonce counters every\n // plugin shares; a second copy would start counting at zero.\n '@agimon-ai/po-web-security',\n // CodeMirror's state and view are singletons in all but name: a document\n // built against one copy is rejected by an editor from the other, and a\n // grammar chunk resolved separately from the editor is exactly how a second\n // copy appears.\n '@codemirror/state',\n '@codemirror/view',\n];\n\nexport interface BundleCockpitWebOptions {\n hostRoot: string;\n /** Package roots to scan for poWeb manifests, usually the installed composition. */\n pluginRoots: readonly string[];\n /** Output directory; receives web/, plugins/, generated/, and pluginRoots.json. */\n outDir: string;\n onNotice?: (message: string) => void;\n}\n\nexport interface BundleCockpitWebResult {\n /** The cockpit shell with the host's own plugins; every other plugin ships in pluginsDir. */\n assetsDir: string;\n /** Standalone session plugin composition artifacts. */\n pluginsDir: string;\n pluginIds: string[];\n /** Every declared plugin copy with its slot and the files its slot entry reaches. */\n plugins: WebPluginBuildRecord[];\n /** The shared content-hashed Tailwind stylesheet below pluginsDir, keyed by slot. */\n pluginStyles: Record<string, string>;\n /** The same shared stylesheet, also loaded by mounts without plugins. */\n baseStyle: string;\n}\n\n/** One plugin copy in a build: its slot entry and the emitted files that entry can load. */\nexport interface WebPluginBuildRecord {\n pluginId: string;\n slot: string;\n packageDir: string;\n isHost: boolean;\n /** The slot entry module below pluginsDir. */\n entry: string;\n /** Every file the entry reaches through imports, plus the shared stylesheet and its assets. */\n files: string[];\n /** The stylesheets of the entry's static import closure, which a mount loads up front. */\n css: string[];\n}\n\ninterface ManifestChunk {\n file: string;\n imports?: string[];\n dynamicImports?: string[];\n css?: string[];\n assets?: string[];\n}\n\n/**\n * The files one manifest entry reaches: `files` follows static and dynamic imports and holds\n * every chunk, asset, and stylesheet on the way; `css` follows static imports only.\n */\nexport function manifestClosure(\n manifest: Readonly<Record<string, ManifestChunk>>,\n entryKey: string,\n): { entry: string; files: string[]; css: string[] } {\n const chunkOf = (key: string): ManifestChunk => {\n const chunk = manifest[key];\n if (chunk === undefined) throw new Error(`The composition manifest has no chunk '${key}'.`);\n return chunk;\n };\n const files = new Set<string>();\n const visited = new Set<string>();\n const walk = (key: string): void => {\n if (visited.has(key)) return;\n visited.add(key);\n const chunk = chunkOf(key);\n files.add(chunk.file);\n for (const file of [...(chunk.css ?? []), ...(chunk.assets ?? [])]) files.add(file);\n for (const next of [...(chunk.imports ?? []), ...(chunk.dynamicImports ?? [])]) walk(next);\n };\n walk(entryKey);\n const css = new Set<string>();\n const staticVisited = new Set<string>();\n const walkStatic = (key: string): void => {\n if (staticVisited.has(key)) return;\n staticVisited.add(key);\n const chunk = chunkOf(key);\n for (const file of chunk.css ?? []) css.add(file);\n for (const next of chunk.imports ?? []) walkStatic(next);\n };\n walkStatic(entryKey);\n return { entry: chunkOf(entryKey).file, files: [...files], css: [...css] };\n}\n\n/**\n * Builds the cockpit SPA against the installed plugin set.\n *\n * This is what `po sync` calls: manifests come from the given package\n * roots (no hardcoded plugin dependencies anywhere), the generated entry\n * modules are aliased over the committed builtin registry, and Vite compiles\n * the host shell and, separately, each plugin's shipped web source into one\n * ES-module composition with an entry per slot, so each plugin and its assets ship once.\n * The roots file beside\n * the bundle lets the dev server serve the same composition with hot reload.\n */\nexport async function bundleCockpitWeb(options: BundleCockpitWebOptions): Promise<BundleCockpitWebResult> {\n const notice = options.onNotice ?? ((): void => {});\n const hostRoot = options.hostRoot;\n const plugins = scanWebPlugins(hostRoot, options.pluginRoots, notice);\n notice(\n `bundling the cockpit with ${String(plugins.length)} web plugin(s): ${plugins.map((p) => p.pluginId).join(', ')}`,\n );\n\n const generatedDir = path.join(options.outDir, 'generated');\n const assetsDir = path.join(options.outDir, 'web');\n const pluginsDir = path.join(options.outDir, 'plugins');\n const generated = writeSyncWebPluginModules(plugins, generatedDir, hostRoot);\n const roots = [...new Set(options.pluginRoots.map((root) => path.resolve(root)))];\n fs.writeFileSync(path.join(options.outDir, PLUGIN_ROOTS_FILE), `${JSON.stringify(roots, null, 2)}\\n`);\n\n // Vite and its plugins are regular dependencies so this works from an\n // installed package, not only the workspace.\n const { build } = await import('vite');\n const { default: react } = await import('@vitejs/plugin-react');\n const { default: tailwindcss } = await import('@tailwindcss/vite');\n\n const clientRoot = path.join(hostRoot, 'src', 'web');\n\n // The shell carries only the host's own plugins; the rest load from the composition below.\n await build({\n configFile: false,\n envDir: false,\n logLevel: 'warn',\n root: clientRoot,\n plugins: [webPluginOverridePlugin(clientRoot, generated.shell), react(), tailwindcss(), bundleAssetPolicyPlugin()],\n resolve: {\n dedupe: [...DEDUPED_RUNTIMES],\n alias: [...webPluginRuntimeAliases(clientRoot), webPluginCssAlias(generated.shell)],\n },\n build: {\n outDir: assetsDir,\n emptyOutDir: true,\n sourcemap: false,\n },\n });\n\n // One ES-module composition: an entry per slot exporting its webPlugin, shared chunks\n // between them, and the shared runtime modules read from the page through facades.\n await build({\n configFile: false,\n envDir: false,\n logLevel: 'warn',\n base: './',\n plugins: [webPluginRuntimeFacadePlugin(hostRoot), react(), tailwindcss()],\n resolve: {\n dedupe: [...DEDUPED_RUNTIMES],\n alias: [webPluginCssAlias(generated)],\n },\n build: {\n cssCodeSplit: true,\n modulePreload: false,\n outDir: pluginsDir,\n emptyOutDir: true,\n sourcemap: false,\n assetsInlineLimit: 0,\n manifest: 'manifest.json',\n rollupOptions: {\n input: generated.slotEntryPaths,\n // Each slot entry is loaded for its webPlugin export, so it must keep it.\n preserveEntrySignatures: 'exports-only',\n output: {\n format: 'es',\n entryFileNames: 'slots/[name]-[hash].js',\n chunkFileNames: 'chunks/[name]-[hash].js',\n assetFileNames: 'assets/[name]-[hash][extname]',\n manualChunks: (id) => (isWebPluginFacade(id) ? WEB_PLUGIN_FACADE_CHUNK : undefined),\n },\n },\n },\n });\n const manifest = JSON.parse(fs.readFileSync(path.join(pluginsDir, 'manifest.json'), 'utf8')) as Record<\n string,\n ManifestChunk\n >;\n // Manifest keys are the entry sources relative to the build root, which is the working directory.\n const manifestRoot = fs.realpathSync(process.cwd());\n const closures = new Map(\n plugins.map((plugin) => {\n const entryPath = fs.realpathSync(generated.slotEntryPaths[plugin.slot]);\n return [plugin.slot, manifestClosure(manifest, path.relative(manifestRoot, entryPath).split(path.sep).join('/'))];\n }),\n );\n\n // One content-hashed Tailwind stylesheet for the shell, fallback template, and all plugins.\n const styles = writeWebPluginStyleEntries(plugins, generatedDir, hostRoot);\n const stylesRoot = path.dirname(styles.baseEntryPath);\n await build({\n configFile: false,\n envDir: false,\n logLevel: 'warn',\n root: stylesRoot,\n base: './',\n plugins: [tailwindcss()],\n resolve: {\n alias: [{ find: /^\\.\\/webPluginSources\\.generated\\.css$/, replacement: styles.baseSourcesPath }],\n },\n build: {\n cssCodeSplit: true,\n outDir: pluginsDir,\n emptyOutDir: false,\n sourcemap: false,\n assetsInlineLimit: 0,\n manifest: 'styles.manifest.json',\n rollupOptions: {\n input: Object.fromEntries([\n [BASE_STYLE_ENTRY, styles.baseEntryPath],\n ...[...styles.pluginEntryPaths].map(([slot, entry]) => [pluginStyleEntryName(slot), entry]),\n ]),\n output: { assetFileNames: 'assets/[name]-[hash][extname]' },\n },\n },\n });\n const styleManifest = JSON.parse(fs.readFileSync(path.join(pluginsDir, 'styles.manifest.json'), 'utf8')) as Record<\n string,\n { file?: string; css?: string[]; assets?: string[] }\n >;\n fs.rmSync(path.join(pluginsDir, 'styles.manifest.json'));\n const emitted = (entry: string): { file: string; assets: string[] } => {\n const record = styleManifest[path.relative(stylesRoot, entry).split(path.sep).join('/')];\n const file = record?.file?.endsWith('.css') ? record.file : record?.css?.[0];\n if (file === undefined) throw new Error(`The stylesheet entry '${entry}' emitted no CSS file.`);\n return { file, assets: record?.assets ?? [] };\n };\n const base = emitted(styles.baseEntryPath);\n const slotStyles = new Map(\n plugins.map((plugin) => {\n const entry = styles.pluginEntryPaths.get(plugin.slot);\n return [plugin.slot, entry === undefined ? base : emitted(entry)];\n }),\n );\n const pluginStyles = Object.fromEntries([...slotStyles].map(([slot, style]) => [slot, style.file]));\n fs.writeFileSync(path.join(pluginsDir, 'index.html'), '<!doctype html>\\n<title>Po plugin composition</title>\\n');\n\n return {\n assetsDir,\n pluginsDir,\n pluginIds: plugins.map((plugin) => plugin.pluginId),\n plugins: plugins.map((plugin) => {\n const closure = closures.get(plugin.slot);\n const style = slotStyles.get(plugin.slot);\n if (closure === undefined || style === undefined) throw new Error(`The slot '${plugin.slot}' emitted no entry.`);\n return {\n pluginId: plugin.pluginId,\n slot: plugin.slot,\n packageDir: plugin.packageDir,\n isHost: plugin.isHost,\n entry: closure.entry,\n // The shared stylesheet and its fonts must be verified for every slot.\n files: [...new Set([...closure.files, style.file, ...style.assets])],\n css: closure.css,\n };\n }),\n pluginStyles,\n baseStyle: base.file,\n };\n}\n"],"mappings":"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"}
|
|
@@ -52,7 +52,7 @@ function webHostVersion(poHome, environment = process.env) {
|
|
|
52
52
|
}
|
|
53
53
|
}
|
|
54
54
|
/** Bumped whenever the build's output layout changes, so an old layout is never reused. */
|
|
55
|
-
const GLOBAL_BUILD_FORMAT = "global-web-
|
|
55
|
+
const GLOBAL_BUILD_FORMAT = "global-web-5";
|
|
56
56
|
/** The record every keyed global build writes last, so a key directory holding it is complete. */
|
|
57
57
|
const BUILD_RECORD = "build.json";
|
|
58
58
|
const BUILD_CHILD_SCRIPT = `
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":["path","fs","createRequire","snapshotFiles","statSignature","crypto","compareCodeUnits","scanWebPlugins","pluginSlot"],"sources":["../../../../src/builders/web/index.ts"],"sourcesContent":["import { execFile } from 'node:child_process';\nimport crypto from 'node:crypto';\nimport fs from 'node:fs';\nimport { createRequire } from 'node:module';\nimport path from 'node:path';\n\nimport type { GlobalWebBuild, GlobalWebBuildPlugin } from '@agimon-ai/po-core/webCompositions';\n\nimport { snapshotFiles, statSignature } from '../server/packageUrl';\nimport { compareCodeUnits, type DeclaredWebPlugin, pluginSlot } from './webPluginManifest';\nimport { scanWebPlugins } from './webPluginScan';\n\nconst WEB_PACKAGE = '@agimon-ai/po-web';\nconst WEB_PACKAGE_ROOT_ENV = 'PO_WEB_PACKAGE_ROOT';\n\n/** The package roots a set of compositions activates, each once. */\nexport function compositionPackageRoots(compositions: readonly { packageEntries: readonly string[] }[]): string[] {\n return [\n ...new Set(\n compositions\n .flatMap((composition) => composition.packageEntries)\n .map(packageRootOf)\n .filter((root): root is string => root !== undefined),\n ),\n ];\n}\n\n/** Walks from a resolved module path up to the directory owning its package.json. */\nexport function packageRootOf(entryPath: string): string | undefined {\n let dir = path.dirname(entryPath);\n for (;;) {\n if (fs.existsSync(path.join(dir, 'package.json'))) return dir;\n const parent = path.dirname(dir);\n if (parent === dir) return undefined;\n dir = parent;\n }\n}\n\nfunction resolveWebPackageRoot(repoRoot: string, environment: NodeJS.ProcessEnv): string | undefined {\n const override = environment[WEB_PACKAGE_ROOT_ENV];\n if (override !== undefined && override !== '') return path.resolve(override);\n try {\n return path.dirname(createRequire(path.join(repoRoot, 'package.json')).resolve(`${WEB_PACKAGE}/package.json`));\n } catch {\n try {\n return path.dirname(createRequire(import.meta.url).resolve(`${WEB_PACKAGE}/package.json`));\n } catch {\n return undefined;\n }\n }\n}\n\n/** The installed po-web host version the global web build would use, or `none` when it is not installed. */\nexport function webHostVersion(poHome: string, environment: NodeJS.ProcessEnv = process.env): string {\n const webRoot = resolveWebPackageRoot(poHome, environment);\n if (webRoot === undefined) return 'none';\n try {\n const manifest = JSON.parse(fs.readFileSync(path.join(webRoot, 'package.json'), 'utf8')) as { version?: unknown };\n return typeof manifest.version === 'string' ? manifest.version : 'unversioned';\n } catch (error) {\n return `unreadable:${error instanceof Error ? error.message : String(error)}`;\n }\n}\n\n/** Bumped whenever the build's output layout changes, so an old layout is never reused. */\nconst GLOBAL_BUILD_FORMAT = 'global-web-4';\n/** The record every keyed global build writes last, so a key directory holding it is complete. */\nexport const BUILD_RECORD = 'build.json';\nconst BUILD_CHILD_SCRIPT = `\nconst [bundleUrl, request] = process.argv.slice(1);\ntry {\n const { bundleCockpitWeb } = await import(bundleUrl);\n const { hostRoot, pluginRoots, outDir } = JSON.parse(request);\n const result = await bundleCockpitWeb({\n hostRoot,\n pluginRoots,\n outDir,\n onNotice: (message) => process.stderr.write(message + '\\\\n'),\n });\n process.stdout.write(JSON.stringify(result));\n} catch (error) {\n process.stderr.write(error instanceof Error ? (error.stack ?? error.message) : String(error));\n process.exitCode = 1;\n}\n`;\n\n/** One build's record: the publishable build, whose plugins carry each copy's package and slot closure. */\nexport type GlobalWebBuildRecord = GlobalWebBuild;\n\nexport interface GlobalWebBuildInput {\n /** Po's global home; builds land in `<home>/build/web/<key>/`. */\n poHome: string;\n /** The union of every scope's package roots; roots without a poWeb block are ignored. */\n pluginRoots: readonly string[];\n environment?: NodeJS.ProcessEnv;\n onNotice?: (message: string) => void;\n /** Test seam over the child-process build. */\n runBuild?: (request: { hostRoot: string; pluginRoots: string[]; outDir: string }) => Promise<BuildOutput>;\n /** Test seam over package resolution. */\n resolveWebRoot?: (repoRoot: string, environment: NodeJS.ProcessEnv) => string | undefined;\n}\n\ninterface BuildOutput {\n assetsDir: string;\n pluginsDir: string;\n plugins: GlobalWebBuildPlugin[];\n pluginStyles: Record<string, string>;\n baseStyle: string;\n}\n\nexport type GlobalWebBuildResult =\n | { status: 'built' | 'cached'; build: GlobalWebBuildRecord }\n | { status: 'skipped'; reason: string }\n | { status: 'failed'; reason: string };\n\n/**\n * The size+mtime signature of every file a plugin package hands the build: its manifest\n * plus the whole top-level folder of the client entry and of each declared export, so an\n * edited import or a rebuilt dist chunk changes the key, not only the entry file.\n */\nfunction packageSignature(packageDir: string, entries: readonly string[]): string {\n try {\n const files = new Set<string>();\n for (const entry of entries) for (const file of snapshotFiles(packageDir, entry)) files.add(file);\n return statSignature(packageDir, [...files].sort());\n } catch (error) {\n // A declared export that is not built yet must not block the build; it only weakens the key.\n return `unreadable:${error instanceof Error ? error.message : String(error)}`;\n }\n}\n\n/**\n * The content key of a build: the format, the host package when there is one, then every\n * plugin package's file signature. A changed source or dist file or a changed plugin set\n * yields a new key; roots that declare no plugin do not take part.\n */\nexport function globalBuildKey(\n format: string,\n hostRoot: string | undefined,\n plugins: readonly DeclaredWebPlugin[],\n): string {\n const hash = crypto.createHash('sha256').update(format);\n if (hostRoot !== undefined)\n hash.update(`host:${hostRoot}:${fs.readFileSync(path.join(hostRoot, 'package.json'), 'utf8')}`);\n const entries = new Map<string, string[]>();\n for (const plugin of [...plugins].sort((a, b) => compareCodeUnits(a.pluginId, b.pluginId))) {\n hash.update(`\\0${plugin.pluginId}:${plugin.packageDir}:${plugin.client.entry}`);\n const list = entries.get(plugin.packageDir) ?? [];\n list.push(path.join(plugin.packageDir, plugin.client.entry));\n entries.set(plugin.packageDir, list);\n }\n for (const packageDir of [...entries.keys()].sort())\n hash.update(`\\0${packageDir}\\0${packageSignature(packageDir, entries.get(packageDir) ?? [])}`);\n return hash.digest('hex').slice(0, 32);\n}\n\nfunction bundleModuleUrl(): string {\n const extension = import.meta.url.endsWith('.ts') ? 'ts' : 'mjs';\n return new URL(`./bundle.${extension}`, import.meta.url).href;\n}\n\n/** Runs Vite in a child process so its module graph and caches never stay in the server heap. */\nfunction runBuildProcess(\n request: { hostRoot: string; pluginRoots: string[]; outDir: string },\n environment: NodeJS.ProcessEnv,\n onNotice: (message: string) => void,\n): Promise<BuildOutput> {\n const args = [\n ...process.execArgv,\n '--input-type=module',\n '--eval',\n BUILD_CHILD_SCRIPT,\n bundleModuleUrl(),\n JSON.stringify(request),\n ];\n return new Promise((resolve, reject) => {\n execFile(\n process.execPath,\n args,\n { cwd: request.hostRoot, env: environment, encoding: 'utf8', maxBuffer: 64 * 1024 * 1024 },\n (error, stdout, stderr) => {\n if (error) {\n reject(new Error(`${stderr || stdout}`.trim() || `Web build exited with code ${String(error.code ?? 1)}`));\n return;\n }\n for (const line of stderr.split('\\n')) if (line.trim()) onNotice(line.trim());\n resolve(JSON.parse(stdout) as BuildOutput);\n },\n );\n });\n}\n\n/** A key directory's parsed build record, or undefined when the build is not there (yet). */\nexport function readBuildJson<T>(directory: string): T | undefined {\n try {\n return JSON.parse(fs.readFileSync(path.join(directory, BUILD_RECORD), 'utf8')) as T;\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') return undefined;\n throw error;\n }\n}\n\nfunction readBuildRecord(directory: string): GlobalWebBuildRecord | undefined {\n const record = readBuildJson<Omit<GlobalWebBuildRecord, 'webDirectory' | 'pluginsDirectory'>>(directory);\n if (record === undefined) return undefined;\n return { ...record, webDirectory: path.join(directory, 'web'), pluginsDirectory: path.join(directory, 'plugins') };\n}\n\nfunction webBuildsRoot(poHome: string): string {\n return path.join(poHome, 'build', 'web');\n}\n\n/** The complete global web build of one key, or undefined when it is not there. Never builds. */\nexport function readGlobalWebBuildRecord(poHome: string, key: string): GlobalWebBuildRecord | undefined {\n return readBuildRecord(path.join(webBuildsRoot(poHome), key));\n}\n\n/**\n * The key of the most recently written complete global web build, for a server that starts\n * before sync ever wrote a pointer. Temporary build folders start with a dot and are skipped.\n */\nexport function newestGlobalWebBuildKey(poHome: string): string | undefined {\n const root = webBuildsRoot(poHome);\n if (!fs.existsSync(root)) return undefined;\n return fs\n .readdirSync(root, { withFileTypes: true })\n .filter((entry) => entry.isDirectory() && !entry.name.startsWith('.'))\n .map((entry) => ({ name: entry.name, file: path.join(root, entry.name, BUILD_RECORD) }))\n .filter((entry) => fs.existsSync(entry.file))\n .map((entry) => ({ name: entry.name, modified: fs.statSync(entry.file).mtimeMs }))\n .sort((left, right) => right.modified - left.modified)[0]?.name;\n}\n\n/**\n * Builds into a temporary sibling of `directory`, writes the build record last, and renames\n * the result into place, so a key directory is always complete. Losing the rename to another\n * process that finished the same key is fine: its bytes are identical.\n */\nexport async function buildKeyDirectory(\n buildsRoot: string,\n key: string,\n directory: string,\n build: (temporary: string) => Promise<object>,\n): Promise<void> {\n fs.mkdirSync(buildsRoot, { recursive: true });\n const temporary = fs.mkdtempSync(path.join(buildsRoot, `.${key}-`));\n try {\n const record = await build(temporary);\n fs.writeFileSync(path.join(temporary, BUILD_RECORD), `${JSON.stringify(record, null, 2)}\\n`);\n try {\n fs.renameSync(temporary, directory);\n } catch (error) {\n if (readBuildJson(directory) === undefined) throw error;\n }\n } finally {\n fs.rmSync(temporary, { recursive: true, force: true });\n }\n}\n\nconst pendingBuilds = new Map<string, Promise<unknown>>();\n\n/** Joins a build of the same key that is already running in this process instead of starting another. */\nexport async function singleFlightBuild<T>(key: string, run: () => Promise<T>): Promise<T> {\n const pending = pendingBuilds.get(key);\n if (pending !== undefined) return pending as Promise<T>;\n const started = run();\n pendingBuilds.set(key, started);\n try {\n return await started;\n } finally {\n pendingBuilds.delete(key);\n }\n}\n\n/**\n * Ensures the one global web build for the given plugin roots exists and returns it.\n *\n * The output is `<home>/build/web/<key>/`, built in a temporary directory and renamed into\n * place, so a key directory is always complete; an existing key builds nothing. Each\n * non-host plugin gets its own content-hashed stylesheet, so a mount loads only its own.\n */\ntype ResolvedGlobalWebBuild =\n | { status: 'resolved'; webRoot: string; pluginRoots: string[]; key: string; buildsRoot: string; directory: string }\n | { status: 'skipped'; reason: string }\n | { status: 'failed'; reason: string };\n\n/** The build key for these plugin roots and where its build lives, without building anything. */\nfunction resolveGlobalWebBuild(input: GlobalWebBuildInput): ResolvedGlobalWebBuild {\n const environment = input.environment ?? process.env;\n const webRoot = (input.resolveWebRoot ?? resolveWebPackageRoot)(input.poHome, environment);\n if (webRoot === undefined || !fs.existsSync(path.join(webRoot, 'package.json')))\n return { status: 'skipped', reason: `${WEB_PACKAGE} is not installed; no web composition is published` };\n const pluginRoots = [...new Set(input.pluginRoots.map((root) => path.resolve(root)))].sort();\n let key: string;\n try {\n key = globalBuildKey(\n GLOBAL_BUILD_FORMAT,\n webRoot,\n scanWebPlugins(webRoot, pluginRoots, input.onNotice ?? ((): void => {})),\n );\n } catch (error) {\n return { status: 'failed', reason: error instanceof Error ? error.message : String(error) };\n }\n const buildsRoot = path.join(input.poHome, 'build', 'web');\n return { status: 'resolved', webRoot, pluginRoots, key, buildsRoot, directory: path.join(buildsRoot, key) };\n}\n\n/** Whether the global build for these plugin roots exists, for a check that must not build. */\nexport function readGlobalWebBuildStatus(\n input: GlobalWebBuildInput,\n):\n | { status: 'current' | 'missing'; key: string }\n | { status: 'skipped'; reason: string }\n | { status: 'failed'; reason: string } {\n const resolved = resolveGlobalWebBuild(input);\n if (resolved.status !== 'resolved') return resolved;\n return { status: readBuildRecord(resolved.directory) === undefined ? 'missing' : 'current', key: resolved.key };\n}\n\nexport async function ensureGlobalWebBuild(input: GlobalWebBuildInput): Promise<GlobalWebBuildResult> {\n const environment = input.environment ?? process.env;\n const notice = input.onNotice ?? ((): void => {});\n const resolved = resolveGlobalWebBuild(input);\n if (resolved.status !== 'resolved') return resolved;\n const { webRoot, pluginRoots, key, buildsRoot, directory } = resolved;\n const existing = readBuildRecord(directory);\n if (existing !== undefined) return { status: 'cached', build: existing };\n return singleFlightBuild(key, async (): Promise<GlobalWebBuildResult> => {\n try {\n await buildKeyDirectory(buildsRoot, key, directory, async (temporary) => {\n const request = { hostRoot: webRoot, pluginRoots, outDir: temporary };\n const output = await (input.runBuild ?? ((value) => runBuildProcess(value, environment, notice)))(request);\n return {\n key,\n plugins: output.plugins,\n pluginStyles: output.pluginStyles,\n baseStyle: output.baseStyle,\n };\n });\n const build = readBuildRecord(directory);\n if (build === undefined) throw new Error(`The web build '${key}' was not recorded.`);\n return { status: 'built', build };\n } catch (error) {\n return { status: 'failed', reason: error instanceof Error ? error.message : String(error) };\n }\n });\n}\n\ntype MountPluginRecord = { pluginId: string; packageDir: string; isHost: boolean };\n\n/** The plugin copies a mount runs, host plugins plus its own roots', at most one per pluginId (first wins). */\nfunction mountPluginCopies(\n build: { plugins: readonly MountPluginRecord[] },\n packageRoots: readonly string[],\n): MountPluginRecord[] {\n const roots = new Set(packageRoots.map((root) => path.resolve(root)));\n const seen = new Set<string>();\n return build.plugins.filter((plugin) => {\n if (!plugin.isHost && !roots.has(path.resolve(plugin.packageDir))) return false;\n if (seen.has(plugin.pluginId)) return false;\n seen.add(plugin.pluginId);\n return true;\n });\n}\n\n/** The `pluginId` values a mount runs: the host's plugins plus those of its own package roots, deduplicated. */\nexport function mountWebPluginIds(\n build: { plugins: readonly MountPluginRecord[] },\n packageRoots: readonly string[],\n): string[] {\n return mountPluginCopies(build, packageRoots).map((plugin) => plugin.pluginId);\n}\n\n/** The slots a mount runs: one copy per pluginId, the first in build order. */\nexport function mountWebPluginSlots(\n build: { plugins: readonly MountPluginRecord[] },\n packageRoots: readonly string[],\n): string[] {\n return mountPluginCopies(build, packageRoots).map(pluginSlot);\n}\n\n/**\n * The package roots among `packageRoots` that declare a web plugin the build has no slot for:\n * a package that was not synced yet, or was left out of the build. Their names, else their roots.\n */\nexport function unslottedWebPackages(\n build: { plugins: readonly MountPluginRecord[] },\n packageRoots: readonly string[],\n): string[] {\n const slotted = new Set(build.plugins.map((plugin) => path.resolve(plugin.packageDir)));\n const missing: string[] = [];\n for (const root of new Set(packageRoots.map((entry) => path.resolve(entry)))) {\n if (slotted.has(root)) continue;\n let manifest: { name?: unknown; poWeb?: unknown };\n try {\n manifest = JSON.parse(fs.readFileSync(path.join(root, 'package.json'), 'utf8')) as typeof manifest;\n } catch {\n // Unreadable manifests are reported by the build's own scan, not as a missing slot.\n continue;\n }\n if (manifest.poWeb === undefined) continue;\n missing.push(typeof manifest.name === 'string' ? manifest.name : root);\n }\n return missing;\n}\n\n/** The pluginIds with more than one installed copy among the host and these roots, each with its slots in build order. */\nexport function duplicatePluginCopies(\n build: { plugins: readonly MountPluginRecord[] },\n packageRoots: readonly string[],\n): Map<string, string[]> {\n const roots = new Set(packageRoots.map((root) => path.resolve(root)));\n const slotsById = new Map<string, string[]>();\n for (const plugin of build.plugins) {\n if (!plugin.isHost && !roots.has(path.resolve(plugin.packageDir))) continue;\n const slot = pluginSlot(plugin);\n const slots = slotsById.get(plugin.pluginId) ?? [];\n if (!slots.includes(slot)) slots.push(slot);\n slotsById.set(plugin.pluginId, slots);\n }\n return new Map([...slotsById].filter(([, slots]) => slots.length > 1));\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["path","fs","createRequire","snapshotFiles","statSignature","crypto","compareCodeUnits","scanWebPlugins","pluginSlot"],"sources":["../../../../src/builders/web/index.ts"],"sourcesContent":["import { execFile } from 'node:child_process';\nimport crypto from 'node:crypto';\nimport fs from 'node:fs';\nimport { createRequire } from 'node:module';\nimport path from 'node:path';\n\nimport type { GlobalWebBuild, GlobalWebBuildPlugin } from '@agimon-ai/po-core/webCompositions';\n\nimport { snapshotFiles, statSignature } from '../server/packageUrl';\nimport { compareCodeUnits, type DeclaredWebPlugin, pluginSlot } from './webPluginManifest';\nimport { scanWebPlugins } from './webPluginScan';\n\nconst WEB_PACKAGE = '@agimon-ai/po-web';\nconst WEB_PACKAGE_ROOT_ENV = 'PO_WEB_PACKAGE_ROOT';\n\n/** The package roots a set of compositions activates, each once. */\nexport function compositionPackageRoots(compositions: readonly { packageEntries: readonly string[] }[]): string[] {\n return [\n ...new Set(\n compositions\n .flatMap((composition) => composition.packageEntries)\n .map(packageRootOf)\n .filter((root): root is string => root !== undefined),\n ),\n ];\n}\n\n/** Walks from a resolved module path up to the directory owning its package.json. */\nexport function packageRootOf(entryPath: string): string | undefined {\n let dir = path.dirname(entryPath);\n for (;;) {\n if (fs.existsSync(path.join(dir, 'package.json'))) return dir;\n const parent = path.dirname(dir);\n if (parent === dir) return undefined;\n dir = parent;\n }\n}\n\nfunction resolveWebPackageRoot(repoRoot: string, environment: NodeJS.ProcessEnv): string | undefined {\n const override = environment[WEB_PACKAGE_ROOT_ENV];\n if (override !== undefined && override !== '') return path.resolve(override);\n try {\n return path.dirname(createRequire(path.join(repoRoot, 'package.json')).resolve(`${WEB_PACKAGE}/package.json`));\n } catch {\n try {\n return path.dirname(createRequire(import.meta.url).resolve(`${WEB_PACKAGE}/package.json`));\n } catch {\n return undefined;\n }\n }\n}\n\n/** The installed po-web host version the global web build would use, or `none` when it is not installed. */\nexport function webHostVersion(poHome: string, environment: NodeJS.ProcessEnv = process.env): string {\n const webRoot = resolveWebPackageRoot(poHome, environment);\n if (webRoot === undefined) return 'none';\n try {\n const manifest = JSON.parse(fs.readFileSync(path.join(webRoot, 'package.json'), 'utf8')) as { version?: unknown };\n return typeof manifest.version === 'string' ? manifest.version : 'unversioned';\n } catch (error) {\n return `unreadable:${error instanceof Error ? error.message : String(error)}`;\n }\n}\n\n/** Bumped whenever the build's output layout changes, so an old layout is never reused. */\nconst GLOBAL_BUILD_FORMAT = 'global-web-5';\n/** The record every keyed global build writes last, so a key directory holding it is complete. */\nexport const BUILD_RECORD = 'build.json';\nconst BUILD_CHILD_SCRIPT = `\nconst [bundleUrl, request] = process.argv.slice(1);\ntry {\n const { bundleCockpitWeb } = await import(bundleUrl);\n const { hostRoot, pluginRoots, outDir } = JSON.parse(request);\n const result = await bundleCockpitWeb({\n hostRoot,\n pluginRoots,\n outDir,\n onNotice: (message) => process.stderr.write(message + '\\\\n'),\n });\n process.stdout.write(JSON.stringify(result));\n} catch (error) {\n process.stderr.write(error instanceof Error ? (error.stack ?? error.message) : String(error));\n process.exitCode = 1;\n}\n`;\n\n/** One build's record: the publishable build, whose plugins carry each copy's package and slot closure. */\nexport type GlobalWebBuildRecord = GlobalWebBuild;\n\nexport interface GlobalWebBuildInput {\n /** Po's global home; builds land in `<home>/build/web/<key>/`. */\n poHome: string;\n /** The union of every scope's package roots; roots without a poWeb block are ignored. */\n pluginRoots: readonly string[];\n environment?: NodeJS.ProcessEnv;\n onNotice?: (message: string) => void;\n /** Test seam over the child-process build. */\n runBuild?: (request: { hostRoot: string; pluginRoots: string[]; outDir: string }) => Promise<BuildOutput>;\n /** Test seam over package resolution. */\n resolveWebRoot?: (repoRoot: string, environment: NodeJS.ProcessEnv) => string | undefined;\n}\n\ninterface BuildOutput {\n assetsDir: string;\n pluginsDir: string;\n plugins: GlobalWebBuildPlugin[];\n pluginStyles: Record<string, string>;\n baseStyle: string;\n}\n\nexport type GlobalWebBuildResult =\n | { status: 'built' | 'cached'; build: GlobalWebBuildRecord }\n | { status: 'skipped'; reason: string }\n | { status: 'failed'; reason: string };\n\n/**\n * The size+mtime signature of every file a plugin package hands the build: its manifest\n * plus the whole top-level folder of the client entry and of each declared export, so an\n * edited import or a rebuilt dist chunk changes the key, not only the entry file.\n */\nfunction packageSignature(packageDir: string, entries: readonly string[]): string {\n try {\n const files = new Set<string>();\n for (const entry of entries) for (const file of snapshotFiles(packageDir, entry)) files.add(file);\n return statSignature(packageDir, [...files].sort());\n } catch (error) {\n // A declared export that is not built yet must not block the build; it only weakens the key.\n return `unreadable:${error instanceof Error ? error.message : String(error)}`;\n }\n}\n\n/**\n * The content key of a build: the format, the host package when there is one, then every\n * plugin package's file signature. A changed source or dist file or a changed plugin set\n * yields a new key; roots that declare no plugin do not take part.\n */\nexport function globalBuildKey(\n format: string,\n hostRoot: string | undefined,\n plugins: readonly DeclaredWebPlugin[],\n): string {\n const hash = crypto.createHash('sha256').update(format);\n if (hostRoot !== undefined)\n hash.update(`host:${hostRoot}:${fs.readFileSync(path.join(hostRoot, 'package.json'), 'utf8')}`);\n const entries = new Map<string, string[]>();\n for (const plugin of [...plugins].sort((a, b) => compareCodeUnits(a.pluginId, b.pluginId))) {\n hash.update(`\\0${plugin.pluginId}:${plugin.packageDir}:${plugin.client.entry}`);\n const list = entries.get(plugin.packageDir) ?? [];\n list.push(path.join(plugin.packageDir, plugin.client.entry));\n entries.set(plugin.packageDir, list);\n }\n for (const packageDir of [...entries.keys()].sort())\n hash.update(`\\0${packageDir}\\0${packageSignature(packageDir, entries.get(packageDir) ?? [])}`);\n return hash.digest('hex').slice(0, 32);\n}\n\nfunction bundleModuleUrl(): string {\n const extension = import.meta.url.endsWith('.ts') ? 'ts' : 'mjs';\n return new URL(`./bundle.${extension}`, import.meta.url).href;\n}\n\n/** Runs Vite in a child process so its module graph and caches never stay in the server heap. */\nfunction runBuildProcess(\n request: { hostRoot: string; pluginRoots: string[]; outDir: string },\n environment: NodeJS.ProcessEnv,\n onNotice: (message: string) => void,\n): Promise<BuildOutput> {\n const args = [\n ...process.execArgv,\n '--input-type=module',\n '--eval',\n BUILD_CHILD_SCRIPT,\n bundleModuleUrl(),\n JSON.stringify(request),\n ];\n return new Promise((resolve, reject) => {\n execFile(\n process.execPath,\n args,\n { cwd: request.hostRoot, env: environment, encoding: 'utf8', maxBuffer: 64 * 1024 * 1024 },\n (error, stdout, stderr) => {\n if (error) {\n reject(new Error(`${stderr || stdout}`.trim() || `Web build exited with code ${String(error.code ?? 1)}`));\n return;\n }\n for (const line of stderr.split('\\n')) if (line.trim()) onNotice(line.trim());\n resolve(JSON.parse(stdout) as BuildOutput);\n },\n );\n });\n}\n\n/** A key directory's parsed build record, or undefined when the build is not there (yet). */\nexport function readBuildJson<T>(directory: string): T | undefined {\n try {\n return JSON.parse(fs.readFileSync(path.join(directory, BUILD_RECORD), 'utf8')) as T;\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') return undefined;\n throw error;\n }\n}\n\nfunction readBuildRecord(directory: string): GlobalWebBuildRecord | undefined {\n const record = readBuildJson<Omit<GlobalWebBuildRecord, 'webDirectory' | 'pluginsDirectory'>>(directory);\n if (record === undefined) return undefined;\n return { ...record, webDirectory: path.join(directory, 'web'), pluginsDirectory: path.join(directory, 'plugins') };\n}\n\nfunction webBuildsRoot(poHome: string): string {\n return path.join(poHome, 'build', 'web');\n}\n\n/** The complete global web build of one key, or undefined when it is not there. Never builds. */\nexport function readGlobalWebBuildRecord(poHome: string, key: string): GlobalWebBuildRecord | undefined {\n return readBuildRecord(path.join(webBuildsRoot(poHome), key));\n}\n\n/**\n * The key of the most recently written complete global web build, for a server that starts\n * before sync ever wrote a pointer. Temporary build folders start with a dot and are skipped.\n */\nexport function newestGlobalWebBuildKey(poHome: string): string | undefined {\n const root = webBuildsRoot(poHome);\n if (!fs.existsSync(root)) return undefined;\n return fs\n .readdirSync(root, { withFileTypes: true })\n .filter((entry) => entry.isDirectory() && !entry.name.startsWith('.'))\n .map((entry) => ({ name: entry.name, file: path.join(root, entry.name, BUILD_RECORD) }))\n .filter((entry) => fs.existsSync(entry.file))\n .map((entry) => ({ name: entry.name, modified: fs.statSync(entry.file).mtimeMs }))\n .sort((left, right) => right.modified - left.modified)[0]?.name;\n}\n\n/**\n * Builds into a temporary sibling of `directory`, writes the build record last, and renames\n * the result into place, so a key directory is always complete. Losing the rename to another\n * process that finished the same key is fine: its bytes are identical.\n */\nexport async function buildKeyDirectory(\n buildsRoot: string,\n key: string,\n directory: string,\n build: (temporary: string) => Promise<object>,\n): Promise<void> {\n fs.mkdirSync(buildsRoot, { recursive: true });\n const temporary = fs.mkdtempSync(path.join(buildsRoot, `.${key}-`));\n try {\n const record = await build(temporary);\n fs.writeFileSync(path.join(temporary, BUILD_RECORD), `${JSON.stringify(record, null, 2)}\\n`);\n try {\n fs.renameSync(temporary, directory);\n } catch (error) {\n if (readBuildJson(directory) === undefined) throw error;\n }\n } finally {\n fs.rmSync(temporary, { recursive: true, force: true });\n }\n}\n\nconst pendingBuilds = new Map<string, Promise<unknown>>();\n\n/** Joins a build of the same key that is already running in this process instead of starting another. */\nexport async function singleFlightBuild<T>(key: string, run: () => Promise<T>): Promise<T> {\n const pending = pendingBuilds.get(key);\n if (pending !== undefined) return pending as Promise<T>;\n const started = run();\n pendingBuilds.set(key, started);\n try {\n return await started;\n } finally {\n pendingBuilds.delete(key);\n }\n}\n\n/**\n * Ensures the one global web build for the given plugin roots exists and returns it.\n *\n * The output is `<home>/build/web/<key>/`, built in a temporary directory and renamed into\n * place, so a key directory is always complete; an existing key builds nothing. Each\n * plugin shares one content-hashed Tailwind stylesheet with the shell and fallback template.\n */\ntype ResolvedGlobalWebBuild =\n | { status: 'resolved'; webRoot: string; pluginRoots: string[]; key: string; buildsRoot: string; directory: string }\n | { status: 'skipped'; reason: string }\n | { status: 'failed'; reason: string };\n\n/** The build key for these plugin roots and where its build lives, without building anything. */\nfunction resolveGlobalWebBuild(input: GlobalWebBuildInput): ResolvedGlobalWebBuild {\n const environment = input.environment ?? process.env;\n const webRoot = (input.resolveWebRoot ?? resolveWebPackageRoot)(input.poHome, environment);\n if (webRoot === undefined || !fs.existsSync(path.join(webRoot, 'package.json')))\n return { status: 'skipped', reason: `${WEB_PACKAGE} is not installed; no web composition is published` };\n const pluginRoots = [...new Set(input.pluginRoots.map((root) => path.resolve(root)))].sort();\n let key: string;\n try {\n key = globalBuildKey(\n GLOBAL_BUILD_FORMAT,\n webRoot,\n scanWebPlugins(webRoot, pluginRoots, input.onNotice ?? ((): void => {})),\n );\n } catch (error) {\n return { status: 'failed', reason: error instanceof Error ? error.message : String(error) };\n }\n const buildsRoot = path.join(input.poHome, 'build', 'web');\n return { status: 'resolved', webRoot, pluginRoots, key, buildsRoot, directory: path.join(buildsRoot, key) };\n}\n\n/** Whether the global build for these plugin roots exists, for a check that must not build. */\nexport function readGlobalWebBuildStatus(\n input: GlobalWebBuildInput,\n):\n | { status: 'current' | 'missing'; key: string }\n | { status: 'skipped'; reason: string }\n | { status: 'failed'; reason: string } {\n const resolved = resolveGlobalWebBuild(input);\n if (resolved.status !== 'resolved') return resolved;\n return { status: readBuildRecord(resolved.directory) === undefined ? 'missing' : 'current', key: resolved.key };\n}\n\nexport async function ensureGlobalWebBuild(input: GlobalWebBuildInput): Promise<GlobalWebBuildResult> {\n const environment = input.environment ?? process.env;\n const notice = input.onNotice ?? ((): void => {});\n const resolved = resolveGlobalWebBuild(input);\n if (resolved.status !== 'resolved') return resolved;\n const { webRoot, pluginRoots, key, buildsRoot, directory } = resolved;\n const existing = readBuildRecord(directory);\n if (existing !== undefined) return { status: 'cached', build: existing };\n return singleFlightBuild(key, async (): Promise<GlobalWebBuildResult> => {\n try {\n await buildKeyDirectory(buildsRoot, key, directory, async (temporary) => {\n const request = { hostRoot: webRoot, pluginRoots, outDir: temporary };\n const output = await (input.runBuild ?? ((value) => runBuildProcess(value, environment, notice)))(request);\n return {\n key,\n plugins: output.plugins,\n pluginStyles: output.pluginStyles,\n baseStyle: output.baseStyle,\n };\n });\n const build = readBuildRecord(directory);\n if (build === undefined) throw new Error(`The web build '${key}' was not recorded.`);\n return { status: 'built', build };\n } catch (error) {\n return { status: 'failed', reason: error instanceof Error ? error.message : String(error) };\n }\n });\n}\n\ntype MountPluginRecord = { pluginId: string; packageDir: string; isHost: boolean };\n\n/** The plugin copies a mount runs, host plugins plus its own roots', at most one per pluginId (first wins). */\nfunction mountPluginCopies(\n build: { plugins: readonly MountPluginRecord[] },\n packageRoots: readonly string[],\n): MountPluginRecord[] {\n const roots = new Set(packageRoots.map((root) => path.resolve(root)));\n const seen = new Set<string>();\n return build.plugins.filter((plugin) => {\n if (!plugin.isHost && !roots.has(path.resolve(plugin.packageDir))) return false;\n if (seen.has(plugin.pluginId)) return false;\n seen.add(plugin.pluginId);\n return true;\n });\n}\n\n/** The `pluginId` values a mount runs: the host's plugins plus those of its own package roots, deduplicated. */\nexport function mountWebPluginIds(\n build: { plugins: readonly MountPluginRecord[] },\n packageRoots: readonly string[],\n): string[] {\n return mountPluginCopies(build, packageRoots).map((plugin) => plugin.pluginId);\n}\n\n/** The slots a mount runs: one copy per pluginId, the first in build order. */\nexport function mountWebPluginSlots(\n build: { plugins: readonly MountPluginRecord[] },\n packageRoots: readonly string[],\n): string[] {\n return mountPluginCopies(build, packageRoots).map(pluginSlot);\n}\n\n/**\n * The package roots among `packageRoots` that declare a web plugin the build has no slot for:\n * a package that was not synced yet, or was left out of the build. Their names, else their roots.\n */\nexport function unslottedWebPackages(\n build: { plugins: readonly MountPluginRecord[] },\n packageRoots: readonly string[],\n): string[] {\n const slotted = new Set(build.plugins.map((plugin) => path.resolve(plugin.packageDir)));\n const missing: string[] = [];\n for (const root of new Set(packageRoots.map((entry) => path.resolve(entry)))) {\n if (slotted.has(root)) continue;\n let manifest: { name?: unknown; poWeb?: unknown };\n try {\n manifest = JSON.parse(fs.readFileSync(path.join(root, 'package.json'), 'utf8')) as typeof manifest;\n } catch {\n // Unreadable manifests are reported by the build's own scan, not as a missing slot.\n continue;\n }\n if (manifest.poWeb === undefined) continue;\n missing.push(typeof manifest.name === 'string' ? manifest.name : root);\n }\n return missing;\n}\n\n/** The pluginIds with more than one installed copy among the host and these roots, each with its slots in build order. */\nexport function duplicatePluginCopies(\n build: { plugins: readonly MountPluginRecord[] },\n packageRoots: readonly string[],\n): Map<string, string[]> {\n const roots = new Set(packageRoots.map((root) => path.resolve(root)));\n const slotsById = new Map<string, string[]>();\n for (const plugin of build.plugins) {\n if (!plugin.isHost && !roots.has(path.resolve(plugin.packageDir))) continue;\n const slot = pluginSlot(plugin);\n const slots = slotsById.get(plugin.pluginId) ?? [];\n if (!slots.includes(slot)) slots.push(slot);\n slotsById.set(plugin.pluginId, slots);\n }\n return new Map([...slotsById].filter(([, slots]) => slots.length > 1));\n}\n"],"mappings":"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"}
|
|
@@ -48,7 +48,7 @@ function webHostVersion(poHome, environment = process.env) {
|
|
|
48
48
|
}
|
|
49
49
|
}
|
|
50
50
|
/** Bumped whenever the build's output layout changes, so an old layout is never reused. */
|
|
51
|
-
const GLOBAL_BUILD_FORMAT = "global-web-
|
|
51
|
+
const GLOBAL_BUILD_FORMAT = "global-web-5";
|
|
52
52
|
/** The record every keyed global build writes last, so a key directory holding it is complete. */
|
|
53
53
|
const BUILD_RECORD = "build.json";
|
|
54
54
|
const BUILD_CHILD_SCRIPT = `
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../../../../src/builders/web/index.ts"],"sourcesContent":["import { execFile } from 'node:child_process';\nimport crypto from 'node:crypto';\nimport fs from 'node:fs';\nimport { createRequire } from 'node:module';\nimport path from 'node:path';\n\nimport type { GlobalWebBuild, GlobalWebBuildPlugin } from '@agimon-ai/po-core/webCompositions';\n\nimport { snapshotFiles, statSignature } from '../server/packageUrl';\nimport { compareCodeUnits, type DeclaredWebPlugin, pluginSlot } from './webPluginManifest';\nimport { scanWebPlugins } from './webPluginScan';\n\nconst WEB_PACKAGE = '@agimon-ai/po-web';\nconst WEB_PACKAGE_ROOT_ENV = 'PO_WEB_PACKAGE_ROOT';\n\n/** The package roots a set of compositions activates, each once. */\nexport function compositionPackageRoots(compositions: readonly { packageEntries: readonly string[] }[]): string[] {\n return [\n ...new Set(\n compositions\n .flatMap((composition) => composition.packageEntries)\n .map(packageRootOf)\n .filter((root): root is string => root !== undefined),\n ),\n ];\n}\n\n/** Walks from a resolved module path up to the directory owning its package.json. */\nexport function packageRootOf(entryPath: string): string | undefined {\n let dir = path.dirname(entryPath);\n for (;;) {\n if (fs.existsSync(path.join(dir, 'package.json'))) return dir;\n const parent = path.dirname(dir);\n if (parent === dir) return undefined;\n dir = parent;\n }\n}\n\nfunction resolveWebPackageRoot(repoRoot: string, environment: NodeJS.ProcessEnv): string | undefined {\n const override = environment[WEB_PACKAGE_ROOT_ENV];\n if (override !== undefined && override !== '') return path.resolve(override);\n try {\n return path.dirname(createRequire(path.join(repoRoot, 'package.json')).resolve(`${WEB_PACKAGE}/package.json`));\n } catch {\n try {\n return path.dirname(createRequire(import.meta.url).resolve(`${WEB_PACKAGE}/package.json`));\n } catch {\n return undefined;\n }\n }\n}\n\n/** The installed po-web host version the global web build would use, or `none` when it is not installed. */\nexport function webHostVersion(poHome: string, environment: NodeJS.ProcessEnv = process.env): string {\n const webRoot = resolveWebPackageRoot(poHome, environment);\n if (webRoot === undefined) return 'none';\n try {\n const manifest = JSON.parse(fs.readFileSync(path.join(webRoot, 'package.json'), 'utf8')) as { version?: unknown };\n return typeof manifest.version === 'string' ? manifest.version : 'unversioned';\n } catch (error) {\n return `unreadable:${error instanceof Error ? error.message : String(error)}`;\n }\n}\n\n/** Bumped whenever the build's output layout changes, so an old layout is never reused. */\nconst GLOBAL_BUILD_FORMAT = 'global-web-4';\n/** The record every keyed global build writes last, so a key directory holding it is complete. */\nexport const BUILD_RECORD = 'build.json';\nconst BUILD_CHILD_SCRIPT = `\nconst [bundleUrl, request] = process.argv.slice(1);\ntry {\n const { bundleCockpitWeb } = await import(bundleUrl);\n const { hostRoot, pluginRoots, outDir } = JSON.parse(request);\n const result = await bundleCockpitWeb({\n hostRoot,\n pluginRoots,\n outDir,\n onNotice: (message) => process.stderr.write(message + '\\\\n'),\n });\n process.stdout.write(JSON.stringify(result));\n} catch (error) {\n process.stderr.write(error instanceof Error ? (error.stack ?? error.message) : String(error));\n process.exitCode = 1;\n}\n`;\n\n/** One build's record: the publishable build, whose plugins carry each copy's package and slot closure. */\nexport type GlobalWebBuildRecord = GlobalWebBuild;\n\nexport interface GlobalWebBuildInput {\n /** Po's global home; builds land in `<home>/build/web/<key>/`. */\n poHome: string;\n /** The union of every scope's package roots; roots without a poWeb block are ignored. */\n pluginRoots: readonly string[];\n environment?: NodeJS.ProcessEnv;\n onNotice?: (message: string) => void;\n /** Test seam over the child-process build. */\n runBuild?: (request: { hostRoot: string; pluginRoots: string[]; outDir: string }) => Promise<BuildOutput>;\n /** Test seam over package resolution. */\n resolveWebRoot?: (repoRoot: string, environment: NodeJS.ProcessEnv) => string | undefined;\n}\n\ninterface BuildOutput {\n assetsDir: string;\n pluginsDir: string;\n plugins: GlobalWebBuildPlugin[];\n pluginStyles: Record<string, string>;\n baseStyle: string;\n}\n\nexport type GlobalWebBuildResult =\n | { status: 'built' | 'cached'; build: GlobalWebBuildRecord }\n | { status: 'skipped'; reason: string }\n | { status: 'failed'; reason: string };\n\n/**\n * The size+mtime signature of every file a plugin package hands the build: its manifest\n * plus the whole top-level folder of the client entry and of each declared export, so an\n * edited import or a rebuilt dist chunk changes the key, not only the entry file.\n */\nfunction packageSignature(packageDir: string, entries: readonly string[]): string {\n try {\n const files = new Set<string>();\n for (const entry of entries) for (const file of snapshotFiles(packageDir, entry)) files.add(file);\n return statSignature(packageDir, [...files].sort());\n } catch (error) {\n // A declared export that is not built yet must not block the build; it only weakens the key.\n return `unreadable:${error instanceof Error ? error.message : String(error)}`;\n }\n}\n\n/**\n * The content key of a build: the format, the host package when there is one, then every\n * plugin package's file signature. A changed source or dist file or a changed plugin set\n * yields a new key; roots that declare no plugin do not take part.\n */\nexport function globalBuildKey(\n format: string,\n hostRoot: string | undefined,\n plugins: readonly DeclaredWebPlugin[],\n): string {\n const hash = crypto.createHash('sha256').update(format);\n if (hostRoot !== undefined)\n hash.update(`host:${hostRoot}:${fs.readFileSync(path.join(hostRoot, 'package.json'), 'utf8')}`);\n const entries = new Map<string, string[]>();\n for (const plugin of [...plugins].sort((a, b) => compareCodeUnits(a.pluginId, b.pluginId))) {\n hash.update(`\\0${plugin.pluginId}:${plugin.packageDir}:${plugin.client.entry}`);\n const list = entries.get(plugin.packageDir) ?? [];\n list.push(path.join(plugin.packageDir, plugin.client.entry));\n entries.set(plugin.packageDir, list);\n }\n for (const packageDir of [...entries.keys()].sort())\n hash.update(`\\0${packageDir}\\0${packageSignature(packageDir, entries.get(packageDir) ?? [])}`);\n return hash.digest('hex').slice(0, 32);\n}\n\nfunction bundleModuleUrl(): string {\n const extension = import.meta.url.endsWith('.ts') ? 'ts' : 'mjs';\n return new URL(`./bundle.${extension}`, import.meta.url).href;\n}\n\n/** Runs Vite in a child process so its module graph and caches never stay in the server heap. */\nfunction runBuildProcess(\n request: { hostRoot: string; pluginRoots: string[]; outDir: string },\n environment: NodeJS.ProcessEnv,\n onNotice: (message: string) => void,\n): Promise<BuildOutput> {\n const args = [\n ...process.execArgv,\n '--input-type=module',\n '--eval',\n BUILD_CHILD_SCRIPT,\n bundleModuleUrl(),\n JSON.stringify(request),\n ];\n return new Promise((resolve, reject) => {\n execFile(\n process.execPath,\n args,\n { cwd: request.hostRoot, env: environment, encoding: 'utf8', maxBuffer: 64 * 1024 * 1024 },\n (error, stdout, stderr) => {\n if (error) {\n reject(new Error(`${stderr || stdout}`.trim() || `Web build exited with code ${String(error.code ?? 1)}`));\n return;\n }\n for (const line of stderr.split('\\n')) if (line.trim()) onNotice(line.trim());\n resolve(JSON.parse(stdout) as BuildOutput);\n },\n );\n });\n}\n\n/** A key directory's parsed build record, or undefined when the build is not there (yet). */\nexport function readBuildJson<T>(directory: string): T | undefined {\n try {\n return JSON.parse(fs.readFileSync(path.join(directory, BUILD_RECORD), 'utf8')) as T;\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') return undefined;\n throw error;\n }\n}\n\nfunction readBuildRecord(directory: string): GlobalWebBuildRecord | undefined {\n const record = readBuildJson<Omit<GlobalWebBuildRecord, 'webDirectory' | 'pluginsDirectory'>>(directory);\n if (record === undefined) return undefined;\n return { ...record, webDirectory: path.join(directory, 'web'), pluginsDirectory: path.join(directory, 'plugins') };\n}\n\nfunction webBuildsRoot(poHome: string): string {\n return path.join(poHome, 'build', 'web');\n}\n\n/** The complete global web build of one key, or undefined when it is not there. Never builds. */\nexport function readGlobalWebBuildRecord(poHome: string, key: string): GlobalWebBuildRecord | undefined {\n return readBuildRecord(path.join(webBuildsRoot(poHome), key));\n}\n\n/**\n * The key of the most recently written complete global web build, for a server that starts\n * before sync ever wrote a pointer. Temporary build folders start with a dot and are skipped.\n */\nexport function newestGlobalWebBuildKey(poHome: string): string | undefined {\n const root = webBuildsRoot(poHome);\n if (!fs.existsSync(root)) return undefined;\n return fs\n .readdirSync(root, { withFileTypes: true })\n .filter((entry) => entry.isDirectory() && !entry.name.startsWith('.'))\n .map((entry) => ({ name: entry.name, file: path.join(root, entry.name, BUILD_RECORD) }))\n .filter((entry) => fs.existsSync(entry.file))\n .map((entry) => ({ name: entry.name, modified: fs.statSync(entry.file).mtimeMs }))\n .sort((left, right) => right.modified - left.modified)[0]?.name;\n}\n\n/**\n * Builds into a temporary sibling of `directory`, writes the build record last, and renames\n * the result into place, so a key directory is always complete. Losing the rename to another\n * process that finished the same key is fine: its bytes are identical.\n */\nexport async function buildKeyDirectory(\n buildsRoot: string,\n key: string,\n directory: string,\n build: (temporary: string) => Promise<object>,\n): Promise<void> {\n fs.mkdirSync(buildsRoot, { recursive: true });\n const temporary = fs.mkdtempSync(path.join(buildsRoot, `.${key}-`));\n try {\n const record = await build(temporary);\n fs.writeFileSync(path.join(temporary, BUILD_RECORD), `${JSON.stringify(record, null, 2)}\\n`);\n try {\n fs.renameSync(temporary, directory);\n } catch (error) {\n if (readBuildJson(directory) === undefined) throw error;\n }\n } finally {\n fs.rmSync(temporary, { recursive: true, force: true });\n }\n}\n\nconst pendingBuilds = new Map<string, Promise<unknown>>();\n\n/** Joins a build of the same key that is already running in this process instead of starting another. */\nexport async function singleFlightBuild<T>(key: string, run: () => Promise<T>): Promise<T> {\n const pending = pendingBuilds.get(key);\n if (pending !== undefined) return pending as Promise<T>;\n const started = run();\n pendingBuilds.set(key, started);\n try {\n return await started;\n } finally {\n pendingBuilds.delete(key);\n }\n}\n\n/**\n * Ensures the one global web build for the given plugin roots exists and returns it.\n *\n * The output is `<home>/build/web/<key>/`, built in a temporary directory and renamed into\n * place, so a key directory is always complete; an existing key builds nothing. Each\n * non-host plugin gets its own content-hashed stylesheet, so a mount loads only its own.\n */\ntype ResolvedGlobalWebBuild =\n | { status: 'resolved'; webRoot: string; pluginRoots: string[]; key: string; buildsRoot: string; directory: string }\n | { status: 'skipped'; reason: string }\n | { status: 'failed'; reason: string };\n\n/** The build key for these plugin roots and where its build lives, without building anything. */\nfunction resolveGlobalWebBuild(input: GlobalWebBuildInput): ResolvedGlobalWebBuild {\n const environment = input.environment ?? process.env;\n const webRoot = (input.resolveWebRoot ?? resolveWebPackageRoot)(input.poHome, environment);\n if (webRoot === undefined || !fs.existsSync(path.join(webRoot, 'package.json')))\n return { status: 'skipped', reason: `${WEB_PACKAGE} is not installed; no web composition is published` };\n const pluginRoots = [...new Set(input.pluginRoots.map((root) => path.resolve(root)))].sort();\n let key: string;\n try {\n key = globalBuildKey(\n GLOBAL_BUILD_FORMAT,\n webRoot,\n scanWebPlugins(webRoot, pluginRoots, input.onNotice ?? ((): void => {})),\n );\n } catch (error) {\n return { status: 'failed', reason: error instanceof Error ? error.message : String(error) };\n }\n const buildsRoot = path.join(input.poHome, 'build', 'web');\n return { status: 'resolved', webRoot, pluginRoots, key, buildsRoot, directory: path.join(buildsRoot, key) };\n}\n\n/** Whether the global build for these plugin roots exists, for a check that must not build. */\nexport function readGlobalWebBuildStatus(\n input: GlobalWebBuildInput,\n):\n | { status: 'current' | 'missing'; key: string }\n | { status: 'skipped'; reason: string }\n | { status: 'failed'; reason: string } {\n const resolved = resolveGlobalWebBuild(input);\n if (resolved.status !== 'resolved') return resolved;\n return { status: readBuildRecord(resolved.directory) === undefined ? 'missing' : 'current', key: resolved.key };\n}\n\nexport async function ensureGlobalWebBuild(input: GlobalWebBuildInput): Promise<GlobalWebBuildResult> {\n const environment = input.environment ?? process.env;\n const notice = input.onNotice ?? ((): void => {});\n const resolved = resolveGlobalWebBuild(input);\n if (resolved.status !== 'resolved') return resolved;\n const { webRoot, pluginRoots, key, buildsRoot, directory } = resolved;\n const existing = readBuildRecord(directory);\n if (existing !== undefined) return { status: 'cached', build: existing };\n return singleFlightBuild(key, async (): Promise<GlobalWebBuildResult> => {\n try {\n await buildKeyDirectory(buildsRoot, key, directory, async (temporary) => {\n const request = { hostRoot: webRoot, pluginRoots, outDir: temporary };\n const output = await (input.runBuild ?? ((value) => runBuildProcess(value, environment, notice)))(request);\n return {\n key,\n plugins: output.plugins,\n pluginStyles: output.pluginStyles,\n baseStyle: output.baseStyle,\n };\n });\n const build = readBuildRecord(directory);\n if (build === undefined) throw new Error(`The web build '${key}' was not recorded.`);\n return { status: 'built', build };\n } catch (error) {\n return { status: 'failed', reason: error instanceof Error ? error.message : String(error) };\n }\n });\n}\n\ntype MountPluginRecord = { pluginId: string; packageDir: string; isHost: boolean };\n\n/** The plugin copies a mount runs, host plugins plus its own roots', at most one per pluginId (first wins). */\nfunction mountPluginCopies(\n build: { plugins: readonly MountPluginRecord[] },\n packageRoots: readonly string[],\n): MountPluginRecord[] {\n const roots = new Set(packageRoots.map((root) => path.resolve(root)));\n const seen = new Set<string>();\n return build.plugins.filter((plugin) => {\n if (!plugin.isHost && !roots.has(path.resolve(plugin.packageDir))) return false;\n if (seen.has(plugin.pluginId)) return false;\n seen.add(plugin.pluginId);\n return true;\n });\n}\n\n/** The `pluginId` values a mount runs: the host's plugins plus those of its own package roots, deduplicated. */\nexport function mountWebPluginIds(\n build: { plugins: readonly MountPluginRecord[] },\n packageRoots: readonly string[],\n): string[] {\n return mountPluginCopies(build, packageRoots).map((plugin) => plugin.pluginId);\n}\n\n/** The slots a mount runs: one copy per pluginId, the first in build order. */\nexport function mountWebPluginSlots(\n build: { plugins: readonly MountPluginRecord[] },\n packageRoots: readonly string[],\n): string[] {\n return mountPluginCopies(build, packageRoots).map(pluginSlot);\n}\n\n/**\n * The package roots among `packageRoots` that declare a web plugin the build has no slot for:\n * a package that was not synced yet, or was left out of the build. Their names, else their roots.\n */\nexport function unslottedWebPackages(\n build: { plugins: readonly MountPluginRecord[] },\n packageRoots: readonly string[],\n): string[] {\n const slotted = new Set(build.plugins.map((plugin) => path.resolve(plugin.packageDir)));\n const missing: string[] = [];\n for (const root of new Set(packageRoots.map((entry) => path.resolve(entry)))) {\n if (slotted.has(root)) continue;\n let manifest: { name?: unknown; poWeb?: unknown };\n try {\n manifest = JSON.parse(fs.readFileSync(path.join(root, 'package.json'), 'utf8')) as typeof manifest;\n } catch {\n // Unreadable manifests are reported by the build's own scan, not as a missing slot.\n continue;\n }\n if (manifest.poWeb === undefined) continue;\n missing.push(typeof manifest.name === 'string' ? manifest.name : root);\n }\n return missing;\n}\n\n/** The pluginIds with more than one installed copy among the host and these roots, each with its slots in build order. */\nexport function duplicatePluginCopies(\n build: { plugins: readonly MountPluginRecord[] },\n packageRoots: readonly string[],\n): Map<string, string[]> {\n const roots = new Set(packageRoots.map((root) => path.resolve(root)));\n const slotsById = new Map<string, string[]>();\n for (const plugin of build.plugins) {\n if (!plugin.isHost && !roots.has(path.resolve(plugin.packageDir))) continue;\n const slot = pluginSlot(plugin);\n const slots = slotsById.get(plugin.pluginId) ?? [];\n if (!slots.includes(slot)) slots.push(slot);\n slotsById.set(plugin.pluginId, slots);\n }\n return new Map([...slotsById].filter(([, slots]) => slots.length > 1));\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../../../../src/builders/web/index.ts"],"sourcesContent":["import { execFile } from 'node:child_process';\nimport crypto from 'node:crypto';\nimport fs from 'node:fs';\nimport { createRequire } from 'node:module';\nimport path from 'node:path';\n\nimport type { GlobalWebBuild, GlobalWebBuildPlugin } from '@agimon-ai/po-core/webCompositions';\n\nimport { snapshotFiles, statSignature } from '../server/packageUrl';\nimport { compareCodeUnits, type DeclaredWebPlugin, pluginSlot } from './webPluginManifest';\nimport { scanWebPlugins } from './webPluginScan';\n\nconst WEB_PACKAGE = '@agimon-ai/po-web';\nconst WEB_PACKAGE_ROOT_ENV = 'PO_WEB_PACKAGE_ROOT';\n\n/** The package roots a set of compositions activates, each once. */\nexport function compositionPackageRoots(compositions: readonly { packageEntries: readonly string[] }[]): string[] {\n return [\n ...new Set(\n compositions\n .flatMap((composition) => composition.packageEntries)\n .map(packageRootOf)\n .filter((root): root is string => root !== undefined),\n ),\n ];\n}\n\n/** Walks from a resolved module path up to the directory owning its package.json. */\nexport function packageRootOf(entryPath: string): string | undefined {\n let dir = path.dirname(entryPath);\n for (;;) {\n if (fs.existsSync(path.join(dir, 'package.json'))) return dir;\n const parent = path.dirname(dir);\n if (parent === dir) return undefined;\n dir = parent;\n }\n}\n\nfunction resolveWebPackageRoot(repoRoot: string, environment: NodeJS.ProcessEnv): string | undefined {\n const override = environment[WEB_PACKAGE_ROOT_ENV];\n if (override !== undefined && override !== '') return path.resolve(override);\n try {\n return path.dirname(createRequire(path.join(repoRoot, 'package.json')).resolve(`${WEB_PACKAGE}/package.json`));\n } catch {\n try {\n return path.dirname(createRequire(import.meta.url).resolve(`${WEB_PACKAGE}/package.json`));\n } catch {\n return undefined;\n }\n }\n}\n\n/** The installed po-web host version the global web build would use, or `none` when it is not installed. */\nexport function webHostVersion(poHome: string, environment: NodeJS.ProcessEnv = process.env): string {\n const webRoot = resolveWebPackageRoot(poHome, environment);\n if (webRoot === undefined) return 'none';\n try {\n const manifest = JSON.parse(fs.readFileSync(path.join(webRoot, 'package.json'), 'utf8')) as { version?: unknown };\n return typeof manifest.version === 'string' ? manifest.version : 'unversioned';\n } catch (error) {\n return `unreadable:${error instanceof Error ? error.message : String(error)}`;\n }\n}\n\n/** Bumped whenever the build's output layout changes, so an old layout is never reused. */\nconst GLOBAL_BUILD_FORMAT = 'global-web-5';\n/** The record every keyed global build writes last, so a key directory holding it is complete. */\nexport const BUILD_RECORD = 'build.json';\nconst BUILD_CHILD_SCRIPT = `\nconst [bundleUrl, request] = process.argv.slice(1);\ntry {\n const { bundleCockpitWeb } = await import(bundleUrl);\n const { hostRoot, pluginRoots, outDir } = JSON.parse(request);\n const result = await bundleCockpitWeb({\n hostRoot,\n pluginRoots,\n outDir,\n onNotice: (message) => process.stderr.write(message + '\\\\n'),\n });\n process.stdout.write(JSON.stringify(result));\n} catch (error) {\n process.stderr.write(error instanceof Error ? (error.stack ?? error.message) : String(error));\n process.exitCode = 1;\n}\n`;\n\n/** One build's record: the publishable build, whose plugins carry each copy's package and slot closure. */\nexport type GlobalWebBuildRecord = GlobalWebBuild;\n\nexport interface GlobalWebBuildInput {\n /** Po's global home; builds land in `<home>/build/web/<key>/`. */\n poHome: string;\n /** The union of every scope's package roots; roots without a poWeb block are ignored. */\n pluginRoots: readonly string[];\n environment?: NodeJS.ProcessEnv;\n onNotice?: (message: string) => void;\n /** Test seam over the child-process build. */\n runBuild?: (request: { hostRoot: string; pluginRoots: string[]; outDir: string }) => Promise<BuildOutput>;\n /** Test seam over package resolution. */\n resolveWebRoot?: (repoRoot: string, environment: NodeJS.ProcessEnv) => string | undefined;\n}\n\ninterface BuildOutput {\n assetsDir: string;\n pluginsDir: string;\n plugins: GlobalWebBuildPlugin[];\n pluginStyles: Record<string, string>;\n baseStyle: string;\n}\n\nexport type GlobalWebBuildResult =\n | { status: 'built' | 'cached'; build: GlobalWebBuildRecord }\n | { status: 'skipped'; reason: string }\n | { status: 'failed'; reason: string };\n\n/**\n * The size+mtime signature of every file a plugin package hands the build: its manifest\n * plus the whole top-level folder of the client entry and of each declared export, so an\n * edited import or a rebuilt dist chunk changes the key, not only the entry file.\n */\nfunction packageSignature(packageDir: string, entries: readonly string[]): string {\n try {\n const files = new Set<string>();\n for (const entry of entries) for (const file of snapshotFiles(packageDir, entry)) files.add(file);\n return statSignature(packageDir, [...files].sort());\n } catch (error) {\n // A declared export that is not built yet must not block the build; it only weakens the key.\n return `unreadable:${error instanceof Error ? error.message : String(error)}`;\n }\n}\n\n/**\n * The content key of a build: the format, the host package when there is one, then every\n * plugin package's file signature. A changed source or dist file or a changed plugin set\n * yields a new key; roots that declare no plugin do not take part.\n */\nexport function globalBuildKey(\n format: string,\n hostRoot: string | undefined,\n plugins: readonly DeclaredWebPlugin[],\n): string {\n const hash = crypto.createHash('sha256').update(format);\n if (hostRoot !== undefined)\n hash.update(`host:${hostRoot}:${fs.readFileSync(path.join(hostRoot, 'package.json'), 'utf8')}`);\n const entries = new Map<string, string[]>();\n for (const plugin of [...plugins].sort((a, b) => compareCodeUnits(a.pluginId, b.pluginId))) {\n hash.update(`\\0${plugin.pluginId}:${plugin.packageDir}:${plugin.client.entry}`);\n const list = entries.get(plugin.packageDir) ?? [];\n list.push(path.join(plugin.packageDir, plugin.client.entry));\n entries.set(plugin.packageDir, list);\n }\n for (const packageDir of [...entries.keys()].sort())\n hash.update(`\\0${packageDir}\\0${packageSignature(packageDir, entries.get(packageDir) ?? [])}`);\n return hash.digest('hex').slice(0, 32);\n}\n\nfunction bundleModuleUrl(): string {\n const extension = import.meta.url.endsWith('.ts') ? 'ts' : 'mjs';\n return new URL(`./bundle.${extension}`, import.meta.url).href;\n}\n\n/** Runs Vite in a child process so its module graph and caches never stay in the server heap. */\nfunction runBuildProcess(\n request: { hostRoot: string; pluginRoots: string[]; outDir: string },\n environment: NodeJS.ProcessEnv,\n onNotice: (message: string) => void,\n): Promise<BuildOutput> {\n const args = [\n ...process.execArgv,\n '--input-type=module',\n '--eval',\n BUILD_CHILD_SCRIPT,\n bundleModuleUrl(),\n JSON.stringify(request),\n ];\n return new Promise((resolve, reject) => {\n execFile(\n process.execPath,\n args,\n { cwd: request.hostRoot, env: environment, encoding: 'utf8', maxBuffer: 64 * 1024 * 1024 },\n (error, stdout, stderr) => {\n if (error) {\n reject(new Error(`${stderr || stdout}`.trim() || `Web build exited with code ${String(error.code ?? 1)}`));\n return;\n }\n for (const line of stderr.split('\\n')) if (line.trim()) onNotice(line.trim());\n resolve(JSON.parse(stdout) as BuildOutput);\n },\n );\n });\n}\n\n/** A key directory's parsed build record, or undefined when the build is not there (yet). */\nexport function readBuildJson<T>(directory: string): T | undefined {\n try {\n return JSON.parse(fs.readFileSync(path.join(directory, BUILD_RECORD), 'utf8')) as T;\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') return undefined;\n throw error;\n }\n}\n\nfunction readBuildRecord(directory: string): GlobalWebBuildRecord | undefined {\n const record = readBuildJson<Omit<GlobalWebBuildRecord, 'webDirectory' | 'pluginsDirectory'>>(directory);\n if (record === undefined) return undefined;\n return { ...record, webDirectory: path.join(directory, 'web'), pluginsDirectory: path.join(directory, 'plugins') };\n}\n\nfunction webBuildsRoot(poHome: string): string {\n return path.join(poHome, 'build', 'web');\n}\n\n/** The complete global web build of one key, or undefined when it is not there. Never builds. */\nexport function readGlobalWebBuildRecord(poHome: string, key: string): GlobalWebBuildRecord | undefined {\n return readBuildRecord(path.join(webBuildsRoot(poHome), key));\n}\n\n/**\n * The key of the most recently written complete global web build, for a server that starts\n * before sync ever wrote a pointer. Temporary build folders start with a dot and are skipped.\n */\nexport function newestGlobalWebBuildKey(poHome: string): string | undefined {\n const root = webBuildsRoot(poHome);\n if (!fs.existsSync(root)) return undefined;\n return fs\n .readdirSync(root, { withFileTypes: true })\n .filter((entry) => entry.isDirectory() && !entry.name.startsWith('.'))\n .map((entry) => ({ name: entry.name, file: path.join(root, entry.name, BUILD_RECORD) }))\n .filter((entry) => fs.existsSync(entry.file))\n .map((entry) => ({ name: entry.name, modified: fs.statSync(entry.file).mtimeMs }))\n .sort((left, right) => right.modified - left.modified)[0]?.name;\n}\n\n/**\n * Builds into a temporary sibling of `directory`, writes the build record last, and renames\n * the result into place, so a key directory is always complete. Losing the rename to another\n * process that finished the same key is fine: its bytes are identical.\n */\nexport async function buildKeyDirectory(\n buildsRoot: string,\n key: string,\n directory: string,\n build: (temporary: string) => Promise<object>,\n): Promise<void> {\n fs.mkdirSync(buildsRoot, { recursive: true });\n const temporary = fs.mkdtempSync(path.join(buildsRoot, `.${key}-`));\n try {\n const record = await build(temporary);\n fs.writeFileSync(path.join(temporary, BUILD_RECORD), `${JSON.stringify(record, null, 2)}\\n`);\n try {\n fs.renameSync(temporary, directory);\n } catch (error) {\n if (readBuildJson(directory) === undefined) throw error;\n }\n } finally {\n fs.rmSync(temporary, { recursive: true, force: true });\n }\n}\n\nconst pendingBuilds = new Map<string, Promise<unknown>>();\n\n/** Joins a build of the same key that is already running in this process instead of starting another. */\nexport async function singleFlightBuild<T>(key: string, run: () => Promise<T>): Promise<T> {\n const pending = pendingBuilds.get(key);\n if (pending !== undefined) return pending as Promise<T>;\n const started = run();\n pendingBuilds.set(key, started);\n try {\n return await started;\n } finally {\n pendingBuilds.delete(key);\n }\n}\n\n/**\n * Ensures the one global web build for the given plugin roots exists and returns it.\n *\n * The output is `<home>/build/web/<key>/`, built in a temporary directory and renamed into\n * place, so a key directory is always complete; an existing key builds nothing. Each\n * plugin shares one content-hashed Tailwind stylesheet with the shell and fallback template.\n */\ntype ResolvedGlobalWebBuild =\n | { status: 'resolved'; webRoot: string; pluginRoots: string[]; key: string; buildsRoot: string; directory: string }\n | { status: 'skipped'; reason: string }\n | { status: 'failed'; reason: string };\n\n/** The build key for these plugin roots and where its build lives, without building anything. */\nfunction resolveGlobalWebBuild(input: GlobalWebBuildInput): ResolvedGlobalWebBuild {\n const environment = input.environment ?? process.env;\n const webRoot = (input.resolveWebRoot ?? resolveWebPackageRoot)(input.poHome, environment);\n if (webRoot === undefined || !fs.existsSync(path.join(webRoot, 'package.json')))\n return { status: 'skipped', reason: `${WEB_PACKAGE} is not installed; no web composition is published` };\n const pluginRoots = [...new Set(input.pluginRoots.map((root) => path.resolve(root)))].sort();\n let key: string;\n try {\n key = globalBuildKey(\n GLOBAL_BUILD_FORMAT,\n webRoot,\n scanWebPlugins(webRoot, pluginRoots, input.onNotice ?? ((): void => {})),\n );\n } catch (error) {\n return { status: 'failed', reason: error instanceof Error ? error.message : String(error) };\n }\n const buildsRoot = path.join(input.poHome, 'build', 'web');\n return { status: 'resolved', webRoot, pluginRoots, key, buildsRoot, directory: path.join(buildsRoot, key) };\n}\n\n/** Whether the global build for these plugin roots exists, for a check that must not build. */\nexport function readGlobalWebBuildStatus(\n input: GlobalWebBuildInput,\n):\n | { status: 'current' | 'missing'; key: string }\n | { status: 'skipped'; reason: string }\n | { status: 'failed'; reason: string } {\n const resolved = resolveGlobalWebBuild(input);\n if (resolved.status !== 'resolved') return resolved;\n return { status: readBuildRecord(resolved.directory) === undefined ? 'missing' : 'current', key: resolved.key };\n}\n\nexport async function ensureGlobalWebBuild(input: GlobalWebBuildInput): Promise<GlobalWebBuildResult> {\n const environment = input.environment ?? process.env;\n const notice = input.onNotice ?? ((): void => {});\n const resolved = resolveGlobalWebBuild(input);\n if (resolved.status !== 'resolved') return resolved;\n const { webRoot, pluginRoots, key, buildsRoot, directory } = resolved;\n const existing = readBuildRecord(directory);\n if (existing !== undefined) return { status: 'cached', build: existing };\n return singleFlightBuild(key, async (): Promise<GlobalWebBuildResult> => {\n try {\n await buildKeyDirectory(buildsRoot, key, directory, async (temporary) => {\n const request = { hostRoot: webRoot, pluginRoots, outDir: temporary };\n const output = await (input.runBuild ?? ((value) => runBuildProcess(value, environment, notice)))(request);\n return {\n key,\n plugins: output.plugins,\n pluginStyles: output.pluginStyles,\n baseStyle: output.baseStyle,\n };\n });\n const build = readBuildRecord(directory);\n if (build === undefined) throw new Error(`The web build '${key}' was not recorded.`);\n return { status: 'built', build };\n } catch (error) {\n return { status: 'failed', reason: error instanceof Error ? error.message : String(error) };\n }\n });\n}\n\ntype MountPluginRecord = { pluginId: string; packageDir: string; isHost: boolean };\n\n/** The plugin copies a mount runs, host plugins plus its own roots', at most one per pluginId (first wins). */\nfunction mountPluginCopies(\n build: { plugins: readonly MountPluginRecord[] },\n packageRoots: readonly string[],\n): MountPluginRecord[] {\n const roots = new Set(packageRoots.map((root) => path.resolve(root)));\n const seen = new Set<string>();\n return build.plugins.filter((plugin) => {\n if (!plugin.isHost && !roots.has(path.resolve(plugin.packageDir))) return false;\n if (seen.has(plugin.pluginId)) return false;\n seen.add(plugin.pluginId);\n return true;\n });\n}\n\n/** The `pluginId` values a mount runs: the host's plugins plus those of its own package roots, deduplicated. */\nexport function mountWebPluginIds(\n build: { plugins: readonly MountPluginRecord[] },\n packageRoots: readonly string[],\n): string[] {\n return mountPluginCopies(build, packageRoots).map((plugin) => plugin.pluginId);\n}\n\n/** The slots a mount runs: one copy per pluginId, the first in build order. */\nexport function mountWebPluginSlots(\n build: { plugins: readonly MountPluginRecord[] },\n packageRoots: readonly string[],\n): string[] {\n return mountPluginCopies(build, packageRoots).map(pluginSlot);\n}\n\n/**\n * The package roots among `packageRoots` that declare a web plugin the build has no slot for:\n * a package that was not synced yet, or was left out of the build. Their names, else their roots.\n */\nexport function unslottedWebPackages(\n build: { plugins: readonly MountPluginRecord[] },\n packageRoots: readonly string[],\n): string[] {\n const slotted = new Set(build.plugins.map((plugin) => path.resolve(plugin.packageDir)));\n const missing: string[] = [];\n for (const root of new Set(packageRoots.map((entry) => path.resolve(entry)))) {\n if (slotted.has(root)) continue;\n let manifest: { name?: unknown; poWeb?: unknown };\n try {\n manifest = JSON.parse(fs.readFileSync(path.join(root, 'package.json'), 'utf8')) as typeof manifest;\n } catch {\n // Unreadable manifests are reported by the build's own scan, not as a missing slot.\n continue;\n }\n if (manifest.poWeb === undefined) continue;\n missing.push(typeof manifest.name === 'string' ? manifest.name : root);\n }\n return missing;\n}\n\n/** The pluginIds with more than one installed copy among the host and these roots, each with its slots in build order. */\nexport function duplicatePluginCopies(\n build: { plugins: readonly MountPluginRecord[] },\n packageRoots: readonly string[],\n): Map<string, string[]> {\n const roots = new Set(packageRoots.map((root) => path.resolve(root)));\n const slotsById = new Map<string, string[]>();\n for (const plugin of build.plugins) {\n if (!plugin.isHost && !roots.has(path.resolve(plugin.packageDir))) continue;\n const slot = pluginSlot(plugin);\n const slots = slotsById.get(plugin.pluginId) ?? [];\n if (!slots.includes(slot)) slots.push(slot);\n slotsById.set(plugin.pluginId, slots);\n }\n return new Map([...slotsById].filter(([, slots]) => slots.length > 1));\n}\n"],"mappings":"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"}
|
|
@@ -109,34 +109,23 @@ function ensureBuiltinWebPluginModules({ packageRoot, check = false }) {
|
|
|
109
109
|
}
|
|
110
110
|
if (stale.length > 0) throw new Error(`Stale generated web plugin modules: ${stale.join(", ")}. Run pnpm build in po-web and commit them.`);
|
|
111
111
|
}
|
|
112
|
-
/** The
|
|
112
|
+
/** The shared stylesheet's Rollup input name. */
|
|
113
113
|
const BASE_STYLE_ENTRY = "base";
|
|
114
114
|
const pluginStyleEntryName = (slot) => `plugin-${slot}`;
|
|
115
|
-
/**
|
|
116
|
-
* One Tailwind entry per plugin: app.css with only the shell's sources plus that plugin's,
|
|
117
|
-
* so a mount that loads its plugins' files never receives another workspace's classes.
|
|
118
|
-
* Host plugins live in the shell's sources and share the base entry.
|
|
119
|
-
*/
|
|
115
|
+
/** One Tailwind entry for the whole build, so later sheets cannot override earlier variants. */
|
|
120
116
|
function writeWebPluginStyleEntries(plugins, generatedDir, packageRoot) {
|
|
121
117
|
const stylesDir = node_path.default.join(generatedDir, "styles");
|
|
122
118
|
node_fs.default.mkdirSync(stylesDir, { recursive: true });
|
|
123
119
|
const fallbackTemplate = fallbackTemplateSourceRoot(packageRoot);
|
|
124
120
|
const baseSourcesPath = node_path.default.join(generatedDir, "webPluginBaseSources.generated.css");
|
|
125
|
-
node_fs.default.writeFileSync(baseSourcesPath, renderCssModule(
|
|
121
|
+
node_fs.default.writeFileSync(baseSourcesPath, renderCssModule(plugins, [node_path.default.join(packageRoot, "src", "web"), ...fallbackTemplate === void 0 ? [] : [fallbackTemplate]]));
|
|
126
122
|
const appCss = relativeImport(node_fs.default.realpathSync(stylesDir), node_fs.default.realpathSync(node_path.default.join(packageRoot, "src", "web", "styles", "app.css")));
|
|
127
123
|
const baseEntryPath = node_path.default.join(stylesDir, `${BASE_STYLE_ENTRY}.css`);
|
|
128
124
|
node_fs.default.writeFileSync(baseEntryPath, `/* ${HEADER.slice(3)} */\n@import '${appCss}';\n`);
|
|
129
|
-
const pluginEntryPaths = /* @__PURE__ */ new Map();
|
|
130
|
-
for (const plugin of plugins) {
|
|
131
|
-
if (plugin.isHost) continue;
|
|
132
|
-
const entryPath = node_path.default.join(stylesDir, `${pluginStyleEntryName(plugin.slot)}.css`);
|
|
133
|
-
node_fs.default.writeFileSync(entryPath, `/* ${HEADER.slice(3)} */\n@import '${appCss}';\n@source "${pluginSourceRoot(plugin)}";\n`);
|
|
134
|
-
pluginEntryPaths.set(plugin.slot, entryPath);
|
|
135
|
-
}
|
|
136
125
|
return {
|
|
137
126
|
baseSourcesPath,
|
|
138
127
|
baseEntryPath,
|
|
139
|
-
pluginEntryPaths
|
|
128
|
+
pluginEntryPaths: /* @__PURE__ */ new Map()
|
|
140
129
|
};
|
|
141
130
|
}
|
|
142
131
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"webPluginGenerate.cjs","names":["path","pathToFileURL","fs","createRequire","scanWebPlugins"],"sources":["../../../../src/builders/web/webPluginGenerate.ts"],"sourcesContent":["import fs from 'node:fs';\nimport { createRequire } from 'node:module';\nimport path from 'node:path';\nimport { pathToFileURL } from 'node:url';\n\nimport type { DeclaredWebPlugin } from './webPluginManifest';\nimport { scanWebPlugins } from './webPluginScan';\n\nconst HEADER = '// Generated by po web builder. Do not edit by hand.';\n/** The template po-web renders when a composition names none, so its classes always compile. */\nconst FALLBACK_TEMPLATE_PACKAGE = '@agimon-ai/po-template-advanced';\nexport const BUILTIN_CLIENT_MODULE = path.join('src', 'web', 'app', 'webPlugins.generated.ts');\nexport const BUILTIN_CSS_MODULE = path.join('src', 'web', 'styles', 'webPluginSources.generated.css');\n\n/**\n * The import name of one plugin copy, derived from its slot so two copies of one pluginId\n * never collide. A host slot is its kebab-case pluginId and keeps the committed module's\n * names; a copy slot's '.' becomes '_', which no host name can contain.\n */\nfunction slotIdentifier(slot: string): string {\n return slot.replace(/-([a-z0-9])/g, (_, ch: string) => ch.toUpperCase()).replace(/[^A-Za-z0-9_$]/g, '_');\n}\n\nfunction relativeImport(fromDir: string, toFile: string): string {\n const spec = path.relative(fromDir, toFile).split(path.sep).join('/');\n return spec.startsWith('.') ? spec : `./${spec}`;\n}\n\nfunction renderClientModule(\n plugins: readonly DeclaredWebPlugin[],\n style: 'relative' | 'absolute',\n packageRoot: string,\n): string {\n const lines = [HEADER, \"import type { WebPluginDefinition } from '@agimon-ai/po-core/web';\"];\n const names: string[] = [];\n const fromDir = path.join(packageRoot, 'src', 'web', 'app');\n for (const plugin of plugins) {\n const name = `${slotIdentifier(plugin.slot)}Plugin`;\n names.push(name);\n const entry = path.join(plugin.packageDir, plugin.client.entry);\n // The committed builtin module keeps machine-neutral relative imports; the\n // sync-generated module lives outside src, is aliased over the committed\n // path, and resolves imports against its real location, so every entry\n // becomes an absolute file URL into the installed packages.\n const specifier =\n style === 'relative' ? relativeImport(fromDir, entry) : pathToFileURL(fs.realpathSync(entry)).href;\n lines.push(`import { webPlugin as ${name} } from '${specifier}';`);\n }\n lines.push('', `export const webPlugins: readonly WebPluginDefinition[] = [${names.join(', ')}];`, '');\n return lines.join('\\n');\n}\n\n/**\n * The directory Tailwind scans for a plugin's class names.\n *\n * `src/extensions` wins, because a plugin's components colocate with the routed\n * file that renders them, in private `_components` and `_lib` folders. The\n * legacy `src/web` root is only a fallback: `existsSync` is true for a\n * half-emptied directory holding nothing but a tsconfig, and packages have been\n * in exactly that state, so preferring it would hand Tailwind a directory with\n * no components in it and silently drop every class.\n *\n * The client entry's own directory is the last resort. It is published through\n * `src/exports/webClient.ts`, a one-line re-export, or generated outside `src`\n * entirely, so it holds no components either.\n */\nfunction pluginSourceRoot(plugin: DeclaredWebPlugin): string {\n const routingRoot = path.join(plugin.packageDir, 'src', 'extensions');\n if (fs.existsSync(routingRoot)) return routingRoot;\n const webRoot = path.join(plugin.packageDir, 'src', 'web');\n if (fs.existsSync(webRoot)) return webRoot;\n return path.dirname(path.join(plugin.packageDir, plugin.client.entry));\n}\n\n/**\n * Where Tailwind finds the fallback template's classes. app.css names it by a\n * path from its own folder into po-web's node_modules, which holds only in\n * this repository; an installed po-web has the template beside it instead,\n * so a sync build that scanned only that path shipped the cockpit without the\n * template's responsive layout. Resolving it as Node does works in both.\n */\nfunction fallbackTemplateSourceRoot(packageRoot: string): string | undefined {\n let manifest: string;\n try {\n manifest = createRequire(path.join(packageRoot, 'package.json')).resolve(\n `${FALLBACK_TEMPLATE_PACKAGE}/package.json`,\n );\n } catch {\n // Not installed: the cockpit then has no fallback template to render either.\n return undefined;\n }\n const extensions = path.join(path.dirname(manifest), 'dist', 'extensions');\n return fs.existsSync(extensions) ? extensions : undefined;\n}\n\nfunction renderCssModule(plugins: readonly DeclaredWebPlugin[], leadingSourceRoots: readonly string[]): string {\n // Sync CSS is compiled from a generated directory, so Tailwind cannot infer\n // the stable shell tree from app.css. Its caller names that tree explicitly\n // so later plugin utilities do not override missing responsive shell utilities.\n const lines = [`/* ${HEADER.slice(3)} */`, ...leadingSourceRoots.map((root) => `@source \"${root}\";`)];\n for (const plugin of plugins) {\n if (plugin.isHost) continue;\n lines.push(`@source \"${pluginSourceRoot(plugin)}\";`);\n }\n lines.push('');\n return lines.join('\\n');\n}\n\n/** The committed client registry, rendered from the host package alone. */\nexport function renderBuiltinWebPluginModules(packageRoot: string): Map<string, string> {\n const plugins = scanWebPlugins(packageRoot);\n return new Map([\n [BUILTIN_CLIENT_MODULE, renderClientModule(plugins, 'relative', packageRoot)],\n [BUILTIN_CSS_MODULE, renderCssModule(plugins, [])],\n ]);\n}\n\n/**\n * Keeps the committed client registry fresh: write-if-changed locally,\n * check-only (throw) in CI. Both vite.config.ts and tsdown.config.ts call\n * this at config-load time.\n */\nexport function ensureBuiltinWebPluginModules({\n packageRoot,\n check = false,\n}: {\n packageRoot: string;\n check?: boolean;\n}): void {\n const stale: string[] = [];\n for (const [relativePath, content] of renderBuiltinWebPluginModules(packageRoot)) {\n const filePath = path.join(packageRoot, relativePath);\n const existing = fs.existsSync(filePath) ? fs.readFileSync(filePath, 'utf8') : undefined;\n if (existing === content) continue;\n if (check) {\n stale.push(relativePath);\n continue;\n }\n fs.mkdirSync(path.dirname(filePath), { recursive: true });\n fs.writeFileSync(filePath, content);\n }\n if (stale.length > 0) {\n throw new Error(\n `Stale generated web plugin modules: ${stale.join(', ')}. Run pnpm build in po-web and commit them.`,\n );\n }\n}\n\n/** A client plugin registry and the Tailwind source list that goes with it. */\nexport interface WebPluginRegistryModules {\n clientModulePath: string;\n cssModulePath: string;\n}\n\nexport interface SyncGeneratedModules extends WebPluginRegistryModules {\n /** One entry module per slot, each re-exporting only that copy's `webPlugin`. */\n slotEntryPaths: Record<string, string>;\n /**\n * The signed shell's registry: host plugins only. Every other plugin reaches the\n * browser through its slot entry, and the global mount replaces the shell's\n * registry once it loads, so compiling them into the shell too shipped each plugin's\n * code and emitted assets (the voice VAD model among them) a second time.\n */\n shell: WebPluginRegistryModules;\n}\n\nexport interface WebPluginStyleEntries {\n /** Shell and fallback-template sources only, aliased over app.css's plugin source list. */\n baseSourcesPath: string;\n /** The stylesheet every plugin entry extends; a mount with no plugin styles loads it alone. */\n baseEntryPath: string;\n /** One stylesheet entry per non-host plugin copy, keyed by its slot. */\n pluginEntryPaths: Map<string, string>;\n}\n\n/** The style entry of each plugin's Rollup input name, so each emits one content-hashed file. */\nexport const BASE_STYLE_ENTRY = 'base';\nexport const pluginStyleEntryName = (slot: string): string => `plugin-${slot}`;\n\n/**\n * One Tailwind entry per plugin: app.css with only the shell's sources plus that plugin's,\n * so a mount that loads its plugins' files never receives another workspace's classes.\n * Host plugins live in the shell's sources and share the base entry.\n */\nexport function writeWebPluginStyleEntries(\n plugins: readonly DeclaredWebPlugin[],\n generatedDir: string,\n packageRoot: string,\n): WebPluginStyleEntries {\n const stylesDir = path.join(generatedDir, 'styles');\n fs.mkdirSync(stylesDir, { recursive: true });\n const fallbackTemplate = fallbackTemplateSourceRoot(packageRoot);\n const baseSourcesPath = path.join(generatedDir, 'webPluginBaseSources.generated.css');\n fs.writeFileSync(\n baseSourcesPath,\n renderCssModule(\n [],\n [path.join(packageRoot, 'src', 'web'), ...(fallbackTemplate === undefined ? [] : [fallbackTemplate])],\n ),\n );\n // Resolvers follow real paths (a temp dir under /var is /private/var), so relate real paths.\n const appCss = relativeImport(\n fs.realpathSync(stylesDir),\n fs.realpathSync(path.join(packageRoot, 'src', 'web', 'styles', 'app.css')),\n );\n const baseEntryPath = path.join(stylesDir, `${BASE_STYLE_ENTRY}.css`);\n fs.writeFileSync(baseEntryPath, `/* ${HEADER.slice(3)} */\\n@import '${appCss}';\\n`);\n const pluginEntryPaths = new Map<string, string>();\n for (const plugin of plugins) {\n if (plugin.isHost) continue;\n const entryPath = path.join(stylesDir, `${pluginStyleEntryName(plugin.slot)}.css`);\n fs.writeFileSync(\n entryPath,\n `/* ${HEADER.slice(3)} */\\n@import '${appCss}';\\n@source \"${pluginSourceRoot(plugin)}\";\\n`,\n );\n pluginEntryPaths.set(plugin.slot, entryPath);\n }\n return { baseSourcesPath, baseEntryPath, pluginEntryPaths };\n}\n\n/**\n * The sync-time variant: renders the full client plugin set (host built-ins plus\n * the installed composition's packages) into a generation directory the bundler\n * aliases over the committed builtin modules.\n */\nexport function writeSyncWebPluginModules(\n plugins: readonly DeclaredWebPlugin[],\n generatedDir: string,\n packageRoot: string,\n): SyncGeneratedModules {\n fs.mkdirSync(generatedDir, { recursive: true });\n const clientModulePath = path.join(generatedDir, 'webPlugins.generated.ts');\n const cssModulePath = path.join(generatedDir, 'webPluginSources.generated.css');\n fs.writeFileSync(clientModulePath, renderClientModule(plugins, 'absolute', packageRoot));\n // Styles are built per plugin by writeWebPluginStyleEntries, so slot entries carry none.\n const slotsDir = path.join(generatedDir, 'slots');\n fs.mkdirSync(slotsDir, { recursive: true });\n const slotEntryPaths: Record<string, string> = {};\n for (const plugin of plugins) {\n const entryPath = path.join(slotsDir, `${plugin.slot}.ts`);\n const specifier = pathToFileURL(fs.realpathSync(path.join(plugin.packageDir, plugin.client.entry))).href;\n fs.writeFileSync(entryPath, `${HEADER}\\nexport { webPlugin } from '${specifier}';\\n`);\n slotEntryPaths[plugin.slot] = entryPath;\n }\n const fallbackTemplate = fallbackTemplateSourceRoot(packageRoot);\n const leadingSourceRoots = [\n path.join(packageRoot, 'src', 'web'),\n ...(fallbackTemplate === undefined ? [] : [fallbackTemplate]),\n ];\n fs.writeFileSync(cssModulePath, renderCssModule(plugins, leadingSourceRoots));\n const hostPlugins = plugins.filter((plugin) => plugin.isHost);\n const shell = {\n clientModulePath: path.join(generatedDir, 'webPluginShell.generated.ts'),\n cssModulePath: path.join(generatedDir, 'webPluginShellSources.generated.css'),\n };\n fs.writeFileSync(shell.clientModulePath, renderClientModule(hostPlugins, 'absolute', packageRoot));\n fs.writeFileSync(shell.cssModulePath, renderCssModule(hostPlugins, leadingSourceRoots));\n return {\n clientModulePath,\n slotEntryPaths,\n cssModulePath,\n shell,\n };\n}\n"],"mappings":";;;;;;;;;AAQA,MAAM,SAAS;;AAEf,MAAM,4BAA4B;AAClC,MAAa,wBAAwBA,UAAAA,QAAK,KAAK,OAAO,OAAO,OAAO,yBAAyB;AAC7F,MAAa,qBAAqBA,UAAAA,QAAK,KAAK,OAAO,OAAO,UAAU,gCAAgC;;;;;;AAOpG,SAAS,eAAe,MAAsB;CAC5C,OAAO,KAAK,QAAQ,iBAAiB,GAAG,OAAe,GAAG,YAAY,CAAC,CAAC,CAAC,QAAQ,mBAAmB,GAAG;AACzG;AAEA,SAAS,eAAe,SAAiB,QAAwB;CAC/D,MAAM,OAAOA,UAAAA,QAAK,SAAS,SAAS,MAAM,CAAC,CAAC,MAAMA,UAAAA,QAAK,GAAG,CAAC,CAAC,KAAK,GAAG;CACpE,OAAO,KAAK,WAAW,GAAG,IAAI,OAAO,KAAK;AAC5C;AAEA,SAAS,mBACP,SACA,OACA,aACQ;CACR,MAAM,QAAQ,CAAC,QAAQ,oEAAoE;CAC3F,MAAM,QAAkB,CAAC;CACzB,MAAM,UAAUA,UAAAA,QAAK,KAAK,aAAa,OAAO,OAAO,KAAK;CAC1D,KAAK,MAAM,UAAU,SAAS;EAC5B,MAAM,OAAO,GAAG,eAAe,OAAO,IAAI,EAAE;EAC5C,MAAM,KAAK,IAAI;EACf,MAAM,QAAQA,UAAAA,QAAK,KAAK,OAAO,YAAY,OAAO,OAAO,KAAK;EAK9D,MAAM,YACJ,UAAU,aAAa,eAAe,SAAS,KAAK,KAAA,GAAIC,SAAAA,cAAAA,CAAcC,QAAAA,QAAG,aAAa,KAAK,CAAC,CAAC,CAAC;EAChG,MAAM,KAAK,yBAAyB,KAAK,WAAW,UAAU,GAAG;CACnE;CACA,MAAM,KAAK,IAAI,8DAA8D,MAAM,KAAK,IAAI,EAAE,KAAK,EAAE;CACrG,OAAO,MAAM,KAAK,IAAI;AACxB;;;;;;;;;;;;;;;AAgBA,SAAS,iBAAiB,QAAmC;CAC3D,MAAM,cAAcF,UAAAA,QAAK,KAAK,OAAO,YAAY,OAAO,YAAY;CACpE,IAAIE,QAAAA,QAAG,WAAW,WAAW,GAAG,OAAO;CACvC,MAAM,UAAUF,UAAAA,QAAK,KAAK,OAAO,YAAY,OAAO,KAAK;CACzD,IAAIE,QAAAA,QAAG,WAAW,OAAO,GAAG,OAAO;CACnC,OAAOF,UAAAA,QAAK,QAAQA,UAAAA,QAAK,KAAK,OAAO,YAAY,OAAO,OAAO,KAAK,CAAC;AACvE;;;;;;;;AASA,SAAS,2BAA2B,aAAyC;CAC3E,IAAI;CACJ,IAAI;EACF,YAAA,GAAWG,YAAAA,cAAAA,CAAcH,UAAAA,QAAK,KAAK,aAAa,cAAc,CAAC,CAAC,CAAC,QAC/D,GAAG,0BAA0B,cAC/B;CACF,QAAQ;EAEN;CACF;CACA,MAAM,aAAaA,UAAAA,QAAK,KAAKA,UAAAA,QAAK,QAAQ,QAAQ,GAAG,QAAQ,YAAY;CACzE,OAAOE,QAAAA,QAAG,WAAW,UAAU,IAAI,aAAa,KAAA;AAClD;AAEA,SAAS,gBAAgB,SAAuC,oBAA+C;CAI7G,MAAM,QAAQ,CAAC,MAAM,OAAO,MAAM,CAAC,EAAE,MAAM,GAAG,mBAAmB,KAAK,SAAS,YAAY,KAAK,GAAG,CAAC;CACpG,KAAK,MAAM,UAAU,SAAS;EAC5B,IAAI,OAAO,QAAQ;EACnB,MAAM,KAAK,YAAY,iBAAiB,MAAM,EAAE,GAAG;CACrD;CACA,MAAM,KAAK,EAAE;CACb,OAAO,MAAM,KAAK,IAAI;AACxB;;AAGA,SAAgB,8BAA8B,aAA0C;CACtF,MAAM,UAAUE,sBAAAA,eAAe,WAAW;CAC1C,uBAAO,IAAI,IAAI,CACb,CAAC,uBAAuB,mBAAmB,SAAS,YAAY,WAAW,CAAC,GAC5E,CAAC,oBAAoB,gBAAgB,SAAS,CAAC,CAAC,CAAC,CACnD,CAAC;AACH;;;;;;AAOA,SAAgB,8BAA8B,EAC5C,aACA,QAAQ,SAID;CACP,MAAM,QAAkB,CAAC;CACzB,KAAK,MAAM,CAAC,cAAc,YAAY,8BAA8B,WAAW,GAAG;EAChF,MAAM,WAAWJ,UAAAA,QAAK,KAAK,aAAa,YAAY;EAEpD,KADiBE,QAAAA,QAAG,WAAW,QAAQ,IAAIA,QAAAA,QAAG,aAAa,UAAU,MAAM,IAAI,KAAA,OAC9D,SAAS;EAC1B,IAAI,OAAO;GACT,MAAM,KAAK,YAAY;GACvB;EACF;EACA,QAAA,QAAG,UAAUF,UAAAA,QAAK,QAAQ,QAAQ,GAAG,EAAE,WAAW,KAAK,CAAC;EACxD,QAAA,QAAG,cAAc,UAAU,OAAO;CACpC;CACA,IAAI,MAAM,SAAS,GACjB,MAAM,IAAI,MACR,uCAAuC,MAAM,KAAK,IAAI,EAAE,4CAC1D;AAEJ;;AA8BA,MAAa,mBAAmB;AAChC,MAAa,wBAAwB,SAAyB,UAAU;;;;;;AAOxE,SAAgB,2BACd,SACA,cACA,aACuB;CACvB,MAAM,YAAYA,UAAAA,QAAK,KAAK,cAAc,QAAQ;CAClD,QAAA,QAAG,UAAU,WAAW,EAAE,WAAW,KAAK,CAAC;CAC3C,MAAM,mBAAmB,2BAA2B,WAAW;CAC/D,MAAM,kBAAkBA,UAAAA,QAAK,KAAK,cAAc,oCAAoC;CACpF,QAAA,QAAG,cACD,iBACA,gBACE,CAAC,GACD,CAACA,UAAAA,QAAK,KAAK,aAAa,OAAO,KAAK,GAAG,GAAI,qBAAqB,KAAA,IAAY,CAAC,IAAI,CAAC,gBAAgB,CAAE,CACtG,CACF;CAEA,MAAM,SAAS,eACbE,QAAAA,QAAG,aAAa,SAAS,GACzBA,QAAAA,QAAG,aAAaF,UAAAA,QAAK,KAAK,aAAa,OAAO,OAAO,UAAU,SAAS,CAAC,CAC3E;CACA,MAAM,gBAAgBA,UAAAA,QAAK,KAAK,WAAW,GAAG,iBAAiB,KAAK;CACpE,QAAA,QAAG,cAAc,eAAe,MAAM,OAAO,MAAM,CAAC,EAAE,gBAAgB,OAAO,KAAK;CAClF,MAAM,mCAAmB,IAAI,IAAoB;CACjD,KAAK,MAAM,UAAU,SAAS;EAC5B,IAAI,OAAO,QAAQ;EACnB,MAAM,YAAYA,UAAAA,QAAK,KAAK,WAAW,GAAG,qBAAqB,OAAO,IAAI,EAAE,KAAK;EACjF,QAAA,QAAG,cACD,WACA,MAAM,OAAO,MAAM,CAAC,EAAE,gBAAgB,OAAO,eAAe,iBAAiB,MAAM,EAAE,KACvF;EACA,iBAAiB,IAAI,OAAO,MAAM,SAAS;CAC7C;CACA,OAAO;EAAE;EAAiB;EAAe;CAAiB;AAC5D;;;;;;AAOA,SAAgB,0BACd,SACA,cACA,aACsB;CACtB,QAAA,QAAG,UAAU,cAAc,EAAE,WAAW,KAAK,CAAC;CAC9C,MAAM,mBAAmBA,UAAAA,QAAK,KAAK,cAAc,yBAAyB;CAC1E,MAAM,gBAAgBA,UAAAA,QAAK,KAAK,cAAc,gCAAgC;CAC9E,QAAA,QAAG,cAAc,kBAAkB,mBAAmB,SAAS,YAAY,WAAW,CAAC;CAEvF,MAAM,WAAWA,UAAAA,QAAK,KAAK,cAAc,OAAO;CAChD,QAAA,QAAG,UAAU,UAAU,EAAE,WAAW,KAAK,CAAC;CAC1C,MAAM,iBAAyC,CAAC;CAChD,KAAK,MAAM,UAAU,SAAS;EAC5B,MAAM,YAAYA,UAAAA,QAAK,KAAK,UAAU,GAAG,OAAO,KAAK,IAAI;EACzD,MAAM,aAAA,GAAYC,SAAAA,cAAAA,CAAcC,QAAAA,QAAG,aAAaF,UAAAA,QAAK,KAAK,OAAO,YAAY,OAAO,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC;EACpG,QAAA,QAAG,cAAc,WAAW,GAAG,OAAO,+BAA+B,UAAU,KAAK;EACpF,eAAe,OAAO,QAAQ;CAChC;CACA,MAAM,mBAAmB,2BAA2B,WAAW;CAC/D,MAAM,qBAAqB,CACzBA,UAAAA,QAAK,KAAK,aAAa,OAAO,KAAK,GACnC,GAAI,qBAAqB,KAAA,IAAY,CAAC,IAAI,CAAC,gBAAgB,CAC7D;CACA,QAAA,QAAG,cAAc,eAAe,gBAAgB,SAAS,kBAAkB,CAAC;CAC5E,MAAM,cAAc,QAAQ,QAAQ,WAAW,OAAO,MAAM;CAC5D,MAAM,QAAQ;EACZ,kBAAkBA,UAAAA,QAAK,KAAK,cAAc,6BAA6B;EACvE,eAAeA,UAAAA,QAAK,KAAK,cAAc,qCAAqC;CAC9E;CACA,QAAA,QAAG,cAAc,MAAM,kBAAkB,mBAAmB,aAAa,YAAY,WAAW,CAAC;CACjG,QAAA,QAAG,cAAc,MAAM,eAAe,gBAAgB,aAAa,kBAAkB,CAAC;CACtF,OAAO;EACL;EACA;EACA;EACA;CACF;AACF"}
|
|
1
|
+
{"version":3,"file":"webPluginGenerate.cjs","names":["path","pathToFileURL","fs","createRequire","scanWebPlugins"],"sources":["../../../../src/builders/web/webPluginGenerate.ts"],"sourcesContent":["import fs from 'node:fs';\nimport { createRequire } from 'node:module';\nimport path from 'node:path';\nimport { pathToFileURL } from 'node:url';\n\nimport type { DeclaredWebPlugin } from './webPluginManifest';\nimport { scanWebPlugins } from './webPluginScan';\n\nconst HEADER = '// Generated by po web builder. Do not edit by hand.';\n/** The template po-web renders when a composition names none, so its classes always compile. */\nconst FALLBACK_TEMPLATE_PACKAGE = '@agimon-ai/po-template-advanced';\nexport const BUILTIN_CLIENT_MODULE = path.join('src', 'web', 'app', 'webPlugins.generated.ts');\nexport const BUILTIN_CSS_MODULE = path.join('src', 'web', 'styles', 'webPluginSources.generated.css');\n\n/**\n * The import name of one plugin copy, derived from its slot so two copies of one pluginId\n * never collide. A host slot is its kebab-case pluginId and keeps the committed module's\n * names; a copy slot's '.' becomes '_', which no host name can contain.\n */\nfunction slotIdentifier(slot: string): string {\n return slot.replace(/-([a-z0-9])/g, (_, ch: string) => ch.toUpperCase()).replace(/[^A-Za-z0-9_$]/g, '_');\n}\n\nfunction relativeImport(fromDir: string, toFile: string): string {\n const spec = path.relative(fromDir, toFile).split(path.sep).join('/');\n return spec.startsWith('.') ? spec : `./${spec}`;\n}\n\nfunction renderClientModule(\n plugins: readonly DeclaredWebPlugin[],\n style: 'relative' | 'absolute',\n packageRoot: string,\n): string {\n const lines = [HEADER, \"import type { WebPluginDefinition } from '@agimon-ai/po-core/web';\"];\n const names: string[] = [];\n const fromDir = path.join(packageRoot, 'src', 'web', 'app');\n for (const plugin of plugins) {\n const name = `${slotIdentifier(plugin.slot)}Plugin`;\n names.push(name);\n const entry = path.join(plugin.packageDir, plugin.client.entry);\n // The committed builtin module keeps machine-neutral relative imports; the\n // sync-generated module lives outside src, is aliased over the committed\n // path, and resolves imports against its real location, so every entry\n // becomes an absolute file URL into the installed packages.\n const specifier =\n style === 'relative' ? relativeImport(fromDir, entry) : pathToFileURL(fs.realpathSync(entry)).href;\n lines.push(`import { webPlugin as ${name} } from '${specifier}';`);\n }\n lines.push('', `export const webPlugins: readonly WebPluginDefinition[] = [${names.join(', ')}];`, '');\n return lines.join('\\n');\n}\n\n/**\n * The directory Tailwind scans for a plugin's class names.\n *\n * `src/extensions` wins, because a plugin's components colocate with the routed\n * file that renders them, in private `_components` and `_lib` folders. The\n * legacy `src/web` root is only a fallback: `existsSync` is true for a\n * half-emptied directory holding nothing but a tsconfig, and packages have been\n * in exactly that state, so preferring it would hand Tailwind a directory with\n * no components in it and silently drop every class.\n *\n * The client entry's own directory is the last resort. It is published through\n * `src/exports/webClient.ts`, a one-line re-export, or generated outside `src`\n * entirely, so it holds no components either.\n */\nfunction pluginSourceRoot(plugin: DeclaredWebPlugin): string {\n const routingRoot = path.join(plugin.packageDir, 'src', 'extensions');\n if (fs.existsSync(routingRoot)) return routingRoot;\n const webRoot = path.join(plugin.packageDir, 'src', 'web');\n if (fs.existsSync(webRoot)) return webRoot;\n return path.dirname(path.join(plugin.packageDir, plugin.client.entry));\n}\n\n/**\n * Where Tailwind finds the fallback template's classes. app.css names it by a\n * path from its own folder into po-web's node_modules, which holds only in\n * this repository; an installed po-web has the template beside it instead,\n * so a sync build that scanned only that path shipped the cockpit without the\n * template's responsive layout. Resolving it as Node does works in both.\n */\nfunction fallbackTemplateSourceRoot(packageRoot: string): string | undefined {\n let manifest: string;\n try {\n manifest = createRequire(path.join(packageRoot, 'package.json')).resolve(\n `${FALLBACK_TEMPLATE_PACKAGE}/package.json`,\n );\n } catch {\n // Not installed: the cockpit then has no fallback template to render either.\n return undefined;\n }\n const extensions = path.join(path.dirname(manifest), 'dist', 'extensions');\n return fs.existsSync(extensions) ? extensions : undefined;\n}\n\nfunction renderCssModule(plugins: readonly DeclaredWebPlugin[], leadingSourceRoots: readonly string[]): string {\n // Sync CSS is compiled from a generated directory, so Tailwind cannot infer\n // the stable shell tree from app.css. Its caller names that tree explicitly\n // so later plugin utilities do not override missing responsive shell utilities.\n const lines = [`/* ${HEADER.slice(3)} */`, ...leadingSourceRoots.map((root) => `@source \"${root}\";`)];\n for (const plugin of plugins) {\n if (plugin.isHost) continue;\n lines.push(`@source \"${pluginSourceRoot(plugin)}\";`);\n }\n lines.push('');\n return lines.join('\\n');\n}\n\n/** The committed client registry, rendered from the host package alone. */\nexport function renderBuiltinWebPluginModules(packageRoot: string): Map<string, string> {\n const plugins = scanWebPlugins(packageRoot);\n return new Map([\n [BUILTIN_CLIENT_MODULE, renderClientModule(plugins, 'relative', packageRoot)],\n [BUILTIN_CSS_MODULE, renderCssModule(plugins, [])],\n ]);\n}\n\n/**\n * Keeps the committed client registry fresh: write-if-changed locally,\n * check-only (throw) in CI. Both vite.config.ts and tsdown.config.ts call\n * this at config-load time.\n */\nexport function ensureBuiltinWebPluginModules({\n packageRoot,\n check = false,\n}: {\n packageRoot: string;\n check?: boolean;\n}): void {\n const stale: string[] = [];\n for (const [relativePath, content] of renderBuiltinWebPluginModules(packageRoot)) {\n const filePath = path.join(packageRoot, relativePath);\n const existing = fs.existsSync(filePath) ? fs.readFileSync(filePath, 'utf8') : undefined;\n if (existing === content) continue;\n if (check) {\n stale.push(relativePath);\n continue;\n }\n fs.mkdirSync(path.dirname(filePath), { recursive: true });\n fs.writeFileSync(filePath, content);\n }\n if (stale.length > 0) {\n throw new Error(\n `Stale generated web plugin modules: ${stale.join(', ')}. Run pnpm build in po-web and commit them.`,\n );\n }\n}\n\n/** A client plugin registry and the Tailwind source list that goes with it. */\nexport interface WebPluginRegistryModules {\n clientModulePath: string;\n cssModulePath: string;\n}\n\nexport interface SyncGeneratedModules extends WebPluginRegistryModules {\n /** One entry module per slot, each re-exporting only that copy's `webPlugin`. */\n slotEntryPaths: Record<string, string>;\n /**\n * The signed shell's registry: host plugins only. Every other plugin reaches the\n * browser through its slot entry, and the global mount replaces the shell's\n * registry once it loads, so compiling them into the shell too shipped each plugin's\n * code and emitted assets (the voice VAD model among them) a second time.\n */\n shell: WebPluginRegistryModules;\n}\n\nexport interface WebPluginStyleEntries {\n /** Shell, fallback-template, and plugin sources, aliased over app.css's source list. */\n baseSourcesPath: string;\n /** The shared Tailwind entry every mount loads. */\n baseEntryPath: string;\n /** ponytail: empty compatibility field; remove only with a public webBuilder API change. */\n pluginEntryPaths: Map<string, string>;\n}\n\n/** The shared stylesheet's Rollup input name. */\nexport const BASE_STYLE_ENTRY = 'base';\nexport const pluginStyleEntryName = (slot: string): string => `plugin-${slot}`;\n\n/** One Tailwind entry for the whole build, so later sheets cannot override earlier variants. */\nexport function writeWebPluginStyleEntries(\n plugins: readonly DeclaredWebPlugin[],\n generatedDir: string,\n packageRoot: string,\n): WebPluginStyleEntries {\n const stylesDir = path.join(generatedDir, 'styles');\n fs.mkdirSync(stylesDir, { recursive: true });\n const fallbackTemplate = fallbackTemplateSourceRoot(packageRoot);\n const baseSourcesPath = path.join(generatedDir, 'webPluginBaseSources.generated.css');\n fs.writeFileSync(\n baseSourcesPath,\n renderCssModule(plugins, [\n path.join(packageRoot, 'src', 'web'),\n ...(fallbackTemplate === undefined ? [] : [fallbackTemplate]),\n ]),\n );\n // Resolvers follow real paths (a temp dir under /var is /private/var), so relate real paths.\n const appCss = relativeImport(\n fs.realpathSync(stylesDir),\n fs.realpathSync(path.join(packageRoot, 'src', 'web', 'styles', 'app.css')),\n );\n const baseEntryPath = path.join(stylesDir, `${BASE_STYLE_ENTRY}.css`);\n fs.writeFileSync(baseEntryPath, `/* ${HEADER.slice(3)} */\\n@import '${appCss}';\\n`);\n const pluginEntryPaths = new Map<string, string>();\n return { baseSourcesPath, baseEntryPath, pluginEntryPaths };\n}\n\n/**\n * The sync-time variant: renders the full client plugin set (host built-ins plus\n * the installed composition's packages) into a generation directory the bundler\n * aliases over the committed builtin modules.\n */\nexport function writeSyncWebPluginModules(\n plugins: readonly DeclaredWebPlugin[],\n generatedDir: string,\n packageRoot: string,\n): SyncGeneratedModules {\n fs.mkdirSync(generatedDir, { recursive: true });\n const clientModulePath = path.join(generatedDir, 'webPlugins.generated.ts');\n const cssModulePath = path.join(generatedDir, 'webPluginSources.generated.css');\n fs.writeFileSync(clientModulePath, renderClientModule(plugins, 'absolute', packageRoot));\n // Tailwind styles compile separately as one shared sheet, so slot entries carry none.\n const slotsDir = path.join(generatedDir, 'slots');\n fs.mkdirSync(slotsDir, { recursive: true });\n const slotEntryPaths: Record<string, string> = {};\n for (const plugin of plugins) {\n const entryPath = path.join(slotsDir, `${plugin.slot}.ts`);\n const specifier = pathToFileURL(fs.realpathSync(path.join(plugin.packageDir, plugin.client.entry))).href;\n fs.writeFileSync(entryPath, `${HEADER}\\nexport { webPlugin } from '${specifier}';\\n`);\n slotEntryPaths[plugin.slot] = entryPath;\n }\n const fallbackTemplate = fallbackTemplateSourceRoot(packageRoot);\n const leadingSourceRoots = [\n path.join(packageRoot, 'src', 'web'),\n ...(fallbackTemplate === undefined ? [] : [fallbackTemplate]),\n ];\n fs.writeFileSync(cssModulePath, renderCssModule(plugins, leadingSourceRoots));\n const hostPlugins = plugins.filter((plugin) => plugin.isHost);\n const shell = {\n clientModulePath: path.join(generatedDir, 'webPluginShell.generated.ts'),\n cssModulePath: path.join(generatedDir, 'webPluginShellSources.generated.css'),\n };\n fs.writeFileSync(shell.clientModulePath, renderClientModule(hostPlugins, 'absolute', packageRoot));\n fs.writeFileSync(shell.cssModulePath, renderCssModule(hostPlugins, leadingSourceRoots));\n return {\n clientModulePath,\n slotEntryPaths,\n cssModulePath,\n shell,\n };\n}\n"],"mappings":"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"}
|
|
@@ -30,21 +30,17 @@ export interface SyncGeneratedModules extends WebPluginRegistryModules {
|
|
|
30
30
|
shell: WebPluginRegistryModules;
|
|
31
31
|
}
|
|
32
32
|
export interface WebPluginStyleEntries {
|
|
33
|
-
/** Shell
|
|
33
|
+
/** Shell, fallback-template, and plugin sources, aliased over app.css's source list. */
|
|
34
34
|
baseSourcesPath: string;
|
|
35
|
-
/** The
|
|
35
|
+
/** The shared Tailwind entry every mount loads. */
|
|
36
36
|
baseEntryPath: string;
|
|
37
|
-
/**
|
|
37
|
+
/** ponytail: empty compatibility field; remove only with a public webBuilder API change. */
|
|
38
38
|
pluginEntryPaths: Map<string, string>;
|
|
39
39
|
}
|
|
40
|
-
/** The
|
|
40
|
+
/** The shared stylesheet's Rollup input name. */
|
|
41
41
|
export declare const BASE_STYLE_ENTRY = "base";
|
|
42
42
|
export declare const pluginStyleEntryName: (slot: string) => string;
|
|
43
|
-
/**
|
|
44
|
-
* One Tailwind entry per plugin: app.css with only the shell's sources plus that plugin's,
|
|
45
|
-
* so a mount that loads its plugins' files never receives another workspace's classes.
|
|
46
|
-
* Host plugins live in the shell's sources and share the base entry.
|
|
47
|
-
*/
|
|
43
|
+
/** One Tailwind entry for the whole build, so later sheets cannot override earlier variants. */
|
|
48
44
|
export declare function writeWebPluginStyleEntries(plugins: readonly DeclaredWebPlugin[], generatedDir: string, packageRoot: string): WebPluginStyleEntries;
|
|
49
45
|
/**
|
|
50
46
|
* The sync-time variant: renders the full client plugin set (host built-ins plus
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"webPluginGenerate.d.mts","names":[],"sources":["../../../../src/builders/web/webPluginGenerate.ts"],"mappings":";;qBAWa;qBACA;;wBAiGG,8BAA8B,sBAAsB;;;;;;wBAapD,gCACd,aACA;EAEA;EACA;;;iBAsBe;EACf;EACA;;iBAGe,6BAA6B;;EAE5C,gBAAgB;;;;;;;EAOhB,OAAO;;iBAGQ;;EAEf;;EAEA;;EAEA,kBAAkB;;;qBAIP;qBACA,uBAAoB
|
|
1
|
+
{"version":3,"file":"webPluginGenerate.d.mts","names":[],"sources":["../../../../src/builders/web/webPluginGenerate.ts"],"mappings":";;qBAWa;qBACA;;wBAiGG,8BAA8B,sBAAsB;;;;;;wBAapD,gCACd,aACA;EAEA;EACA;;;iBAsBe;EACf;EACA;;iBAGe,6BAA6B;;EAE5C,gBAAgB;;;;;;;EAOhB,OAAO;;iBAGQ;;EAEf;;EAEA;;EAEA,kBAAkB;;;qBAIP;qBACA,uBAAoB;;wBAGjB,2BACd,kBAAkB,qBAClB,sBACA,sBACC;;;;;;wBA4Ba,0BACd,kBAAkB,qBAClB,sBACA,sBACC"}
|
|
@@ -106,34 +106,23 @@ function ensureBuiltinWebPluginModules({ packageRoot, check = false }) {
|
|
|
106
106
|
}
|
|
107
107
|
if (stale.length > 0) throw new Error(`Stale generated web plugin modules: ${stale.join(", ")}. Run pnpm build in po-web and commit them.`);
|
|
108
108
|
}
|
|
109
|
-
/** The
|
|
109
|
+
/** The shared stylesheet's Rollup input name. */
|
|
110
110
|
const BASE_STYLE_ENTRY = "base";
|
|
111
111
|
const pluginStyleEntryName = (slot) => `plugin-${slot}`;
|
|
112
|
-
/**
|
|
113
|
-
* One Tailwind entry per plugin: app.css with only the shell's sources plus that plugin's,
|
|
114
|
-
* so a mount that loads its plugins' files never receives another workspace's classes.
|
|
115
|
-
* Host plugins live in the shell's sources and share the base entry.
|
|
116
|
-
*/
|
|
112
|
+
/** One Tailwind entry for the whole build, so later sheets cannot override earlier variants. */
|
|
117
113
|
function writeWebPluginStyleEntries(plugins, generatedDir, packageRoot) {
|
|
118
114
|
const stylesDir = path.join(generatedDir, "styles");
|
|
119
115
|
fs.mkdirSync(stylesDir, { recursive: true });
|
|
120
116
|
const fallbackTemplate = fallbackTemplateSourceRoot(packageRoot);
|
|
121
117
|
const baseSourcesPath = path.join(generatedDir, "webPluginBaseSources.generated.css");
|
|
122
|
-
fs.writeFileSync(baseSourcesPath, renderCssModule(
|
|
118
|
+
fs.writeFileSync(baseSourcesPath, renderCssModule(plugins, [path.join(packageRoot, "src", "web"), ...fallbackTemplate === void 0 ? [] : [fallbackTemplate]]));
|
|
123
119
|
const appCss = relativeImport(fs.realpathSync(stylesDir), fs.realpathSync(path.join(packageRoot, "src", "web", "styles", "app.css")));
|
|
124
120
|
const baseEntryPath = path.join(stylesDir, `${BASE_STYLE_ENTRY}.css`);
|
|
125
121
|
fs.writeFileSync(baseEntryPath, `/* ${HEADER.slice(3)} */\n@import '${appCss}';\n`);
|
|
126
|
-
const pluginEntryPaths = /* @__PURE__ */ new Map();
|
|
127
|
-
for (const plugin of plugins) {
|
|
128
|
-
if (plugin.isHost) continue;
|
|
129
|
-
const entryPath = path.join(stylesDir, `${pluginStyleEntryName(plugin.slot)}.css`);
|
|
130
|
-
fs.writeFileSync(entryPath, `/* ${HEADER.slice(3)} */\n@import '${appCss}';\n@source "${pluginSourceRoot(plugin)}";\n`);
|
|
131
|
-
pluginEntryPaths.set(plugin.slot, entryPath);
|
|
132
|
-
}
|
|
133
122
|
return {
|
|
134
123
|
baseSourcesPath,
|
|
135
124
|
baseEntryPath,
|
|
136
|
-
pluginEntryPaths
|
|
125
|
+
pluginEntryPaths: /* @__PURE__ */ new Map()
|
|
137
126
|
};
|
|
138
127
|
}
|
|
139
128
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"webPluginGenerate.mjs","names":[],"sources":["../../../../src/builders/web/webPluginGenerate.ts"],"sourcesContent":["import fs from 'node:fs';\nimport { createRequire } from 'node:module';\nimport path from 'node:path';\nimport { pathToFileURL } from 'node:url';\n\nimport type { DeclaredWebPlugin } from './webPluginManifest';\nimport { scanWebPlugins } from './webPluginScan';\n\nconst HEADER = '// Generated by po web builder. Do not edit by hand.';\n/** The template po-web renders when a composition names none, so its classes always compile. */\nconst FALLBACK_TEMPLATE_PACKAGE = '@agimon-ai/po-template-advanced';\nexport const BUILTIN_CLIENT_MODULE = path.join('src', 'web', 'app', 'webPlugins.generated.ts');\nexport const BUILTIN_CSS_MODULE = path.join('src', 'web', 'styles', 'webPluginSources.generated.css');\n\n/**\n * The import name of one plugin copy, derived from its slot so two copies of one pluginId\n * never collide. A host slot is its kebab-case pluginId and keeps the committed module's\n * names; a copy slot's '.' becomes '_', which no host name can contain.\n */\nfunction slotIdentifier(slot: string): string {\n return slot.replace(/-([a-z0-9])/g, (_, ch: string) => ch.toUpperCase()).replace(/[^A-Za-z0-9_$]/g, '_');\n}\n\nfunction relativeImport(fromDir: string, toFile: string): string {\n const spec = path.relative(fromDir, toFile).split(path.sep).join('/');\n return spec.startsWith('.') ? spec : `./${spec}`;\n}\n\nfunction renderClientModule(\n plugins: readonly DeclaredWebPlugin[],\n style: 'relative' | 'absolute',\n packageRoot: string,\n): string {\n const lines = [HEADER, \"import type { WebPluginDefinition } from '@agimon-ai/po-core/web';\"];\n const names: string[] = [];\n const fromDir = path.join(packageRoot, 'src', 'web', 'app');\n for (const plugin of plugins) {\n const name = `${slotIdentifier(plugin.slot)}Plugin`;\n names.push(name);\n const entry = path.join(plugin.packageDir, plugin.client.entry);\n // The committed builtin module keeps machine-neutral relative imports; the\n // sync-generated module lives outside src, is aliased over the committed\n // path, and resolves imports against its real location, so every entry\n // becomes an absolute file URL into the installed packages.\n const specifier =\n style === 'relative' ? relativeImport(fromDir, entry) : pathToFileURL(fs.realpathSync(entry)).href;\n lines.push(`import { webPlugin as ${name} } from '${specifier}';`);\n }\n lines.push('', `export const webPlugins: readonly WebPluginDefinition[] = [${names.join(', ')}];`, '');\n return lines.join('\\n');\n}\n\n/**\n * The directory Tailwind scans for a plugin's class names.\n *\n * `src/extensions` wins, because a plugin's components colocate with the routed\n * file that renders them, in private `_components` and `_lib` folders. The\n * legacy `src/web` root is only a fallback: `existsSync` is true for a\n * half-emptied directory holding nothing but a tsconfig, and packages have been\n * in exactly that state, so preferring it would hand Tailwind a directory with\n * no components in it and silently drop every class.\n *\n * The client entry's own directory is the last resort. It is published through\n * `src/exports/webClient.ts`, a one-line re-export, or generated outside `src`\n * entirely, so it holds no components either.\n */\nfunction pluginSourceRoot(plugin: DeclaredWebPlugin): string {\n const routingRoot = path.join(plugin.packageDir, 'src', 'extensions');\n if (fs.existsSync(routingRoot)) return routingRoot;\n const webRoot = path.join(plugin.packageDir, 'src', 'web');\n if (fs.existsSync(webRoot)) return webRoot;\n return path.dirname(path.join(plugin.packageDir, plugin.client.entry));\n}\n\n/**\n * Where Tailwind finds the fallback template's classes. app.css names it by a\n * path from its own folder into po-web's node_modules, which holds only in\n * this repository; an installed po-web has the template beside it instead,\n * so a sync build that scanned only that path shipped the cockpit without the\n * template's responsive layout. Resolving it as Node does works in both.\n */\nfunction fallbackTemplateSourceRoot(packageRoot: string): string | undefined {\n let manifest: string;\n try {\n manifest = createRequire(path.join(packageRoot, 'package.json')).resolve(\n `${FALLBACK_TEMPLATE_PACKAGE}/package.json`,\n );\n } catch {\n // Not installed: the cockpit then has no fallback template to render either.\n return undefined;\n }\n const extensions = path.join(path.dirname(manifest), 'dist', 'extensions');\n return fs.existsSync(extensions) ? extensions : undefined;\n}\n\nfunction renderCssModule(plugins: readonly DeclaredWebPlugin[], leadingSourceRoots: readonly string[]): string {\n // Sync CSS is compiled from a generated directory, so Tailwind cannot infer\n // the stable shell tree from app.css. Its caller names that tree explicitly\n // so later plugin utilities do not override missing responsive shell utilities.\n const lines = [`/* ${HEADER.slice(3)} */`, ...leadingSourceRoots.map((root) => `@source \"${root}\";`)];\n for (const plugin of plugins) {\n if (plugin.isHost) continue;\n lines.push(`@source \"${pluginSourceRoot(plugin)}\";`);\n }\n lines.push('');\n return lines.join('\\n');\n}\n\n/** The committed client registry, rendered from the host package alone. */\nexport function renderBuiltinWebPluginModules(packageRoot: string): Map<string, string> {\n const plugins = scanWebPlugins(packageRoot);\n return new Map([\n [BUILTIN_CLIENT_MODULE, renderClientModule(plugins, 'relative', packageRoot)],\n [BUILTIN_CSS_MODULE, renderCssModule(plugins, [])],\n ]);\n}\n\n/**\n * Keeps the committed client registry fresh: write-if-changed locally,\n * check-only (throw) in CI. Both vite.config.ts and tsdown.config.ts call\n * this at config-load time.\n */\nexport function ensureBuiltinWebPluginModules({\n packageRoot,\n check = false,\n}: {\n packageRoot: string;\n check?: boolean;\n}): void {\n const stale: string[] = [];\n for (const [relativePath, content] of renderBuiltinWebPluginModules(packageRoot)) {\n const filePath = path.join(packageRoot, relativePath);\n const existing = fs.existsSync(filePath) ? fs.readFileSync(filePath, 'utf8') : undefined;\n if (existing === content) continue;\n if (check) {\n stale.push(relativePath);\n continue;\n }\n fs.mkdirSync(path.dirname(filePath), { recursive: true });\n fs.writeFileSync(filePath, content);\n }\n if (stale.length > 0) {\n throw new Error(\n `Stale generated web plugin modules: ${stale.join(', ')}. Run pnpm build in po-web and commit them.`,\n );\n }\n}\n\n/** A client plugin registry and the Tailwind source list that goes with it. */\nexport interface WebPluginRegistryModules {\n clientModulePath: string;\n cssModulePath: string;\n}\n\nexport interface SyncGeneratedModules extends WebPluginRegistryModules {\n /** One entry module per slot, each re-exporting only that copy's `webPlugin`. */\n slotEntryPaths: Record<string, string>;\n /**\n * The signed shell's registry: host plugins only. Every other plugin reaches the\n * browser through its slot entry, and the global mount replaces the shell's\n * registry once it loads, so compiling them into the shell too shipped each plugin's\n * code and emitted assets (the voice VAD model among them) a second time.\n */\n shell: WebPluginRegistryModules;\n}\n\nexport interface WebPluginStyleEntries {\n /** Shell and fallback-template sources only, aliased over app.css's plugin source list. */\n baseSourcesPath: string;\n /** The stylesheet every plugin entry extends; a mount with no plugin styles loads it alone. */\n baseEntryPath: string;\n /** One stylesheet entry per non-host plugin copy, keyed by its slot. */\n pluginEntryPaths: Map<string, string>;\n}\n\n/** The style entry of each plugin's Rollup input name, so each emits one content-hashed file. */\nexport const BASE_STYLE_ENTRY = 'base';\nexport const pluginStyleEntryName = (slot: string): string => `plugin-${slot}`;\n\n/**\n * One Tailwind entry per plugin: app.css with only the shell's sources plus that plugin's,\n * so a mount that loads its plugins' files never receives another workspace's classes.\n * Host plugins live in the shell's sources and share the base entry.\n */\nexport function writeWebPluginStyleEntries(\n plugins: readonly DeclaredWebPlugin[],\n generatedDir: string,\n packageRoot: string,\n): WebPluginStyleEntries {\n const stylesDir = path.join(generatedDir, 'styles');\n fs.mkdirSync(stylesDir, { recursive: true });\n const fallbackTemplate = fallbackTemplateSourceRoot(packageRoot);\n const baseSourcesPath = path.join(generatedDir, 'webPluginBaseSources.generated.css');\n fs.writeFileSync(\n baseSourcesPath,\n renderCssModule(\n [],\n [path.join(packageRoot, 'src', 'web'), ...(fallbackTemplate === undefined ? [] : [fallbackTemplate])],\n ),\n );\n // Resolvers follow real paths (a temp dir under /var is /private/var), so relate real paths.\n const appCss = relativeImport(\n fs.realpathSync(stylesDir),\n fs.realpathSync(path.join(packageRoot, 'src', 'web', 'styles', 'app.css')),\n );\n const baseEntryPath = path.join(stylesDir, `${BASE_STYLE_ENTRY}.css`);\n fs.writeFileSync(baseEntryPath, `/* ${HEADER.slice(3)} */\\n@import '${appCss}';\\n`);\n const pluginEntryPaths = new Map<string, string>();\n for (const plugin of plugins) {\n if (plugin.isHost) continue;\n const entryPath = path.join(stylesDir, `${pluginStyleEntryName(plugin.slot)}.css`);\n fs.writeFileSync(\n entryPath,\n `/* ${HEADER.slice(3)} */\\n@import '${appCss}';\\n@source \"${pluginSourceRoot(plugin)}\";\\n`,\n );\n pluginEntryPaths.set(plugin.slot, entryPath);\n }\n return { baseSourcesPath, baseEntryPath, pluginEntryPaths };\n}\n\n/**\n * The sync-time variant: renders the full client plugin set (host built-ins plus\n * the installed composition's packages) into a generation directory the bundler\n * aliases over the committed builtin modules.\n */\nexport function writeSyncWebPluginModules(\n plugins: readonly DeclaredWebPlugin[],\n generatedDir: string,\n packageRoot: string,\n): SyncGeneratedModules {\n fs.mkdirSync(generatedDir, { recursive: true });\n const clientModulePath = path.join(generatedDir, 'webPlugins.generated.ts');\n const cssModulePath = path.join(generatedDir, 'webPluginSources.generated.css');\n fs.writeFileSync(clientModulePath, renderClientModule(plugins, 'absolute', packageRoot));\n // Styles are built per plugin by writeWebPluginStyleEntries, so slot entries carry none.\n const slotsDir = path.join(generatedDir, 'slots');\n fs.mkdirSync(slotsDir, { recursive: true });\n const slotEntryPaths: Record<string, string> = {};\n for (const plugin of plugins) {\n const entryPath = path.join(slotsDir, `${plugin.slot}.ts`);\n const specifier = pathToFileURL(fs.realpathSync(path.join(plugin.packageDir, plugin.client.entry))).href;\n fs.writeFileSync(entryPath, `${HEADER}\\nexport { webPlugin } from '${specifier}';\\n`);\n slotEntryPaths[plugin.slot] = entryPath;\n }\n const fallbackTemplate = fallbackTemplateSourceRoot(packageRoot);\n const leadingSourceRoots = [\n path.join(packageRoot, 'src', 'web'),\n ...(fallbackTemplate === undefined ? [] : [fallbackTemplate]),\n ];\n fs.writeFileSync(cssModulePath, renderCssModule(plugins, leadingSourceRoots));\n const hostPlugins = plugins.filter((plugin) => plugin.isHost);\n const shell = {\n clientModulePath: path.join(generatedDir, 'webPluginShell.generated.ts'),\n cssModulePath: path.join(generatedDir, 'webPluginShellSources.generated.css'),\n };\n fs.writeFileSync(shell.clientModulePath, renderClientModule(hostPlugins, 'absolute', packageRoot));\n fs.writeFileSync(shell.cssModulePath, renderCssModule(hostPlugins, leadingSourceRoots));\n return {\n clientModulePath,\n slotEntryPaths,\n cssModulePath,\n shell,\n };\n}\n"],"mappings":";;;;;;AAQA,MAAM,SAAS;;AAEf,MAAM,4BAA4B;AAClC,MAAa,wBAAwB,KAAK,KAAK,OAAO,OAAO,OAAO,yBAAyB;AAC7F,MAAa,qBAAqB,KAAK,KAAK,OAAO,OAAO,UAAU,gCAAgC;;;;;;AAOpG,SAAS,eAAe,MAAsB;CAC5C,OAAO,KAAK,QAAQ,iBAAiB,GAAG,OAAe,GAAG,YAAY,CAAC,CAAC,CAAC,QAAQ,mBAAmB,GAAG;AACzG;AAEA,SAAS,eAAe,SAAiB,QAAwB;CAC/D,MAAM,OAAO,KAAK,SAAS,SAAS,MAAM,CAAC,CAAC,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK,GAAG;CACpE,OAAO,KAAK,WAAW,GAAG,IAAI,OAAO,KAAK;AAC5C;AAEA,SAAS,mBACP,SACA,OACA,aACQ;CACR,MAAM,QAAQ,CAAC,QAAQ,oEAAoE;CAC3F,MAAM,QAAkB,CAAC;CACzB,MAAM,UAAU,KAAK,KAAK,aAAa,OAAO,OAAO,KAAK;CAC1D,KAAK,MAAM,UAAU,SAAS;EAC5B,MAAM,OAAO,GAAG,eAAe,OAAO,IAAI,EAAE;EAC5C,MAAM,KAAK,IAAI;EACf,MAAM,QAAQ,KAAK,KAAK,OAAO,YAAY,OAAO,OAAO,KAAK;EAK9D,MAAM,YACJ,UAAU,aAAa,eAAe,SAAS,KAAK,IAAI,cAAc,GAAG,aAAa,KAAK,CAAC,CAAC,CAAC;EAChG,MAAM,KAAK,yBAAyB,KAAK,WAAW,UAAU,GAAG;CACnE;CACA,MAAM,KAAK,IAAI,8DAA8D,MAAM,KAAK,IAAI,EAAE,KAAK,EAAE;CACrG,OAAO,MAAM,KAAK,IAAI;AACxB;;;;;;;;;;;;;;;AAgBA,SAAS,iBAAiB,QAAmC;CAC3D,MAAM,cAAc,KAAK,KAAK,OAAO,YAAY,OAAO,YAAY;CACpE,IAAI,GAAG,WAAW,WAAW,GAAG,OAAO;CACvC,MAAM,UAAU,KAAK,KAAK,OAAO,YAAY,OAAO,KAAK;CACzD,IAAI,GAAG,WAAW,OAAO,GAAG,OAAO;CACnC,OAAO,KAAK,QAAQ,KAAK,KAAK,OAAO,YAAY,OAAO,OAAO,KAAK,CAAC;AACvE;;;;;;;;AASA,SAAS,2BAA2B,aAAyC;CAC3E,IAAI;CACJ,IAAI;EACF,WAAW,cAAc,KAAK,KAAK,aAAa,cAAc,CAAC,CAAC,CAAC,QAC/D,GAAG,0BAA0B,cAC/B;CACF,QAAQ;EAEN;CACF;CACA,MAAM,aAAa,KAAK,KAAK,KAAK,QAAQ,QAAQ,GAAG,QAAQ,YAAY;CACzE,OAAO,GAAG,WAAW,UAAU,IAAI,aAAa,KAAA;AAClD;AAEA,SAAS,gBAAgB,SAAuC,oBAA+C;CAI7G,MAAM,QAAQ,CAAC,MAAM,OAAO,MAAM,CAAC,EAAE,MAAM,GAAG,mBAAmB,KAAK,SAAS,YAAY,KAAK,GAAG,CAAC;CACpG,KAAK,MAAM,UAAU,SAAS;EAC5B,IAAI,OAAO,QAAQ;EACnB,MAAM,KAAK,YAAY,iBAAiB,MAAM,EAAE,GAAG;CACrD;CACA,MAAM,KAAK,EAAE;CACb,OAAO,MAAM,KAAK,IAAI;AACxB;;AAGA,SAAgB,8BAA8B,aAA0C;CACtF,MAAM,UAAU,eAAe,WAAW;CAC1C,uBAAO,IAAI,IAAI,CACb,CAAC,uBAAuB,mBAAmB,SAAS,YAAY,WAAW,CAAC,GAC5E,CAAC,oBAAoB,gBAAgB,SAAS,CAAC,CAAC,CAAC,CACnD,CAAC;AACH;;;;;;AAOA,SAAgB,8BAA8B,EAC5C,aACA,QAAQ,SAID;CACP,MAAM,QAAkB,CAAC;CACzB,KAAK,MAAM,CAAC,cAAc,YAAY,8BAA8B,WAAW,GAAG;EAChF,MAAM,WAAW,KAAK,KAAK,aAAa,YAAY;EAEpD,KADiB,GAAG,WAAW,QAAQ,IAAI,GAAG,aAAa,UAAU,MAAM,IAAI,KAAA,OAC9D,SAAS;EAC1B,IAAI,OAAO;GACT,MAAM,KAAK,YAAY;GACvB;EACF;EACA,GAAG,UAAU,KAAK,QAAQ,QAAQ,GAAG,EAAE,WAAW,KAAK,CAAC;EACxD,GAAG,cAAc,UAAU,OAAO;CACpC;CACA,IAAI,MAAM,SAAS,GACjB,MAAM,IAAI,MACR,uCAAuC,MAAM,KAAK,IAAI,EAAE,4CAC1D;AAEJ;;AA8BA,MAAa,mBAAmB;AAChC,MAAa,wBAAwB,SAAyB,UAAU;;;;;;AAOxE,SAAgB,2BACd,SACA,cACA,aACuB;CACvB,MAAM,YAAY,KAAK,KAAK,cAAc,QAAQ;CAClD,GAAG,UAAU,WAAW,EAAE,WAAW,KAAK,CAAC;CAC3C,MAAM,mBAAmB,2BAA2B,WAAW;CAC/D,MAAM,kBAAkB,KAAK,KAAK,cAAc,oCAAoC;CACpF,GAAG,cACD,iBACA,gBACE,CAAC,GACD,CAAC,KAAK,KAAK,aAAa,OAAO,KAAK,GAAG,GAAI,qBAAqB,KAAA,IAAY,CAAC,IAAI,CAAC,gBAAgB,CAAE,CACtG,CACF;CAEA,MAAM,SAAS,eACb,GAAG,aAAa,SAAS,GACzB,GAAG,aAAa,KAAK,KAAK,aAAa,OAAO,OAAO,UAAU,SAAS,CAAC,CAC3E;CACA,MAAM,gBAAgB,KAAK,KAAK,WAAW,GAAG,iBAAiB,KAAK;CACpE,GAAG,cAAc,eAAe,MAAM,OAAO,MAAM,CAAC,EAAE,gBAAgB,OAAO,KAAK;CAClF,MAAM,mCAAmB,IAAI,IAAoB;CACjD,KAAK,MAAM,UAAU,SAAS;EAC5B,IAAI,OAAO,QAAQ;EACnB,MAAM,YAAY,KAAK,KAAK,WAAW,GAAG,qBAAqB,OAAO,IAAI,EAAE,KAAK;EACjF,GAAG,cACD,WACA,MAAM,OAAO,MAAM,CAAC,EAAE,gBAAgB,OAAO,eAAe,iBAAiB,MAAM,EAAE,KACvF;EACA,iBAAiB,IAAI,OAAO,MAAM,SAAS;CAC7C;CACA,OAAO;EAAE;EAAiB;EAAe;CAAiB;AAC5D;;;;;;AAOA,SAAgB,0BACd,SACA,cACA,aACsB;CACtB,GAAG,UAAU,cAAc,EAAE,WAAW,KAAK,CAAC;CAC9C,MAAM,mBAAmB,KAAK,KAAK,cAAc,yBAAyB;CAC1E,MAAM,gBAAgB,KAAK,KAAK,cAAc,gCAAgC;CAC9E,GAAG,cAAc,kBAAkB,mBAAmB,SAAS,YAAY,WAAW,CAAC;CAEvF,MAAM,WAAW,KAAK,KAAK,cAAc,OAAO;CAChD,GAAG,UAAU,UAAU,EAAE,WAAW,KAAK,CAAC;CAC1C,MAAM,iBAAyC,CAAC;CAChD,KAAK,MAAM,UAAU,SAAS;EAC5B,MAAM,YAAY,KAAK,KAAK,UAAU,GAAG,OAAO,KAAK,IAAI;EACzD,MAAM,YAAY,cAAc,GAAG,aAAa,KAAK,KAAK,OAAO,YAAY,OAAO,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC;EACpG,GAAG,cAAc,WAAW,GAAG,OAAO,+BAA+B,UAAU,KAAK;EACpF,eAAe,OAAO,QAAQ;CAChC;CACA,MAAM,mBAAmB,2BAA2B,WAAW;CAC/D,MAAM,qBAAqB,CACzB,KAAK,KAAK,aAAa,OAAO,KAAK,GACnC,GAAI,qBAAqB,KAAA,IAAY,CAAC,IAAI,CAAC,gBAAgB,CAC7D;CACA,GAAG,cAAc,eAAe,gBAAgB,SAAS,kBAAkB,CAAC;CAC5E,MAAM,cAAc,QAAQ,QAAQ,WAAW,OAAO,MAAM;CAC5D,MAAM,QAAQ;EACZ,kBAAkB,KAAK,KAAK,cAAc,6BAA6B;EACvE,eAAe,KAAK,KAAK,cAAc,qCAAqC;CAC9E;CACA,GAAG,cAAc,MAAM,kBAAkB,mBAAmB,aAAa,YAAY,WAAW,CAAC;CACjG,GAAG,cAAc,MAAM,eAAe,gBAAgB,aAAa,kBAAkB,CAAC;CACtF,OAAO;EACL;EACA;EACA;EACA;CACF;AACF"}
|
|
1
|
+
{"version":3,"file":"webPluginGenerate.mjs","names":[],"sources":["../../../../src/builders/web/webPluginGenerate.ts"],"sourcesContent":["import fs from 'node:fs';\nimport { createRequire } from 'node:module';\nimport path from 'node:path';\nimport { pathToFileURL } from 'node:url';\n\nimport type { DeclaredWebPlugin } from './webPluginManifest';\nimport { scanWebPlugins } from './webPluginScan';\n\nconst HEADER = '// Generated by po web builder. Do not edit by hand.';\n/** The template po-web renders when a composition names none, so its classes always compile. */\nconst FALLBACK_TEMPLATE_PACKAGE = '@agimon-ai/po-template-advanced';\nexport const BUILTIN_CLIENT_MODULE = path.join('src', 'web', 'app', 'webPlugins.generated.ts');\nexport const BUILTIN_CSS_MODULE = path.join('src', 'web', 'styles', 'webPluginSources.generated.css');\n\n/**\n * The import name of one plugin copy, derived from its slot so two copies of one pluginId\n * never collide. A host slot is its kebab-case pluginId and keeps the committed module's\n * names; a copy slot's '.' becomes '_', which no host name can contain.\n */\nfunction slotIdentifier(slot: string): string {\n return slot.replace(/-([a-z0-9])/g, (_, ch: string) => ch.toUpperCase()).replace(/[^A-Za-z0-9_$]/g, '_');\n}\n\nfunction relativeImport(fromDir: string, toFile: string): string {\n const spec = path.relative(fromDir, toFile).split(path.sep).join('/');\n return spec.startsWith('.') ? spec : `./${spec}`;\n}\n\nfunction renderClientModule(\n plugins: readonly DeclaredWebPlugin[],\n style: 'relative' | 'absolute',\n packageRoot: string,\n): string {\n const lines = [HEADER, \"import type { WebPluginDefinition } from '@agimon-ai/po-core/web';\"];\n const names: string[] = [];\n const fromDir = path.join(packageRoot, 'src', 'web', 'app');\n for (const plugin of plugins) {\n const name = `${slotIdentifier(plugin.slot)}Plugin`;\n names.push(name);\n const entry = path.join(plugin.packageDir, plugin.client.entry);\n // The committed builtin module keeps machine-neutral relative imports; the\n // sync-generated module lives outside src, is aliased over the committed\n // path, and resolves imports against its real location, so every entry\n // becomes an absolute file URL into the installed packages.\n const specifier =\n style === 'relative' ? relativeImport(fromDir, entry) : pathToFileURL(fs.realpathSync(entry)).href;\n lines.push(`import { webPlugin as ${name} } from '${specifier}';`);\n }\n lines.push('', `export const webPlugins: readonly WebPluginDefinition[] = [${names.join(', ')}];`, '');\n return lines.join('\\n');\n}\n\n/**\n * The directory Tailwind scans for a plugin's class names.\n *\n * `src/extensions` wins, because a plugin's components colocate with the routed\n * file that renders them, in private `_components` and `_lib` folders. The\n * legacy `src/web` root is only a fallback: `existsSync` is true for a\n * half-emptied directory holding nothing but a tsconfig, and packages have been\n * in exactly that state, so preferring it would hand Tailwind a directory with\n * no components in it and silently drop every class.\n *\n * The client entry's own directory is the last resort. It is published through\n * `src/exports/webClient.ts`, a one-line re-export, or generated outside `src`\n * entirely, so it holds no components either.\n */\nfunction pluginSourceRoot(plugin: DeclaredWebPlugin): string {\n const routingRoot = path.join(plugin.packageDir, 'src', 'extensions');\n if (fs.existsSync(routingRoot)) return routingRoot;\n const webRoot = path.join(plugin.packageDir, 'src', 'web');\n if (fs.existsSync(webRoot)) return webRoot;\n return path.dirname(path.join(plugin.packageDir, plugin.client.entry));\n}\n\n/**\n * Where Tailwind finds the fallback template's classes. app.css names it by a\n * path from its own folder into po-web's node_modules, which holds only in\n * this repository; an installed po-web has the template beside it instead,\n * so a sync build that scanned only that path shipped the cockpit without the\n * template's responsive layout. Resolving it as Node does works in both.\n */\nfunction fallbackTemplateSourceRoot(packageRoot: string): string | undefined {\n let manifest: string;\n try {\n manifest = createRequire(path.join(packageRoot, 'package.json')).resolve(\n `${FALLBACK_TEMPLATE_PACKAGE}/package.json`,\n );\n } catch {\n // Not installed: the cockpit then has no fallback template to render either.\n return undefined;\n }\n const extensions = path.join(path.dirname(manifest), 'dist', 'extensions');\n return fs.existsSync(extensions) ? extensions : undefined;\n}\n\nfunction renderCssModule(plugins: readonly DeclaredWebPlugin[], leadingSourceRoots: readonly string[]): string {\n // Sync CSS is compiled from a generated directory, so Tailwind cannot infer\n // the stable shell tree from app.css. Its caller names that tree explicitly\n // so later plugin utilities do not override missing responsive shell utilities.\n const lines = [`/* ${HEADER.slice(3)} */`, ...leadingSourceRoots.map((root) => `@source \"${root}\";`)];\n for (const plugin of plugins) {\n if (plugin.isHost) continue;\n lines.push(`@source \"${pluginSourceRoot(plugin)}\";`);\n }\n lines.push('');\n return lines.join('\\n');\n}\n\n/** The committed client registry, rendered from the host package alone. */\nexport function renderBuiltinWebPluginModules(packageRoot: string): Map<string, string> {\n const plugins = scanWebPlugins(packageRoot);\n return new Map([\n [BUILTIN_CLIENT_MODULE, renderClientModule(plugins, 'relative', packageRoot)],\n [BUILTIN_CSS_MODULE, renderCssModule(plugins, [])],\n ]);\n}\n\n/**\n * Keeps the committed client registry fresh: write-if-changed locally,\n * check-only (throw) in CI. Both vite.config.ts and tsdown.config.ts call\n * this at config-load time.\n */\nexport function ensureBuiltinWebPluginModules({\n packageRoot,\n check = false,\n}: {\n packageRoot: string;\n check?: boolean;\n}): void {\n const stale: string[] = [];\n for (const [relativePath, content] of renderBuiltinWebPluginModules(packageRoot)) {\n const filePath = path.join(packageRoot, relativePath);\n const existing = fs.existsSync(filePath) ? fs.readFileSync(filePath, 'utf8') : undefined;\n if (existing === content) continue;\n if (check) {\n stale.push(relativePath);\n continue;\n }\n fs.mkdirSync(path.dirname(filePath), { recursive: true });\n fs.writeFileSync(filePath, content);\n }\n if (stale.length > 0) {\n throw new Error(\n `Stale generated web plugin modules: ${stale.join(', ')}. Run pnpm build in po-web and commit them.`,\n );\n }\n}\n\n/** A client plugin registry and the Tailwind source list that goes with it. */\nexport interface WebPluginRegistryModules {\n clientModulePath: string;\n cssModulePath: string;\n}\n\nexport interface SyncGeneratedModules extends WebPluginRegistryModules {\n /** One entry module per slot, each re-exporting only that copy's `webPlugin`. */\n slotEntryPaths: Record<string, string>;\n /**\n * The signed shell's registry: host plugins only. Every other plugin reaches the\n * browser through its slot entry, and the global mount replaces the shell's\n * registry once it loads, so compiling them into the shell too shipped each plugin's\n * code and emitted assets (the voice VAD model among them) a second time.\n */\n shell: WebPluginRegistryModules;\n}\n\nexport interface WebPluginStyleEntries {\n /** Shell, fallback-template, and plugin sources, aliased over app.css's source list. */\n baseSourcesPath: string;\n /** The shared Tailwind entry every mount loads. */\n baseEntryPath: string;\n /** ponytail: empty compatibility field; remove only with a public webBuilder API change. */\n pluginEntryPaths: Map<string, string>;\n}\n\n/** The shared stylesheet's Rollup input name. */\nexport const BASE_STYLE_ENTRY = 'base';\nexport const pluginStyleEntryName = (slot: string): string => `plugin-${slot}`;\n\n/** One Tailwind entry for the whole build, so later sheets cannot override earlier variants. */\nexport function writeWebPluginStyleEntries(\n plugins: readonly DeclaredWebPlugin[],\n generatedDir: string,\n packageRoot: string,\n): WebPluginStyleEntries {\n const stylesDir = path.join(generatedDir, 'styles');\n fs.mkdirSync(stylesDir, { recursive: true });\n const fallbackTemplate = fallbackTemplateSourceRoot(packageRoot);\n const baseSourcesPath = path.join(generatedDir, 'webPluginBaseSources.generated.css');\n fs.writeFileSync(\n baseSourcesPath,\n renderCssModule(plugins, [\n path.join(packageRoot, 'src', 'web'),\n ...(fallbackTemplate === undefined ? [] : [fallbackTemplate]),\n ]),\n );\n // Resolvers follow real paths (a temp dir under /var is /private/var), so relate real paths.\n const appCss = relativeImport(\n fs.realpathSync(stylesDir),\n fs.realpathSync(path.join(packageRoot, 'src', 'web', 'styles', 'app.css')),\n );\n const baseEntryPath = path.join(stylesDir, `${BASE_STYLE_ENTRY}.css`);\n fs.writeFileSync(baseEntryPath, `/* ${HEADER.slice(3)} */\\n@import '${appCss}';\\n`);\n const pluginEntryPaths = new Map<string, string>();\n return { baseSourcesPath, baseEntryPath, pluginEntryPaths };\n}\n\n/**\n * The sync-time variant: renders the full client plugin set (host built-ins plus\n * the installed composition's packages) into a generation directory the bundler\n * aliases over the committed builtin modules.\n */\nexport function writeSyncWebPluginModules(\n plugins: readonly DeclaredWebPlugin[],\n generatedDir: string,\n packageRoot: string,\n): SyncGeneratedModules {\n fs.mkdirSync(generatedDir, { recursive: true });\n const clientModulePath = path.join(generatedDir, 'webPlugins.generated.ts');\n const cssModulePath = path.join(generatedDir, 'webPluginSources.generated.css');\n fs.writeFileSync(clientModulePath, renderClientModule(plugins, 'absolute', packageRoot));\n // Tailwind styles compile separately as one shared sheet, so slot entries carry none.\n const slotsDir = path.join(generatedDir, 'slots');\n fs.mkdirSync(slotsDir, { recursive: true });\n const slotEntryPaths: Record<string, string> = {};\n for (const plugin of plugins) {\n const entryPath = path.join(slotsDir, `${plugin.slot}.ts`);\n const specifier = pathToFileURL(fs.realpathSync(path.join(plugin.packageDir, plugin.client.entry))).href;\n fs.writeFileSync(entryPath, `${HEADER}\\nexport { webPlugin } from '${specifier}';\\n`);\n slotEntryPaths[plugin.slot] = entryPath;\n }\n const fallbackTemplate = fallbackTemplateSourceRoot(packageRoot);\n const leadingSourceRoots = [\n path.join(packageRoot, 'src', 'web'),\n ...(fallbackTemplate === undefined ? [] : [fallbackTemplate]),\n ];\n fs.writeFileSync(cssModulePath, renderCssModule(plugins, leadingSourceRoots));\n const hostPlugins = plugins.filter((plugin) => plugin.isHost);\n const shell = {\n clientModulePath: path.join(generatedDir, 'webPluginShell.generated.ts'),\n cssModulePath: path.join(generatedDir, 'webPluginShellSources.generated.css'),\n };\n fs.writeFileSync(shell.clientModulePath, renderClientModule(hostPlugins, 'absolute', packageRoot));\n fs.writeFileSync(shell.cssModulePath, renderCssModule(hostPlugins, leadingSourceRoots));\n return {\n clientModulePath,\n slotEntryPaths,\n cssModulePath,\n shell,\n };\n}\n"],"mappings":";;;;;;AAQA,MAAM,SAAS;;AAEf,MAAM,4BAA4B;AAClC,MAAa,wBAAwB,KAAK,KAAK,OAAO,OAAO,OAAO,yBAAyB;AAC7F,MAAa,qBAAqB,KAAK,KAAK,OAAO,OAAO,UAAU,gCAAgC;;;;;;AAOpG,SAAS,eAAe,MAAsB;CAC5C,OAAO,KAAK,QAAQ,iBAAiB,GAAG,OAAe,GAAG,YAAY,CAAC,CAAC,CAAC,QAAQ,mBAAmB,GAAG;AACzG;AAEA,SAAS,eAAe,SAAiB,QAAwB;CAC/D,MAAM,OAAO,KAAK,SAAS,SAAS,MAAM,CAAC,CAAC,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK,GAAG;CACpE,OAAO,KAAK,WAAW,GAAG,IAAI,OAAO,KAAK;AAC5C;AAEA,SAAS,mBACP,SACA,OACA,aACQ;CACR,MAAM,QAAQ,CAAC,QAAQ,oEAAoE;CAC3F,MAAM,QAAkB,CAAC;CACzB,MAAM,UAAU,KAAK,KAAK,aAAa,OAAO,OAAO,KAAK;CAC1D,KAAK,MAAM,UAAU,SAAS;EAC5B,MAAM,OAAO,GAAG,eAAe,OAAO,IAAI,EAAE;EAC5C,MAAM,KAAK,IAAI;EACf,MAAM,QAAQ,KAAK,KAAK,OAAO,YAAY,OAAO,OAAO,KAAK;EAK9D,MAAM,YACJ,UAAU,aAAa,eAAe,SAAS,KAAK,IAAI,cAAc,GAAG,aAAa,KAAK,CAAC,CAAC,CAAC;EAChG,MAAM,KAAK,yBAAyB,KAAK,WAAW,UAAU,GAAG;CACnE;CACA,MAAM,KAAK,IAAI,8DAA8D,MAAM,KAAK,IAAI,EAAE,KAAK,EAAE;CACrG,OAAO,MAAM,KAAK,IAAI;AACxB;;;;;;;;;;;;;;;AAgBA,SAAS,iBAAiB,QAAmC;CAC3D,MAAM,cAAc,KAAK,KAAK,OAAO,YAAY,OAAO,YAAY;CACpE,IAAI,GAAG,WAAW,WAAW,GAAG,OAAO;CACvC,MAAM,UAAU,KAAK,KAAK,OAAO,YAAY,OAAO,KAAK;CACzD,IAAI,GAAG,WAAW,OAAO,GAAG,OAAO;CACnC,OAAO,KAAK,QAAQ,KAAK,KAAK,OAAO,YAAY,OAAO,OAAO,KAAK,CAAC;AACvE;;;;;;;;AASA,SAAS,2BAA2B,aAAyC;CAC3E,IAAI;CACJ,IAAI;EACF,WAAW,cAAc,KAAK,KAAK,aAAa,cAAc,CAAC,CAAC,CAAC,QAC/D,GAAG,0BAA0B,cAC/B;CACF,QAAQ;EAEN;CACF;CACA,MAAM,aAAa,KAAK,KAAK,KAAK,QAAQ,QAAQ,GAAG,QAAQ,YAAY;CACzE,OAAO,GAAG,WAAW,UAAU,IAAI,aAAa,KAAA;AAClD;AAEA,SAAS,gBAAgB,SAAuC,oBAA+C;CAI7G,MAAM,QAAQ,CAAC,MAAM,OAAO,MAAM,CAAC,EAAE,MAAM,GAAG,mBAAmB,KAAK,SAAS,YAAY,KAAK,GAAG,CAAC;CACpG,KAAK,MAAM,UAAU,SAAS;EAC5B,IAAI,OAAO,QAAQ;EACnB,MAAM,KAAK,YAAY,iBAAiB,MAAM,EAAE,GAAG;CACrD;CACA,MAAM,KAAK,EAAE;CACb,OAAO,MAAM,KAAK,IAAI;AACxB;;AAGA,SAAgB,8BAA8B,aAA0C;CACtF,MAAM,UAAU,eAAe,WAAW;CAC1C,uBAAO,IAAI,IAAI,CACb,CAAC,uBAAuB,mBAAmB,SAAS,YAAY,WAAW,CAAC,GAC5E,CAAC,oBAAoB,gBAAgB,SAAS,CAAC,CAAC,CAAC,CACnD,CAAC;AACH;;;;;;AAOA,SAAgB,8BAA8B,EAC5C,aACA,QAAQ,SAID;CACP,MAAM,QAAkB,CAAC;CACzB,KAAK,MAAM,CAAC,cAAc,YAAY,8BAA8B,WAAW,GAAG;EAChF,MAAM,WAAW,KAAK,KAAK,aAAa,YAAY;EAEpD,KADiB,GAAG,WAAW,QAAQ,IAAI,GAAG,aAAa,UAAU,MAAM,IAAI,KAAA,OAC9D,SAAS;EAC1B,IAAI,OAAO;GACT,MAAM,KAAK,YAAY;GACvB;EACF;EACA,GAAG,UAAU,KAAK,QAAQ,QAAQ,GAAG,EAAE,WAAW,KAAK,CAAC;EACxD,GAAG,cAAc,UAAU,OAAO;CACpC;CACA,IAAI,MAAM,SAAS,GACjB,MAAM,IAAI,MACR,uCAAuC,MAAM,KAAK,IAAI,EAAE,4CAC1D;AAEJ;;AA8BA,MAAa,mBAAmB;AAChC,MAAa,wBAAwB,SAAyB,UAAU;;AAGxE,SAAgB,2BACd,SACA,cACA,aACuB;CACvB,MAAM,YAAY,KAAK,KAAK,cAAc,QAAQ;CAClD,GAAG,UAAU,WAAW,EAAE,WAAW,KAAK,CAAC;CAC3C,MAAM,mBAAmB,2BAA2B,WAAW;CAC/D,MAAM,kBAAkB,KAAK,KAAK,cAAc,oCAAoC;CACpF,GAAG,cACD,iBACA,gBAAgB,SAAS,CACvB,KAAK,KAAK,aAAa,OAAO,KAAK,GACnC,GAAI,qBAAqB,KAAA,IAAY,CAAC,IAAI,CAAC,gBAAgB,CAC7D,CAAC,CACH;CAEA,MAAM,SAAS,eACb,GAAG,aAAa,SAAS,GACzB,GAAG,aAAa,KAAK,KAAK,aAAa,OAAO,OAAO,UAAU,SAAS,CAAC,CAC3E;CACA,MAAM,gBAAgB,KAAK,KAAK,WAAW,GAAG,iBAAiB,KAAK;CACpE,GAAG,cAAc,eAAe,MAAM,OAAO,MAAM,CAAC,EAAE,gBAAgB,OAAO,KAAK;CAElF,OAAO;EAAE;EAAiB;EAAe,kCAAA,IADZ,IAC2B;CAAE;AAC5D;;;;;;AAOA,SAAgB,0BACd,SACA,cACA,aACsB;CACtB,GAAG,UAAU,cAAc,EAAE,WAAW,KAAK,CAAC;CAC9C,MAAM,mBAAmB,KAAK,KAAK,cAAc,yBAAyB;CAC1E,MAAM,gBAAgB,KAAK,KAAK,cAAc,gCAAgC;CAC9E,GAAG,cAAc,kBAAkB,mBAAmB,SAAS,YAAY,WAAW,CAAC;CAEvF,MAAM,WAAW,KAAK,KAAK,cAAc,OAAO;CAChD,GAAG,UAAU,UAAU,EAAE,WAAW,KAAK,CAAC;CAC1C,MAAM,iBAAyC,CAAC;CAChD,KAAK,MAAM,UAAU,SAAS;EAC5B,MAAM,YAAY,KAAK,KAAK,UAAU,GAAG,OAAO,KAAK,IAAI;EACzD,MAAM,YAAY,cAAc,GAAG,aAAa,KAAK,KAAK,OAAO,YAAY,OAAO,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC;EACpG,GAAG,cAAc,WAAW,GAAG,OAAO,+BAA+B,UAAU,KAAK;EACpF,eAAe,OAAO,QAAQ;CAChC;CACA,MAAM,mBAAmB,2BAA2B,WAAW;CAC/D,MAAM,qBAAqB,CACzB,KAAK,KAAK,aAAa,OAAO,KAAK,GACnC,GAAI,qBAAqB,KAAA,IAAY,CAAC,IAAI,CAAC,gBAAgB,CAC7D;CACA,GAAG,cAAc,eAAe,gBAAgB,SAAS,kBAAkB,CAAC;CAC5E,MAAM,cAAc,QAAQ,QAAQ,WAAW,OAAO,MAAM;CAC5D,MAAM,QAAQ;EACZ,kBAAkB,KAAK,KAAK,cAAc,6BAA6B;EACvE,eAAe,KAAK,KAAK,cAAc,qCAAqC;CAC9E;CACA,GAAG,cAAc,MAAM,kBAAkB,mBAAmB,aAAa,YAAY,WAAW,CAAC;CACjG,GAAG,cAAc,MAAM,eAAe,gBAAgB,aAAa,kBAAkB,CAAC;CACtF,OAAO;EACL;EACA;EACA;EACA;CACF;AACF"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agimon-ai/po",
|
|
3
|
-
"version": "0.0.0-alpha.
|
|
3
|
+
"version": "0.0.0-alpha.21",
|
|
4
4
|
"description": "Opinionated, composable agent harness for scoped tools, skills, MCP servers, and developer workflows.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"agent-harness",
|
|
@@ -212,23 +212,23 @@
|
|
|
212
212
|
"registry": "https://registry.npmjs.org/"
|
|
213
213
|
},
|
|
214
214
|
"dependencies": {
|
|
215
|
-
"@agimon-ai/po-artifact": "0.0.0-alpha.
|
|
216
|
-
"@agimon-ai/po-autostop": "0.0.0-alpha.
|
|
217
|
-
"@agimon-ai/po-cache": "0.0.0-alpha.
|
|
218
|
-
"@agimon-ai/po-config": "0.0.0-alpha.
|
|
219
|
-
"@agimon-ai/po-core": "0.0.0-alpha.
|
|
220
|
-
"@agimon-ai/po-domain": "0.0.0-alpha.
|
|
221
|
-
"@agimon-ai/po-logsink": "0.0.1-alpha.
|
|
222
|
-
"@agimon-ai/po-major-mode": "0.0.0-alpha.
|
|
223
|
-
"@agimon-ai/po-minor-mode": "0.0.0-alpha.
|
|
224
|
-
"@agimon-ai/po-notification": "0.0.0-alpha.
|
|
225
|
-
"@agimon-ai/po-profile": "0.0.0-alpha.
|
|
226
|
-
"@agimon-ai/po-session": "0.0.0-alpha.
|
|
227
|
-
"@agimon-ai/po-skill": "0.0.0-alpha.
|
|
228
|
-
"@agimon-ai/po-telemetry": "0.0.0-alpha.
|
|
229
|
-
"@agimon-ai/po-ui": "0.0.0-alpha.
|
|
230
|
-
"@agimon-ai/po-web-components": "0.0.0-alpha.
|
|
231
|
-
"@agimon-ai/po-web-security": "0.0.0-alpha.
|
|
215
|
+
"@agimon-ai/po-artifact": "0.0.0-alpha.19",
|
|
216
|
+
"@agimon-ai/po-autostop": "0.0.0-alpha.20",
|
|
217
|
+
"@agimon-ai/po-cache": "0.0.0-alpha.20",
|
|
218
|
+
"@agimon-ai/po-config": "0.0.0-alpha.20",
|
|
219
|
+
"@agimon-ai/po-core": "0.0.0-alpha.20",
|
|
220
|
+
"@agimon-ai/po-domain": "0.0.0-alpha.20",
|
|
221
|
+
"@agimon-ai/po-logsink": "0.0.1-alpha.2",
|
|
222
|
+
"@agimon-ai/po-major-mode": "0.0.0-alpha.20",
|
|
223
|
+
"@agimon-ai/po-minor-mode": "0.0.0-alpha.20",
|
|
224
|
+
"@agimon-ai/po-notification": "0.0.0-alpha.20",
|
|
225
|
+
"@agimon-ai/po-profile": "0.0.0-alpha.20",
|
|
226
|
+
"@agimon-ai/po-session": "0.0.0-alpha.20",
|
|
227
|
+
"@agimon-ai/po-skill": "0.0.0-alpha.20",
|
|
228
|
+
"@agimon-ai/po-telemetry": "0.0.0-alpha.20",
|
|
229
|
+
"@agimon-ai/po-ui": "0.0.0-alpha.20",
|
|
230
|
+
"@agimon-ai/po-web-components": "0.0.0-alpha.20",
|
|
231
|
+
"@agimon-ai/po-web-security": "0.0.0-alpha.20",
|
|
232
232
|
"@deepseek-ai/cordis": "4.0.5",
|
|
233
233
|
"@earendil-works/pi-agent-core": "1.3.0",
|
|
234
234
|
"@earendil-works/pi-coding-agent": "1.3.0",
|
|
@@ -249,8 +249,8 @@
|
|
|
249
249
|
"yaml": "2.9.1"
|
|
250
250
|
},
|
|
251
251
|
"devDependencies": {
|
|
252
|
-
"@agimon-ai/po-runner": "0.0.0-alpha.
|
|
253
|
-
"@agimon-ai/vibe-lint-plugin-po-cli": "0.0.0-alpha.
|
|
252
|
+
"@agimon-ai/po-runner": "0.0.0-alpha.20",
|
|
253
|
+
"@agimon-ai/vibe-lint-plugin-po-cli": "0.0.0-alpha.20",
|
|
254
254
|
"@earendil-works/pi-ai": "1.3.0",
|
|
255
255
|
"@modelcontextprotocol/ext-apps": "2.0.3",
|
|
256
256
|
"@modelcontextprotocol/server": "2.3.1",
|