@amxts/core 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/README.md +18 -23
  2. package/README.ru.md +19 -22
  3. package/as/amxts.d.ts +63 -3
  4. package/as/constants.ts +684 -116
  5. package/as/entities.ts +1905 -1364
  6. package/as/events.ts +1694 -905
  7. package/as/facade.ts +1240 -220
  8. package/as/fetch.ts +1519 -0
  9. package/as/flags.ts +159 -145
  10. package/as/hlds.ts +1071 -0
  11. package/as/hooks.ts +5226 -5379
  12. package/as/kit.ts +15 -5
  13. package/as/lib/check.ts +1 -1
  14. package/as/natives.ts +2658 -2499
  15. package/as/remote.ts +39 -15
  16. package/as/tsconfig.json +6 -2
  17. package/as/vector.ts +12 -9
  18. package/includes/order.txt +22 -29
  19. package/includes/sources.json +0 -8
  20. package/node_modules/assemblyscript/std/assembly/index.d.ts +20 -11
  21. package/package.json +29 -7
  22. package/runtime/deps/assemblyscript/dist/asc.js +86 -16
  23. package/runtime/deps/assemblyscript/dist/assemblyscript.js +29 -29
  24. package/runtime/licenses/MPL-2.0.txt +373 -0
  25. package/runtime/licenses/README.md +5 -1
  26. package/runtime/licenses/curl-COPYING +22 -0
  27. package/runtime/licenses/libssh2-COPYING +43 -0
  28. package/runtime/licenses/mbedTLS-LICENSE +553 -0
  29. package/runtime/natives.txt +108 -105
  30. package/runtime/patches/assemblyscript-0.28.20-amxts.patch +752 -296
  31. package/runtime/patches/wamr-2.4.5-amxts.patch +148 -0
  32. package/scripts/apply-docs.ts +2 -2
  33. package/scripts/auto-imports.ts +2 -2
  34. package/scripts/build-identity.ts +115 -0
  35. package/scripts/build-wasm.ts +101 -50
  36. package/scripts/check.ts +17 -3
  37. package/scripts/client-messages.ts +246 -0
  38. package/scripts/compile-pool.ts +128 -0
  39. package/scripts/compile-worker.ts +23 -0
  40. package/scripts/compile.ts +156 -59
  41. package/scripts/docker-server.ts +13 -3
  42. package/scripts/docs/as/amxts.ts +86 -0
  43. package/scripts/docs/as/facade.ts +688 -188
  44. package/scripts/docs/as/fetch.ts +805 -0
  45. package/scripts/docs/entities.ts +43 -144
  46. package/scripts/docs/events.ts +15 -26
  47. package/scripts/docs/game.ts +68 -22
  48. package/scripts/docs/messages.ts +330 -16
  49. package/scripts/editor-api.ts +101 -0
  50. package/scripts/hlds-events.ts +370 -0
  51. package/scripts/includes.ts +12 -10
  52. package/scripts/player-fields.ts +16 -15
  53. package/scripts/prebuilt.ts +65 -23
  54. package/scripts/prepare.ts +39 -28
  55. package/scripts/project.ts +120 -20
  56. package/scripts/reapi-events.ts +98 -0
  57. package/scripts/shared-modules.ts +85 -23
  58. package/scripts/source-map.ts +242 -0
  59. package/scripts/system.ts +1 -1
  60. package/scripts/typed-commands.ts +263 -0
  61. package/scripts/typed-configs.ts +23 -12
  62. package/scripts/ui.ts +3 -1
  63. package/scripts/upgrade-menus.ts +274 -0
  64. package/scripts/upgrade-names.ts +384 -0
  65. package/scripts/upgrade.ts +830 -0
  66. package/src/cli-api.mjs +37 -4
  67. package/src/system.mjs +74 -3
  68. package/src/testing/compile.ts +8 -5
  69. package/src/testing/natives.ts +199 -5
  70. package/src/testing/network.ts +531 -0
  71. package/src/testing/server.ts +565 -47
  72. package/src/testing/tables.ts +25 -0
  73. package/as/modules/http.ts +0 -184
  74. package/scripts/docs/as/modules/http.ts +0 -66
@@ -0,0 +1,246 @@
1
+ // The messages Counter-Strike's server sends its clients, as
2
+ // server.addMessageListener(name, ...) hears them - read by
3
+ // scripts/generate-host.ts, which writes their events into as/events.ts.
4
+ //
5
+ // Every name the game registers is a message a plugin can listen to, by the
6
+ // name MESSAGE_NAMES gives it in the player's words: `death`, not `DeathMsg`.
7
+ // The game's own name stays in the tooltip and in `event.name`, where someone
8
+ // porting a Pawn plugin looks for it. MESSAGE_FIELDS is the one table
9
+ // of the layouts the game writes them in: a message there is a class of its
10
+ // own with a typed field per argument it names (1 is the first write_*),
11
+ // read and written as the kind says; one without a known layout - a bot's
12
+ // progress, a tutor's text - is read by place, through `event.args`. Every
13
+ // message has `args` too, for what the table leaves out. `player` is every
14
+ // message's receiver, so a player an argument names is `target` (or its
15
+ // role: `killer`, `sender`). The words are scripts/docs/messages.ts's.
16
+ //
17
+ // A name hears one message, or a few that are the same thing to an author:
18
+ // `progressBar` is BarTime and BarTime2, which only adds where the bar
19
+ // starts. Its event has the fields of them all, each at the same argument in
20
+ // every one that writes it; a field one of them lacks reads as empty there,
21
+ // as an argument past the last does.
22
+
23
+ /**
24
+ * What an argument is read as: a number, a boolean, text; `texts` every text
25
+ * from the argument on (the words a game text puts in); `player` a player's
26
+ * number, `0` none (`Player | null`); `weapon` a weapon's id as WeaponKind
27
+ * names it; `team` a TeamName number and `teamName` a team written as text,
28
+ * both a Team; `vector` three coordinates from the argument on; `color`
29
+ * `size` bytes from it, red, green, blue (and alpha); `fixed` a number of
30
+ * `scale` parts to one (a time in 1/4096 s); `bit` whether `mask`'s bit is
31
+ * set; `fadeDirection` ScreenFade's FFADE_OUT bit as FadeDirection;
32
+ * `hideHud`, `damage`, `scoreStatus` a mask as an array of HideHud, Damage,
33
+ * ScoreStatus names; `statusIcon` StatusIcon's state (0 hide, 1 show,
34
+ * 2 flash); `destination` TextMsg's (1 notify, 2 console, 3 chat, 4 center);
35
+ * `vguiMenu` a VGUIMenu number as VguiMenu names it.
36
+ */
37
+ export type MessageFieldKind = 'number' | 'boolean' | 'string' | 'texts' | 'player' | 'weapon' | 'team' | 'teamName' | 'vector' | 'color' | 'fixed' | 'bit' | 'fadeDirection' | 'hideHud' | 'damage' | 'scoreStatus' | 'statusIcon' | 'destination' | 'vguiMenu';
38
+
39
+ export interface MessageField {
40
+ name: string;
41
+ /** The argument's number, 1 for the first. */
42
+ arg: number;
43
+ kind: MessageFieldKind;
44
+ /** `fixed`: the parts that make one. `bit`: the bit. `color`: the bytes. */
45
+ of?: number;
46
+ }
47
+
48
+ const f = (name: string, arg: number, kind: MessageFieldKind, of?: number): MessageField => ({ name, arg, kind, ...(of === undefined ? {} : { of }) });
49
+
50
+ /** The messages whose layout is known, each argument a field. */
51
+ export const MESSAGE_FIELDS: Record<string, MessageField[]> = {
52
+ ADStop: [],
53
+ AllowSpec: [f('allowed', 1, 'boolean')],
54
+ AmmoPickup: [f('ammo', 1, 'number'), f('amount', 2, 'number')],
55
+ AmmoX: [f('ammo', 1, 'number'), f('amount', 2, 'number')],
56
+ ArmorType: [f('helmet', 1, 'boolean')],
57
+ BarTime: [f('seconds', 1, 'number')],
58
+ BarTime2: [f('seconds', 1, 'number'), f('startPercent', 2, 'number')],
59
+ Battery: [f('armor', 1, 'number')],
60
+ BlinkAcct: [f('blinks', 1, 'number')],
61
+ BombDrop: [f('origin', 1, 'vector'), f('planted', 4, 'boolean')],
62
+ BombPickup: [],
63
+ BotVoice: [f('talking', 1, 'boolean'), f('target', 2, 'player')],
64
+ BuyClose: [],
65
+ ClCorpse: [f('model', 1, 'string'), f('team', 11, 'team'), f('target', 12, 'player')],
66
+ Crosshair: [f('shown', 1, 'boolean')],
67
+ CurWeapon: [f('active', 1, 'boolean'), f('weapon', 2, 'weapon'), f('clip', 3, 'number')],
68
+ Damage: [f('armor', 1, 'number'), f('damage', 2, 'number'), f('damageType', 3, 'damage'), f('origin', 4, 'vector')],
69
+ DeathMsg: [f('killer', 1, 'player'), f('victim', 2, 'player'), f('headshot', 3, 'boolean'), f('weapon', 4, 'string')],
70
+ Flashlight: [f('on', 1, 'boolean'), f('battery', 2, 'number')],
71
+ FlashBat: [f('battery', 1, 'number')],
72
+ Geiger: [f('range', 1, 'number')],
73
+ Health: [f('health', 1, 'number')],
74
+ HideWeapon: [f('flags', 1, 'hideHud')],
75
+ HostageK: [f('hostage', 1, 'number')],
76
+ HostagePos: [f('hostage', 2, 'number'), f('origin', 3, 'vector')],
77
+ HudText: [f('text', 1, 'string')],
78
+ HudTextArgs: [f('text', 1, 'string'), f('params', 4, 'texts')],
79
+ HudTextPro: [f('text', 1, 'string')],
80
+ InitHUD: [],
81
+ ItemPickup: [f('item', 1, 'string')],
82
+ ItemStatus: [f('nightVision', 1, 'bit', 1), f('defuseKit', 1, 'bit', 2)],
83
+ Location: [f('target', 1, 'player'), f('place', 2, 'string')],
84
+ Money: [f('amount', 1, 'number'), f('flash', 2, 'boolean')],
85
+ MOTD: [f('last', 1, 'boolean'), f('text', 2, 'string')],
86
+ NVGToggle: [f('on', 1, 'boolean')],
87
+ Radar: [f('target', 1, 'player'), f('origin', 2, 'vector')],
88
+ ReqState: [],
89
+ ResetHUD: [],
90
+ RoundTime: [f('seconds', 1, 'number')],
91
+ SayText: [f('sender', 1, 'player'), f('text', 2, 'string'), f('params', 3, 'texts')],
92
+ Scenario: [f('active', 1, 'boolean'), f('sprite', 2, 'string'), f('alpha', 3, 'number')],
93
+ ScoreAttrib: [f('target', 1, 'player'), f('flags', 2, 'scoreStatus')],
94
+ ScoreInfo: [f('target', 1, 'player'), f('frags', 2, 'number'), f('deaths', 3, 'number'), f('team', 5, 'team')],
95
+ ScreenFade: [
96
+ f('duration', 1, 'fixed', 4096),
97
+ f('hold', 2, 'fixed', 4096),
98
+ f('direction', 3, 'fadeDirection'),
99
+ f('modulate', 3, 'bit', 2),
100
+ f('stay', 3, 'bit', 4),
101
+ f('color', 4, 'color', 4),
102
+ ],
103
+ ScreenShake: [f('amplitude', 1, 'fixed', 4096), f('duration', 2, 'fixed', 4096), f('frequency', 3, 'fixed', 256)],
104
+ SendAudio: [f('sender', 1, 'player'), f('sound', 2, 'string'), f('pitch', 3, 'number')],
105
+ ServerName: [f('serverName', 1, 'string')],
106
+ SetFOV: [f('fov', 1, 'number')],
107
+ ShowMenu: [f('more', 3, 'boolean'), f('text', 4, 'string')],
108
+ ShowTimer: [],
109
+ SpecHealth: [f('health', 1, 'number')],
110
+ SpecHealth2: [f('health', 1, 'number'), f('target', 2, 'player')],
111
+ Spectator: [f('target', 1, 'player'), f('spectator', 2, 'boolean')],
112
+ StatusIcon: [f('state', 1, 'statusIcon'), f('sprite', 2, 'string'), f('color', 3, 'color', 3)],
113
+ StatusText: [f('line', 1, 'number'), f('text', 2, 'string')],
114
+ StatusValue: [f('slot', 1, 'number'), f('value', 2, 'number')],
115
+ TaskTime: [f('seconds', 1, 'number'), f('active', 2, 'boolean'), f('fade', 3, 'number')],
116
+ TeamInfo: [f('target', 1, 'player'), f('team', 2, 'teamName')],
117
+ TeamScore: [f('team', 1, 'teamName'), f('score', 2, 'number')],
118
+ TextMsg: [f('destination', 1, 'destination'), f('text', 2, 'string'), f('params', 3, 'texts')],
119
+ Train: [f('speed', 1, 'number')],
120
+ TutorClose: [],
121
+ VGUIMenu: [f('menu', 1, 'vguiMenu')],
122
+ ViewMode: [],
123
+ WeaponList: [
124
+ f('classname', 1, 'string'),
125
+ f('ammo', 2, 'number'),
126
+ f('maxAmmo', 3, 'number'),
127
+ f('ammo2', 4, 'number'),
128
+ f('maxAmmo2', 5, 'number'),
129
+ f('slot', 6, 'number'),
130
+ f('position', 7, 'number'),
131
+ f('weapon', 8, 'weapon'),
132
+ ],
133
+ WeapPickup: [f('weapon', 1, 'weapon')],
134
+ };
135
+
136
+ /** Every message Counter-Strike 1.6 registers, by the name get_user_msgid takes. */
137
+ export const CLIENT_MESSAGES = `
138
+ ADStop AllowSpec AmmoPickup AmmoX ArmorType BarTime BarTime2 Battery BlinkAcct BombDrop BombPickup
139
+ BotProgress BotVoice Brass BuyClose ClCorpse Crosshair CurWeapon CZCareer CZCareerHUD Damage
140
+ DeathMsg Flashlight FlashBat Fog ForceCam GameMode GameTitle Geiger Health HideWeapon HLTV HostageK
141
+ HostagePos HudText HudTextArgs HudTextPro InitHUD ItemPickup ItemStatus Location Money MOTD
142
+ NVGToggle Radar ReceiveW ReloadSound ReqState ResetHUD RoundTime SayText Scenario ScoreAttrib
143
+ ScoreInfo ScreenFade ScreenShake SendAudio ServerName SetFOV ShadowIdx ShowMenu ShowTimer
144
+ SpecHealth SpecHealth2 Spectator StatusIcon StatusText StatusValue TaskTime TeamInfo TeamScore
145
+ TextMsg Train TutorClose TutorLine TutorState TutorText VGUIMenu ViewMode VoiceMask WeaponList
146
+ WeapPickup
147
+ `.trim().split(/\s+/);
148
+
149
+ /**
150
+ * Every message's name in the player's words, by the game's: lowerCamelCase,
151
+ * without the protocol's `Msg`, an abbreviation spelled out (`CurWeapon` is
152
+ * `currentWeapon`, `SetFOV` `fov`) and an opaque name given its meaning
153
+ * (`SayText` is `chat`, `Battery` `armor`, `ShowMenu` `menu`). Messages that
154
+ * are one thing to an author share a name (`BarTime`, `BarTime2`).
155
+ */
156
+ export const MESSAGE_NAMES: Record<string, string> = {
157
+ ADStop: 'adStop',
158
+ AllowSpec: 'allowSpectate',
159
+ AmmoPickup: 'ammoPickup',
160
+ AmmoX: 'ammo',
161
+ ArmorType: 'armorType',
162
+ BarTime: 'progressBar',
163
+ BarTime2: 'progressBar',
164
+ Battery: 'armor',
165
+ BlinkAcct: 'moneyBlink',
166
+ BombDrop: 'bombDrop',
167
+ BombPickup: 'bombPickup',
168
+ BotProgress: 'botProgress',
169
+ BotVoice: 'botVoice',
170
+ Brass: 'shellCasing',
171
+ BuyClose: 'closeBuyMenu',
172
+ ClCorpse: 'corpse',
173
+ Crosshair: 'crosshair',
174
+ CurWeapon: 'currentWeapon',
175
+ CZCareer: 'czCareer',
176
+ CZCareerHUD: 'czCareerHud',
177
+ Damage: 'damage',
178
+ DeathMsg: 'death',
179
+ Flashlight: 'flashlight',
180
+ FlashBat: 'flashlightBattery',
181
+ Fog: 'fog',
182
+ ForceCam: 'forceCamera',
183
+ GameMode: 'gameMode',
184
+ GameTitle: 'gameTitle',
185
+ Geiger: 'geiger',
186
+ Health: 'health',
187
+ HideWeapon: 'hideWeapon',
188
+ HLTV: 'hltv',
189
+ HostageK: 'hostageKilled',
190
+ HostagePos: 'hostagePosition',
191
+ HudText: 'hint',
192
+ HudTextArgs: 'hint',
193
+ HudTextPro: 'hint',
194
+ InitHUD: 'initHud',
195
+ ItemPickup: 'itemPickup',
196
+ ItemStatus: 'itemStatus',
197
+ Location: 'location',
198
+ Money: 'money',
199
+ MOTD: 'motd',
200
+ NVGToggle: 'nightVision',
201
+ Radar: 'radar',
202
+ ReceiveW: 'weather',
203
+ ReloadSound: 'reloadSound',
204
+ ReqState: 'requestState',
205
+ ResetHUD: 'resetHud',
206
+ RoundTime: 'roundTime',
207
+ SayText: 'chat',
208
+ Scenario: 'scenarioIcon',
209
+ ScoreAttrib: 'scoreAttribute',
210
+ ScoreInfo: 'score',
211
+ ScreenFade: 'screenFade',
212
+ ScreenShake: 'screenShake',
213
+ SendAudio: 'sound',
214
+ ServerName: 'serverName',
215
+ SetFOV: 'fov',
216
+ ShadowIdx: 'shadow',
217
+ ShowMenu: 'menu',
218
+ ShowTimer: 'showTimer',
219
+ SpecHealth: 'spectatedHealth',
220
+ SpecHealth2: 'spectatedHealth',
221
+ Spectator: 'spectator',
222
+ StatusIcon: 'statusIcon',
223
+ StatusText: 'statusText',
224
+ StatusValue: 'statusValue',
225
+ TaskTime: 'taskTime',
226
+ TeamInfo: 'team',
227
+ TeamScore: 'teamScore',
228
+ TextMsg: 'text',
229
+ Train: 'train',
230
+ TutorClose: 'tutorClose',
231
+ TutorLine: 'tutorLine',
232
+ TutorState: 'tutorState',
233
+ TutorText: 'tutorText',
234
+ VGUIMenu: 'vguiMenu',
235
+ ViewMode: 'viewMode',
236
+ VoiceMask: 'voiceMask',
237
+ WeaponList: 'weaponList',
238
+ WeapPickup: 'weaponPickup',
239
+ };
240
+
241
+ /** The names server.addMessageListener takes, each with the game's messages it hears, in the game's order. */
242
+ export const MESSAGE_GROUPS = new Map<string, string[]>();
243
+ for (const message of CLIENT_MESSAGES) {
244
+ const name = MESSAGE_NAMES[message];
245
+ if (name) MESSAGE_GROUPS.set(name, [...(MESSAGE_GROUPS.get(name) ?? []), message]);
246
+ }
@@ -0,0 +1,128 @@
1
+ // Several plugins compiled at once. asc is not reentrant and a compile holds
2
+ // about a gigabyte at its peak, so each runs in a process of its own
3
+ // (scripts/compile-worker.ts), as many at once as the CPUs and the memory
4
+ // budget allow: AMXTS_BUILD_MEMORY megabytes for all of them (3072), or
5
+ // AMXTS_BUILD_JOBS compiles. A single plugin, or room for one, is compiled in
6
+ // this process. Either way a plugin compiles to the same bytes.
7
+ import type { Plugin } from './compile';
8
+ import type { NativesBeside, PluginNative } from './plugin-natives';
9
+ import { spawn } from 'node:child_process';
10
+ import { availableParallelism } from 'node:os';
11
+ import { dirname, join } from 'node:path';
12
+ import { fileURLToPath } from 'node:url';
13
+ import { setNativesBeside } from './plugin-natives';
14
+
15
+ const MB = 1024 * 1024;
16
+
17
+ /** A compile's peak, asc's and wamrc's together: measured at about a gigabyte, with room. */
18
+ export const COMPILE_MEMORY = 1280 * MB;
19
+
20
+ /** What the compiles of a build may hold together, unless AMXTS_BUILD_MEMORY says otherwise. */
21
+ export const BUILD_MEMORY = 3072 * MB;
22
+
23
+ const WORKER = join(dirname(fileURLToPath(import.meta.url)), 'compile-worker.ts');
24
+
25
+ /** How many compiles run at once: AMXTS_BUILD_JOBS, else as many as the memory budget holds, at most one per CPU. */
26
+ export function compileSlots(env: Record<string, string | undefined> = process.env, cpus = availableParallelism()): number {
27
+ const jobs = Number(env.AMXTS_BUILD_JOBS);
28
+ if (Number.isInteger(jobs) && jobs > 0) return jobs;
29
+ const budget = Number(env.AMXTS_BUILD_MEMORY) > 0 ? Number(env.AMXTS_BUILD_MEMORY) * MB : BUILD_MEMORY;
30
+ return Math.max(1, Math.min(cpus, Math.floor(budget / COMPILE_MEMORY)));
31
+ }
32
+
33
+ /**
34
+ * `run` on every item, at most `slots` at a time, each given the slot it runs
35
+ * in (0, 1, ...) and its index: the items start in order, and the results come
36
+ * back in it.
37
+ */
38
+ export async function inSlots<T, R>(items: T[], slots: number, run: (item: T, slot: number, index: number) => Promise<R>): Promise<R[]> {
39
+ const results: R[] = Array.from({ length: items.length });
40
+ let next = 0;
41
+ const slot = async (number: number) => {
42
+ while (next < items.length) {
43
+ const index = next++;
44
+ results[index] = await run(items[index], number, index);
45
+ }
46
+ };
47
+ await Promise.all(Array.from({ length: Math.min(slots, items.length) }, (_, number) => slot(number)));
48
+ return results;
49
+ }
50
+
51
+ /** One plugin compiled: what went wrong, or null, and its natives. */
52
+ export interface Compiled {
53
+ problem: string | null;
54
+ natives: PluginNative[];
55
+ }
56
+
57
+ /** What a worker is sent: the plugin, and the plugin cache it compiles through (scripts/plugin-cache.ts). */
58
+ export interface WorkerJob {
59
+ plugin: Plugin;
60
+ /** The project, whose cache it is; null for none. */
61
+ dir: string | null;
62
+ /** The includes the build writes itself. */
63
+ includes: string[];
64
+ }
65
+
66
+ export type WorkerResult = Compiled & { beside: NativesBeside };
67
+
68
+ /** A worker process: one job at a time, until it is closed. */
69
+ function worker() {
70
+ const child = spawn(process.execPath, [WORKER], { stdio: ['ignore', 'inherit', 'inherit', 'ipc'] });
71
+ let waiting: { resolve: (result: WorkerResult) => void; reject: (error: Error) => void } | null = null;
72
+ const answer = () => {
73
+ const now = waiting;
74
+ waiting = null;
75
+ return now;
76
+ };
77
+ child.on('message', (result: WorkerResult) => answer()?.resolve(result));
78
+ child.on('exit', code => answer()?.reject(new Error(`a compile process stopped (exit ${code})`)));
79
+ return {
80
+ compile: (job: WorkerJob) => new Promise<WorkerResult>((resolve, reject) => {
81
+ waiting = { resolve, reject };
82
+ child.send(job);
83
+ }),
84
+ close: () => child.connected && child.disconnect(),
85
+ };
86
+ }
87
+
88
+ export interface CompileAll {
89
+ /** The project and the includes its builds write: the plugin cache's (null: none). */
90
+ dir: string | null;
91
+ includes: string[];
92
+ /** The compile in this process: the build's plugin cache. */
93
+ here: (plugin: Plugin, natives: PluginNative[]) => Promise<string | null>;
94
+ started?: (index: number) => void;
95
+ finished?: (index: number, compiled: Compiled) => void;
96
+ }
97
+
98
+ /**
99
+ * The plugins compiled, several at once when there is room - in this process
100
+ * when there is room for one. After a failure no other compile starts: those
101
+ * that did not are null.
102
+ */
103
+ export async function compileAll(plugins: Plugin[], options: CompileAll): Promise<(Compiled | null)[]> {
104
+ const slots = Math.min(plugins.length, compileSlots());
105
+ const workers = slots > 1 ? Array.from({ length: slots }, worker) : [];
106
+ let failed = false;
107
+ const compile = async (plugin: Plugin, slot: number): Promise<Compiled> => {
108
+ if (workers.length === 0) {
109
+ const natives: PluginNative[] = [];
110
+ return { problem: await options.here(plugin, natives), natives };
111
+ }
112
+ const { beside, ...compiled } = await workers[slot].compile({ plugin, dir: options.dir, includes: options.includes });
113
+ setNativesBeside(compiled.natives, beside);
114
+ return compiled;
115
+ };
116
+ try {
117
+ return await inSlots(plugins, slots, async (plugin, slot, index) => {
118
+ if (failed) return null;
119
+ options.started?.(index);
120
+ const compiled = await compile(plugin, slot);
121
+ failed ||= compiled.problem !== null;
122
+ options.finished?.(index, compiled);
123
+ return compiled;
124
+ });
125
+ } finally {
126
+ for (const each of workers) each.close();
127
+ }
128
+ }
@@ -0,0 +1,23 @@
1
+ // A compile process of scripts/compile-pool.ts: compiles the plugins it is
2
+ // sent, one at a time, through the build's plugin cache (scripts/plugin-cache.ts),
3
+ // and answers each with what went wrong and the plugin's natives. It ends when
4
+ // the build disconnects.
5
+ import type { WorkerJob, WorkerResult } from './compile-pool';
6
+ import type { PluginNative } from './plugin-natives';
7
+ import { pluginCache } from './plugin-cache';
8
+ import { nativesBeside } from './plugin-natives';
9
+
10
+ const caches = new Map<string, ReturnType<typeof pluginCache>>();
11
+
12
+ process.on('message', async ({ plugin, dir, includes }: WorkerJob) => {
13
+ const key = JSON.stringify([dir, includes]);
14
+ if (!caches.has(key)) caches.set(key, pluginCache(dir, includes));
15
+ const natives: PluginNative[] = [];
16
+ let problem: string | null;
17
+ try {
18
+ problem = await caches.get(key)!.compile(plugin, natives);
19
+ } catch (error) {
20
+ problem = String((error as Error).stack ?? error);
21
+ }
22
+ process.send!({ problem, natives, beside: nativesBeside(natives) } satisfies WorkerResult);
23
+ });