agl 1.0.17 → 21.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (277) hide show
  1. package/.smartconfig.json +55 -0
  2. package/changelog.md +1026 -0
  3. package/cli.js +14 -0
  4. package/dist_serve/bundle.js +44778 -0
  5. package/dist_serve/index.html +13 -0
  6. package/dist_ts/00_commitinfo_data.d.ts +8 -0
  7. package/dist_ts/00_commitinfo_data.js +9 -0
  8. package/dist_ts/classes.authmodels.d.ts +126 -0
  9. package/dist_ts/classes.authmodels.js +1422 -0
  10. package/dist_ts/classes.authstore.d.ts +160 -0
  11. package/dist_ts/classes.authstore.js +2483 -0
  12. package/dist_ts/classes.browserresourcehost.d.ts +85 -0
  13. package/dist_ts/classes.browserresourcehost.js +912 -0
  14. package/dist_ts/classes.cli.d.ts +21 -0
  15. package/dist_ts/classes.cli.js +1082 -0
  16. package/dist_ts/classes.config.d.ts +23 -0
  17. package/dist_ts/classes.config.js +267 -0
  18. package/dist_ts/classes.controller.d.ts +523 -0
  19. package/dist_ts/classes.controller.js +10611 -0
  20. package/dist_ts/classes.controllermanagement.d.ts +32 -0
  21. package/dist_ts/classes.controllermanagement.js +222 -0
  22. package/dist_ts/classes.draftmanager.d.ts +30 -0
  23. package/dist_ts/classes.draftmanager.js +179 -0
  24. package/dist_ts/classes.embeddeddb.d.ts +30 -0
  25. package/dist_ts/classes.embeddeddb.js +159 -0
  26. package/dist_ts/classes.flexframedtransport.d.ts +56 -0
  27. package/dist_ts/classes.flexframedtransport.js +275 -0
  28. package/dist_ts/classes.flexmodels.private.d.ts +231 -0
  29. package/dist_ts/classes.flexmodels.private.js +985 -0
  30. package/dist_ts/classes.flexmodels.public.d.ts +207 -0
  31. package/dist_ts/classes.flexmodels.public.js +885 -0
  32. package/dist_ts/classes.flexprojectionreader.d.ts +89 -0
  33. package/dist_ts/classes.flexprojectionreader.js +580 -0
  34. package/dist_ts/classes.flexservice.d.ts +196 -0
  35. package/dist_ts/classes.flexservice.js +3704 -0
  36. package/dist_ts/classes.flexstore.d.ts +174 -0
  37. package/dist_ts/classes.flexstore.js +3400 -0
  38. package/dist_ts/classes.flexsupervisor.d.ts +105 -0
  39. package/dist_ts/classes.flexsupervisor.js +1191 -0
  40. package/dist_ts/classes.gitreversion.d.ts +431 -0
  41. package/dist_ts/classes.gitreversion.js +10592 -0
  42. package/dist_ts/classes.harnessoperationmanager.d.ts +113 -0
  43. package/dist_ts/classes.harnessoperationmanager.js +602 -0
  44. package/dist_ts/classes.opencodeclient.d.ts +146 -0
  45. package/dist_ts/classes.opencodeclient.js +3003 -0
  46. package/dist_ts/classes.opencodesupervisor.d.ts +60 -0
  47. package/dist_ts/classes.opencodesupervisor.js +1003 -0
  48. package/dist_ts/classes.passkeymanager.d.ts +34 -0
  49. package/dist_ts/classes.passkeymanager.js +456 -0
  50. package/dist_ts/classes.processinspection.d.ts +50 -0
  51. package/dist_ts/classes.processinspection.js +484 -0
  52. package/dist_ts/classes.resourcecoordinator.d.ts +64 -0
  53. package/dist_ts/classes.resourcecoordinator.js +294 -0
  54. package/dist_ts/classes.sessionintelligencereader.d.ts +13 -0
  55. package/dist_ts/classes.sessionintelligencereader.js +40 -0
  56. package/dist_ts/classes.systemmetricssampler.d.ts +53 -0
  57. package/dist_ts/classes.systemmetricssampler.js +168 -0
  58. package/dist_ts/classes.terminalmanager.d.ts +95 -0
  59. package/dist_ts/classes.terminalmanager.js +981 -0
  60. package/dist_ts/classes.terminalresourcehost.d.ts +20 -0
  61. package/dist_ts/classes.terminalresourcehost.js +84 -0
  62. package/dist_ts/classes.upgradecoordinator.d.ts +244 -0
  63. package/dist_ts/classes.upgradecoordinator.js +2155 -0
  64. package/dist_ts/classes.upgradetransaction.d.ts +92 -0
  65. package/dist_ts/classes.upgradetransaction.js +2125 -0
  66. package/dist_ts/classes.uploadmanager.d.ts +24 -0
  67. package/dist_ts/classes.uploadmanager.js +210 -0
  68. package/dist_ts/classes.validation.d.ts +91 -0
  69. package/dist_ts/classes.validation.js +1087 -0
  70. package/dist_ts/flexharness.child.d.ts +1 -0
  71. package/dist_ts/flexharness.child.js +28 -0
  72. package/dist_ts/functions.canonicaldirectory.d.ts +21 -0
  73. package/dist_ts/functions.canonicaldirectory.js +144 -0
  74. package/dist_ts/functions.controllerdataroot.d.ts +15 -0
  75. package/dist_ts/functions.controllerdataroot.js +76 -0
  76. package/dist_ts/functions.embeddeddb.d.ts +2 -0
  77. package/dist_ts/functions.embeddeddb.js +21 -0
  78. package/dist_ts/functions.flexcredentialstore.d.ts +16 -0
  79. package/dist_ts/functions.flexcredentialstore.js +93 -0
  80. package/dist_ts/functions.opencodeauth.d.ts +3 -0
  81. package/dist_ts/functions.opencodeauth.js +31 -0
  82. package/dist_ts/functions.runtimeenvironment.d.ts +2 -0
  83. package/dist_ts/functions.runtimeenvironment.js +79 -0
  84. package/dist_ts/functions.upgradefollower.d.ts +3 -0
  85. package/dist_ts/functions.upgradefollower.js +95 -0
  86. package/dist_ts/index.d.ts +24 -0
  87. package/dist_ts/index.js +25 -0
  88. package/dist_ts/interfaces.auth.d.ts +181 -0
  89. package/dist_ts/interfaces.auth.js +82 -0
  90. package/dist_ts/interfaces.config.d.ts +34 -0
  91. package/dist_ts/interfaces.config.js +2 -0
  92. package/dist_ts/interfaces.flexipc.d.ts +939 -0
  93. package/dist_ts/interfaces.flexipc.js +1492 -0
  94. package/dist_ts/interfaces.gitreversion.d.ts +246 -0
  95. package/dist_ts/interfaces.gitreversion.js +2 -0
  96. package/dist_ts/interfaces.lifecycle.d.ts +37 -0
  97. package/dist_ts/interfaces.lifecycle.js +2 -0
  98. package/dist_ts/interfaces.opencode.d.ts +101 -0
  99. package/dist_ts/interfaces.opencode.js +3 -0
  100. package/dist_ts/interfaces.projects.d.ts +156 -0
  101. package/dist_ts/interfaces.projects.js +5 -0
  102. package/dist_ts/plugins.d.ts +36 -0
  103. package/dist_ts/plugins.js +40 -0
  104. package/dist_ts_interfaces/index.d.ts +2 -0
  105. package/dist_ts_interfaces/index.js +3 -0
  106. package/dist_ts_interfaces/interfaces.d.ts +747 -0
  107. package/dist_ts_interfaces/interfaces.js +47 -0
  108. package/dist_ts_interfaces/plugins.d.ts +4 -0
  109. package/dist_ts_interfaces/plugins.js +3 -0
  110. package/dist_ts_interfaces/requests.d.ts +806 -0
  111. package/dist_ts_interfaces/requests.js +2 -0
  112. package/dist_ts_migration/classes.documentmigrationrunner.d.ts +8 -0
  113. package/dist_ts_migration/classes.documentmigrationrunner.js +24 -0
  114. package/dist_ts_migration/classes.flexmigrationrunner.d.ts +11 -0
  115. package/dist_ts_migration/classes.flexmigrationrunner.js +25 -0
  116. package/dist_ts_migration/classes.flexpostharnessmigrationrunner.d.ts +10 -0
  117. package/dist_ts_migration/classes.flexpostharnessmigrationrunner.js +15 -0
  118. package/dist_ts_migration/classes.migrationrunner.d.ts +8 -0
  119. package/dist_ts_migration/classes.migrationrunner.js +34 -0
  120. package/dist_ts_migration/index.d.ts +12 -0
  121. package/dist_ts_migration/index.js +13 -0
  122. package/dist_ts_migration/v10_flexharnessv3migration.d.ts +11 -0
  123. package/dist_ts_migration/v10_flexharnessv3migration.js +23 -0
  124. package/dist_ts_migration/v13_flexprojectionorder.d.ts +11 -0
  125. package/dist_ts_migration/v13_flexprojectionorder.js +23 -0
  126. package/dist_ts_migration/v16_flexprovidercredentials.d.ts +22 -0
  127. package/dist_ts_migration/v16_flexprovidercredentials.js +149 -0
  128. package/dist_ts_migration/v18_resourcelayout.d.ts +22 -0
  129. package/dist_ts_migration/v18_resourcelayout.js +23 -0
  130. package/dist_ts_migration/v18_resourcelayoutmigration.d.ts +6 -0
  131. package/dist_ts_migration/v18_resourcelayoutmigration.js +61 -0
  132. package/dist_ts_migration/v19_flexprojectmanagement.d.ts +11 -0
  133. package/dist_ts_migration/v19_flexprojectmanagement.js +21 -0
  134. package/dist_ts_migration/v1_embeddeddatabaselocation.d.ts +28 -0
  135. package/dist_ts_migration/v1_embeddeddatabaselocation.js +415 -0
  136. package/dist_ts_migration/v22_projectfilesystemidentitymigration.d.ts +7 -0
  137. package/dist_ts_migration/v22_projectfilesystemidentitymigration.js +105 -0
  138. package/dist_ts_migration/v2_controllerdataroot.d.ts +130 -0
  139. package/dist_ts_migration/v2_controllerdataroot.js +1887 -0
  140. package/dist_ts_migration/v6_protocoldocumentmigration.d.ts +7 -0
  141. package/dist_ts_migration/v6_protocoldocumentmigration.js +100 -0
  142. package/dist_ts_migration/v6_protocoldocuments.d.ts +7 -0
  143. package/dist_ts_migration/v6_protocoldocuments.js +138 -0
  144. package/html/index.html +13 -0
  145. package/{LICENSE → license.md} +2 -2
  146. package/package.json +86 -48
  147. package/readme.md +480 -0
  148. package/readme.plan.md +188 -0
  149. package/ts/00_commitinfo_data.ts +8 -0
  150. package/ts/classes.authmodels.ts +1245 -0
  151. package/ts/classes.authstore.ts +3274 -0
  152. package/ts/classes.browserresourcehost.ts +1205 -0
  153. package/ts/classes.cli.ts +1254 -0
  154. package/ts/classes.config.ts +351 -0
  155. package/ts/classes.controller.ts +14426 -0
  156. package/ts/classes.controllermanagement.ts +330 -0
  157. package/ts/classes.draftmanager.ts +232 -0
  158. package/ts/classes.embeddeddb.ts +166 -0
  159. package/ts/classes.flexframedtransport.ts +283 -0
  160. package/ts/classes.flexmodels.private.ts +823 -0
  161. package/ts/classes.flexmodels.public.ts +732 -0
  162. package/ts/classes.flexprojectionreader.ts +816 -0
  163. package/ts/classes.flexservice.ts +4302 -0
  164. package/ts/classes.flexstore.ts +4565 -0
  165. package/ts/classes.flexsupervisor.ts +1453 -0
  166. package/ts/classes.gitreversion.ts +13353 -0
  167. package/ts/classes.harnessoperationmanager.ts +870 -0
  168. package/ts/classes.opencodeclient.ts +4110 -0
  169. package/ts/classes.opencodesupervisor.ts +1280 -0
  170. package/ts/classes.passkeymanager.ts +570 -0
  171. package/ts/classes.processinspection.ts +623 -0
  172. package/ts/classes.resourcecoordinator.ts +436 -0
  173. package/ts/classes.sessionintelligencereader.ts +52 -0
  174. package/ts/classes.systemmetricssampler.ts +215 -0
  175. package/ts/classes.terminalmanager.ts +1207 -0
  176. package/ts/classes.terminalresourcehost.ts +122 -0
  177. package/ts/classes.upgradecoordinator.ts +2675 -0
  178. package/ts/classes.upgradetransaction.ts +2900 -0
  179. package/ts/classes.uploadmanager.ts +240 -0
  180. package/ts/classes.validation.ts +1409 -0
  181. package/ts/flexharness.child.ts +35 -0
  182. package/ts/functions.canonicaldirectory.ts +213 -0
  183. package/ts/functions.controllerdataroot.ts +107 -0
  184. package/ts/functions.embeddeddb.ts +22 -0
  185. package/ts/functions.flexcredentialstore.ts +128 -0
  186. package/ts/functions.opencodeauth.ts +34 -0
  187. package/ts/functions.runtimeenvironment.ts +90 -0
  188. package/ts/functions.upgradefollower.ts +115 -0
  189. package/ts/index.ts +36 -6
  190. package/ts/interfaces.auth.ts +316 -0
  191. package/ts/interfaces.config.ts +37 -0
  192. package/ts/interfaces.flexipc.ts +2586 -0
  193. package/ts/interfaces.gitreversion.ts +336 -0
  194. package/ts/interfaces.lifecycle.ts +46 -0
  195. package/ts/interfaces.opencode.ts +153 -0
  196. package/ts/interfaces.projects.ts +182 -0
  197. package/ts/plugins.ts +64 -0
  198. package/ts_interfaces/index.ts +2 -0
  199. package/ts_interfaces/interfaces.ts +827 -0
  200. package/ts_interfaces/plugins.ts +15 -0
  201. package/ts_interfaces/requests.ts +1186 -0
  202. package/ts_migration/classes.documentmigrationrunner.ts +32 -0
  203. package/ts_migration/classes.flexmigrationrunner.ts +33 -0
  204. package/ts_migration/classes.flexpostharnessmigrationrunner.ts +22 -0
  205. package/ts_migration/classes.migrationrunner.ts +40 -0
  206. package/ts_migration/index.ts +12 -0
  207. package/ts_migration/v10_flexharnessv3migration.ts +35 -0
  208. package/ts_migration/v13_flexprojectionorder.ts +33 -0
  209. package/ts_migration/v16_flexprovidercredentials.ts +196 -0
  210. package/ts_migration/v18_resourcelayout.ts +55 -0
  211. package/ts_migration/v18_resourcelayoutmigration.ts +68 -0
  212. package/ts_migration/v19_flexprojectmanagement.ts +28 -0
  213. package/ts_migration/v1_embeddeddatabaselocation.ts +560 -0
  214. package/ts_migration/v22_projectfilesystemidentitymigration.ts +121 -0
  215. package/ts_migration/v2_controllerdataroot.ts +2349 -0
  216. package/ts_migration/v6_protocoldocumentmigration.ts +144 -0
  217. package/ts_migration/v6_protocoldocuments.ts +198 -0
  218. package/ts_web/00_commitinfo_data.ts +8 -0
  219. package/ts_web/classes.controllersocketclient.ts +517 -0
  220. package/ts_web/classes.sessiondraftsync.ts +390 -0
  221. package/ts_web/elements.harnesscontrollerapp.ts +11494 -0
  222. package/ts_web/index.ts +2 -0
  223. package/ts_web/plugins.ts +31 -0
  224. package/.gitlab-ci.yml +0 -71
  225. package/.npmignore +0 -5
  226. package/README.md +0 -40
  227. package/dist/cli.js +0 -2
  228. package/dist/index.d.ts +0 -0
  229. package/dist/index.js +0 -9
  230. package/dist/mod_bundle/index.d.ts +0 -1
  231. package/dist/mod_bundle/index.js +0 -18
  232. package/dist/mod_bundle/mod.plugins.d.ts +0 -4
  233. package/dist/mod_bundle/mod.plugins.js +0 -11
  234. package/dist/mod_install/index.d.ts +0 -4
  235. package/dist/mod_install/index.js +0 -21
  236. package/dist/mod_install/mod.plugins.d.ts +0 -1
  237. package/dist/mod_install/mod.plugins.js +0 -7
  238. package/dist/mod_standard/index.d.ts +0 -4
  239. package/dist/mod_standard/index.js +0 -21
  240. package/dist/mod_standard/mod.browser.d.ts +0 -6
  241. package/dist/mod_standard/mod.browser.js +0 -14
  242. package/dist/mod_standard/mod.clean.d.ts +0 -4
  243. package/dist/mod_standard/mod.clean.js +0 -12
  244. package/dist/mod_standard/mod.compile.d.ts +0 -4
  245. package/dist/mod_standard/mod.compile.js +0 -21
  246. package/dist/mod_standard/mod.plugins.d.ts +0 -8
  247. package/dist/mod_standard/mod.plugins.js +0 -19
  248. package/dist/mod_standard/mod.test.d.ts +0 -4
  249. package/dist/mod_standard/mod.test.js +0 -12
  250. package/dist/plr.cli.d.ts +0 -3
  251. package/dist/plr.cli.js +0 -57
  252. package/dist/plr.config.d.ts +0 -11
  253. package/dist/plr.config.js +0 -26
  254. package/dist/plr.mods.d.ts +0 -17
  255. package/dist/plr.mods.js +0 -17
  256. package/dist/plr.paths.d.ts +0 -19
  257. package/dist/plr.paths.js +0 -26
  258. package/dist/plr.plugins.d.ts +0 -8
  259. package/dist/plr.plugins.js +0 -16
  260. package/npmextra.json +0 -10
  261. package/ts/mod_bundle/index.ts +0 -20
  262. package/ts/mod_bundle/mod.plugins.ts +0 -9
  263. package/ts/mod_install/index.ts +0 -20
  264. package/ts/mod_install/mod.plugins.ts +0 -1
  265. package/ts/mod_standard/index.ts +0 -23
  266. package/ts/mod_standard/mod.browser.ts +0 -17
  267. package/ts/mod_standard/mod.clean.ts +0 -13
  268. package/ts/mod_standard/mod.compile.ts +0 -23
  269. package/ts/mod_standard/mod.plugins.ts +0 -17
  270. package/ts/mod_standard/mod.test.ts +0 -13
  271. package/ts/plr.cli.ts +0 -60
  272. package/ts/plr.config.ts +0 -36
  273. package/ts/plr.mods.ts +0 -25
  274. package/ts/plr.paths.ts +0 -29
  275. package/ts/plr.plugins.ts +0 -17
  276. package/tslint.json +0 -3
  277. package/yarn.lock +0 -3464
@@ -0,0 +1,166 @@
1
+ import * as plugins from './plugins.js';
2
+ import {
3
+ EmbeddedDatabaseMigrationRunner,
4
+ } from '../ts_migration/classes.migrationrunner.js';
5
+ import {
6
+ embeddedDatabaseSocketPath,
7
+ isSocketListening,
8
+ } from './functions.embeddeddb.js';
9
+
10
+ export { embeddedDatabaseSocketPath } from './functions.embeddeddb.js';
11
+
12
+ export interface IEmbeddedControllerDatabaseOptions {
13
+ /** directory holding the smartdb storage files and its unix socket */
14
+ dataDirectory: string;
15
+ /** previous installed default, logically migrated only while its engine is stopped */
16
+ legacyDataDirectory?: string;
17
+ }
18
+
19
+ const lstatDirectory = async (directoryArg: string): Promise<plugins.fs.Stats | undefined> => {
20
+ try {
21
+ const stats = await plugins.fs.promises.lstat(directoryArg);
22
+ if (!stats.isDirectory()) {
23
+ throw new Error(`Controller database path must be a real directory: ${directoryArg}`);
24
+ }
25
+ return stats;
26
+ } catch (errorArg) {
27
+ if ((errorArg as NodeJS.ErrnoException).code === 'ENOENT') return undefined;
28
+ throw errorArg;
29
+ }
30
+ };
31
+
32
+ /**
33
+ * The controller's embedded database engine: a @push.rocks/smartdb
34
+ * LocalSmartDb over a unix socket inside the data directory. The socket
35
+ * path is deterministic so a second controller process (e.g. the
36
+ * temp-password CLI) attaches to the running daemon's engine instead of
37
+ * opening the storage files a second time; only the process that booted
38
+ * the engine stops it.
39
+ */
40
+ export class EmbeddedControllerDatabase {
41
+ private localDb?: plugins.smartdb.LocalSmartDb;
42
+ private connectionUri?: string;
43
+ private startPromise?: Promise<string>;
44
+ private migrationRunner?: EmbeddedDatabaseMigrationRunner;
45
+
46
+ constructor(private readonly options: IEmbeddedControllerDatabaseOptions) {}
47
+
48
+ public get socketPath(): string {
49
+ // Unix socket paths are capped near 108 bytes (SUN_LEN), so the socket
50
+ // lives in tmpdir under a short name derived from the data directory —
51
+ // deterministic, so a second controller process attaches to the same
52
+ // engine, and independent of how deep the data directory is nested.
53
+ return embeddedDatabaseSocketPath(this.options.dataDirectory);
54
+ }
55
+
56
+ public get ownsEngine(): boolean {
57
+ return this.localDb !== undefined;
58
+ }
59
+
60
+ /** Starts (or attaches to) the engine and returns the smartdata-ready URI. */
61
+ public async start(): Promise<string> {
62
+ if (this.connectionUri) return this.connectionUri;
63
+ if (this.startPromise) return this.startPromise;
64
+ const startPromise = this.startOnce();
65
+ this.startPromise = startPromise;
66
+ try {
67
+ return await startPromise;
68
+ } finally {
69
+ if (this.startPromise === startPromise) this.startPromise = undefined;
70
+ }
71
+ }
72
+
73
+ private async startOnce(): Promise<string> {
74
+ if (this.localDb || this.migrationRunner?.hasOwnedResources) {
75
+ throw new Error('Embedded database startup cleanup must complete before retrying.');
76
+ }
77
+ if (this.options.legacyDataDirectory) {
78
+ const migrationRunner = new EmbeddedDatabaseMigrationRunner({
79
+ sourceDirectory: this.options.legacyDataDirectory,
80
+ destinationDirectory: this.options.dataDirectory,
81
+ sourceSocketPath: embeddedDatabaseSocketPath(this.options.legacyDataDirectory),
82
+ destinationSocketPath: this.socketPath,
83
+ });
84
+ this.migrationRunner = migrationRunner;
85
+ try {
86
+ const result = await migrationRunner.run();
87
+ if (result) {
88
+ this.localDb = result.localDb;
89
+ this.connectionUri = result.connectionUri;
90
+ return result.connectionUri;
91
+ }
92
+ } finally {
93
+ if (!migrationRunner.hasOwnedResources && this.migrationRunner === migrationRunner) {
94
+ this.migrationRunner = undefined;
95
+ }
96
+ }
97
+ }
98
+ await plugins.fs.promises.mkdir(this.options.dataDirectory, {
99
+ recursive: true,
100
+ mode: 0o700,
101
+ });
102
+ await lstatDirectory(this.options.dataDirectory);
103
+ const socketUri = `mongodb://${encodeURIComponent(this.socketPath)}/?directConnection=true`;
104
+ if (await this.isSocketListening()) {
105
+ this.connectionUri = socketUri;
106
+ return socketUri;
107
+ }
108
+ const localDb = new plugins.smartdb.LocalSmartDb({
109
+ folderPath: this.options.dataDirectory,
110
+ socketPath: this.socketPath,
111
+ });
112
+ this.localDb = localDb;
113
+ try {
114
+ await localDb.start();
115
+ } catch (errorArg) {
116
+ try {
117
+ await localDb.stop();
118
+ if (this.localDb === localDb) this.localDb = undefined;
119
+ } catch (cleanupErrorArg) {
120
+ throw new AggregateError(
121
+ [errorArg, cleanupErrorArg],
122
+ 'Embedded database startup failed and cleanup was incomplete.',
123
+ { cause: errorArg },
124
+ );
125
+ }
126
+ throw errorArg;
127
+ }
128
+ this.connectionUri = socketUri;
129
+ return socketUri;
130
+ }
131
+
132
+ public async stop(): Promise<void> {
133
+ if (this.startPromise) {
134
+ await this.startPromise.catch(() => undefined);
135
+ }
136
+ const localDb = this.localDb;
137
+ const migrationRunner = this.migrationRunner;
138
+ const errors: unknown[] = [];
139
+ if (localDb) {
140
+ try {
141
+ await localDb.stop();
142
+ if (this.localDb === localDb) this.localDb = undefined;
143
+ } catch (errorArg) {
144
+ errors.push(errorArg);
145
+ }
146
+ }
147
+ if (migrationRunner) {
148
+ try {
149
+ await migrationRunner.stop();
150
+ if (this.migrationRunner === migrationRunner) this.migrationRunner = undefined;
151
+ } catch (errorArg) {
152
+ errors.push(errorArg);
153
+ }
154
+ }
155
+ if (errors.length === 0) this.connectionUri = undefined;
156
+ if (errors.length === 1) throw errors[0];
157
+ if (errors.length > 1) {
158
+ throw new AggregateError(errors, 'Embedded database shutdown was incomplete.');
159
+ }
160
+ }
161
+
162
+ private isSocketListening(): Promise<boolean> {
163
+ return isSocketListening(this.socketPath);
164
+ }
165
+
166
+ }
@@ -0,0 +1,283 @@
1
+ import * as plugins from './plugins.js';
2
+ import { flexIpcMaximumFrameBytes } from './interfaces.flexipc.js';
3
+
4
+ export interface IFlexFramedTransportOptions<TIncoming, TOutgoing> {
5
+ readable: plugins.stream.Readable;
6
+ writable: plugins.stream.Writable;
7
+ parseIncoming: (valueArg: unknown) => TIncoming;
8
+ validateOutgoing?: (valueArg: unknown) => TOutgoing;
9
+ maximumFrameBytes?: number;
10
+ }
11
+
12
+ type TMessageListener<TMessage> = (messageArg: TMessage) => void;
13
+ type TErrorListener = (errorArg: Error) => void;
14
+ type TCloseListener = () => void;
15
+
16
+ export const flexIpcMaximumQueuedFrames = 128;
17
+ export const flexIpcMaximumQueuedFrameBytes = 64 * 1024 * 1024;
18
+ const flexIpcCloseDrainTimeoutMs = 2_000;
19
+
20
+ const normalizeMaximumFrameBytes = (valueArg: number | undefined): number => {
21
+ const selected = valueArg ?? flexIpcMaximumFrameBytes;
22
+ if (!Number.isSafeInteger(selected) || selected < 1 || selected > flexIpcMaximumFrameBytes) {
23
+ throw new Error(`maximumFrameBytes must be between 1 and ${flexIpcMaximumFrameBytes}.`);
24
+ }
25
+ return selected;
26
+ };
27
+
28
+ export const encodeFlexFrame = (
29
+ valueArg: unknown,
30
+ maximumFrameBytesArg = flexIpcMaximumFrameBytes,
31
+ ): Buffer => {
32
+ const serialized = Buffer.from(JSON.stringify(valueArg), 'utf8');
33
+ try {
34
+ if (serialized.byteLength > maximumFrameBytesArg) {
35
+ throw new Error('Flex IPC frame exceeds its size limit.');
36
+ }
37
+ const frame = Buffer.allocUnsafe(4 + serialized.byteLength);
38
+ frame.writeUInt32BE(serialized.byteLength, 0);
39
+ serialized.copy(frame, 4);
40
+ return frame;
41
+ } finally {
42
+ serialized.fill(0);
43
+ }
44
+ };
45
+
46
+ /**
47
+ * A single-reader/single-writer JSON transport. The body buffer is allocated
48
+ * only after the fixed header has passed the configured length limit.
49
+ */
50
+ export class FlexFramedTransport<TIncoming, TOutgoing> {
51
+ private readonly readable: plugins.stream.Readable;
52
+ private readonly writable: plugins.stream.Writable;
53
+ private readonly parseIncoming: (valueArg: unknown) => TIncoming;
54
+ private readonly validateOutgoing?: (valueArg: unknown) => TOutgoing;
55
+ private readonly maximumFrameBytes: number;
56
+ private readonly header = Buffer.allocUnsafe(4);
57
+ private headerOffset = 0;
58
+ private body?: Buffer;
59
+ private bodyOffset = 0;
60
+ private messageListeners = new Set<TMessageListener<TIncoming>>();
61
+ private errorListeners = new Set<TErrorListener>();
62
+ private closeListeners = new Set<TCloseListener>();
63
+ private writeTail = Promise.resolve();
64
+ private queuedFrames = 0;
65
+ private queuedFrameBytes = 0;
66
+ private failed = false;
67
+ private closed = false;
68
+ private closePromise?: Promise<void>;
69
+
70
+ constructor(optionsArg: IFlexFramedTransportOptions<TIncoming, TOutgoing>) {
71
+ this.readable = optionsArg.readable;
72
+ this.writable = optionsArg.writable;
73
+ this.parseIncoming = optionsArg.parseIncoming;
74
+ this.validateOutgoing = optionsArg.validateOutgoing;
75
+ this.maximumFrameBytes = normalizeMaximumFrameBytes(optionsArg.maximumFrameBytes);
76
+ this.readable.on('data', this.onData);
77
+ this.readable.once('error', this.onReadError);
78
+ this.readable.once('end', this.onReadableClosed);
79
+ this.readable.once('close', this.onReadableClosed);
80
+ this.writable.once('error', this.onWriteError);
81
+ this.writable.once('finish', this.onWritableClosed);
82
+ this.writable.once('close', this.onWritableClosed);
83
+ }
84
+
85
+ public onMessage(listenerArg: TMessageListener<TIncoming>): () => void {
86
+ this.messageListeners.add(listenerArg);
87
+ return () => this.messageListeners.delete(listenerArg);
88
+ }
89
+
90
+ public onError(listenerArg: TErrorListener): () => void {
91
+ this.errorListeners.add(listenerArg);
92
+ return () => this.errorListeners.delete(listenerArg);
93
+ }
94
+
95
+ public onClose(listenerArg: TCloseListener): () => void {
96
+ this.closeListeners.add(listenerArg);
97
+ return () => this.closeListeners.delete(listenerArg);
98
+ }
99
+
100
+ public async send(messageArg: TOutgoing): Promise<void> {
101
+ if (this.closed || this.failed) throw new Error('Flex IPC transport is closed.');
102
+ this.validateOutgoing?.(messageArg);
103
+ const frame = encodeFlexFrame(messageArg, this.maximumFrameBytes);
104
+ if (
105
+ this.queuedFrames >= flexIpcMaximumQueuedFrames
106
+ || this.queuedFrameBytes + frame.byteLength > flexIpcMaximumQueuedFrameBytes
107
+ ) throw new Error('Flex IPC outbound queue limit exceeded.');
108
+ this.queuedFrames++;
109
+ this.queuedFrameBytes += frame.byteLength;
110
+ const write = this.writeTail.then(() => this.writeFrame(frame));
111
+ this.writeTail = write.catch(() => undefined);
112
+ try {
113
+ await write;
114
+ } finally {
115
+ this.queuedFrames--;
116
+ this.queuedFrameBytes -= frame.byteLength;
117
+ frame.fill(0);
118
+ }
119
+ }
120
+
121
+ public close(): Promise<void> {
122
+ if (this.closePromise) return this.closePromise;
123
+ if (this.closed) return Promise.resolve();
124
+ this.closePromise = this.closeTransport();
125
+ return this.closePromise;
126
+ }
127
+
128
+ private async closeTransport(): Promise<void> {
129
+ this.closed = true;
130
+ this.detachStreams();
131
+ let drainTimer: ReturnType<typeof setTimeout> | undefined;
132
+ const drainTimeout = new Promise<'timeout'>((resolve) => {
133
+ drainTimer = setTimeout(() => resolve('timeout'), flexIpcCloseDrainTimeoutMs);
134
+ drainTimer.unref?.();
135
+ });
136
+ const gracefulClose = this.writeTail.then(async () => {
137
+ if (!this.writable.destroyed && !this.writable.writableEnded) {
138
+ await new Promise<void>((resolve) => {
139
+ this.writable.end(resolve);
140
+ });
141
+ }
142
+ return 'drained' as const;
143
+ });
144
+ const drainResult = await Promise.race([
145
+ gracefulClose,
146
+ drainTimeout,
147
+ ]);
148
+ if (drainTimer) clearTimeout(drainTimer);
149
+ if (drainResult === 'timeout') {
150
+ if (!this.writable.destroyed) this.writable.destroy();
151
+ if (!this.readable.destroyed) this.readable.destroy();
152
+ this.emitClose();
153
+ return;
154
+ }
155
+ if (!this.readable.destroyed) this.readable.destroy();
156
+ this.emitClose();
157
+ }
158
+
159
+ private readonly onData = (chunkArg: unknown): void => {
160
+ if (this.closed || this.failed) return;
161
+ const chunk = typeof chunkArg === 'string'
162
+ ? Buffer.from(chunkArg, 'utf8')
163
+ : chunkArg instanceof Uint8Array
164
+ ? Buffer.from(chunkArg.buffer, chunkArg.byteOffset, chunkArg.byteLength)
165
+ : undefined;
166
+ if (!chunk) {
167
+ this.fail(new Error('Flex IPC received a non-byte chunk.'));
168
+ return;
169
+ }
170
+
171
+ let chunkOffset = 0;
172
+ try {
173
+ while (chunkOffset < chunk.byteLength) {
174
+ if (!this.body) {
175
+ const headerBytes = Math.min(4 - this.headerOffset, chunk.byteLength - chunkOffset);
176
+ chunk.copy(this.header, this.headerOffset, chunkOffset, chunkOffset + headerBytes);
177
+ this.headerOffset += headerBytes;
178
+ chunkOffset += headerBytes;
179
+ if (this.headerOffset < 4) continue;
180
+ const bodyLength = this.header.readUInt32BE(0);
181
+ if (bodyLength > this.maximumFrameBytes) {
182
+ throw new Error('Flex IPC frame exceeds its size limit.');
183
+ }
184
+ this.body = Buffer.allocUnsafe(bodyLength);
185
+ this.bodyOffset = 0;
186
+ if (bodyLength === 0) this.finishBody();
187
+ continue;
188
+ }
189
+
190
+ const bodyBytes = Math.min(
191
+ this.body.byteLength - this.bodyOffset,
192
+ chunk.byteLength - chunkOffset,
193
+ );
194
+ chunk.copy(this.body, this.bodyOffset, chunkOffset, chunkOffset + bodyBytes);
195
+ this.bodyOffset += bodyBytes;
196
+ chunkOffset += bodyBytes;
197
+ if (this.bodyOffset === this.body.byteLength) this.finishBody();
198
+ }
199
+ } catch (errorArg) {
200
+ this.fail(errorArg instanceof Error ? errorArg : new Error('Flex IPC framing failed.'));
201
+ }
202
+ };
203
+
204
+ private finishBody(): void {
205
+ const completedBody = this.body;
206
+ this.body = undefined;
207
+ this.bodyOffset = 0;
208
+ this.headerOffset = 0;
209
+ if (!completedBody) throw new Error('Flex IPC body state was lost.');
210
+ let parsed: unknown;
211
+ try {
212
+ parsed = JSON.parse(completedBody.toString('utf8')) as unknown;
213
+ } catch {
214
+ throw new Error('Flex IPC frame contains invalid JSON.');
215
+ } finally {
216
+ completedBody.fill(0);
217
+ }
218
+ const message = this.parseIncoming(parsed);
219
+ for (const listener of [...this.messageListeners]) listener(message);
220
+ }
221
+
222
+ private writeFrame(frameArg: Buffer): Promise<void> {
223
+ if (this.closed || this.failed || this.writable.destroyed || this.writable.writableEnded) {
224
+ throw new Error('Flex IPC transport is closed.');
225
+ }
226
+ return new Promise<void>((resolve, reject) => {
227
+ this.writable.write(frameArg, (errorArg?: Error | null) => {
228
+ if (errorArg) reject(errorArg);
229
+ else resolve();
230
+ });
231
+ });
232
+ }
233
+
234
+ private readonly onReadError = (errorArg: Error): void => this.fail(errorArg);
235
+ private readonly onWriteError = (errorArg: Error): void => this.fail(errorArg);
236
+ private readonly onReadableClosed = (): void => {
237
+ if (this.closed) return;
238
+ if (this.headerOffset !== 0 || this.body) {
239
+ this.fail(new Error('Flex IPC stream ended within a frame.'));
240
+ return;
241
+ }
242
+ this.closed = true;
243
+ this.detachStreams();
244
+ if (!this.readable.destroyed) this.readable.destroy();
245
+ if (!this.writable.destroyed) this.writable.destroy();
246
+ this.emitClose();
247
+ };
248
+
249
+ private readonly onWritableClosed = (): void => {
250
+ if (this.closed) return;
251
+ this.closed = true;
252
+ this.detachStreams();
253
+ if (!this.readable.destroyed) this.readable.destroy();
254
+ if (!this.writable.destroyed) this.writable.destroy();
255
+ this.emitClose();
256
+ };
257
+
258
+ private fail(errorArg: Error): void {
259
+ if (this.failed || this.closed) return;
260
+ this.failed = true;
261
+ this.detachStreams();
262
+ if (!this.readable.destroyed) this.readable.destroy();
263
+ if (!this.writable.destroyed) this.writable.destroy();
264
+ for (const listener of [...this.errorListeners]) listener(errorArg);
265
+ this.emitClose();
266
+ }
267
+
268
+ private detachStreams(): void {
269
+ this.readable.removeListener('data', this.onData);
270
+ this.readable.removeListener('error', this.onReadError);
271
+ this.readable.removeListener('end', this.onReadableClosed);
272
+ this.readable.removeListener('close', this.onReadableClosed);
273
+ this.writable.removeListener('error', this.onWriteError);
274
+ this.writable.removeListener('finish', this.onWritableClosed);
275
+ this.writable.removeListener('close', this.onWritableClosed);
276
+ }
277
+
278
+ private emitClose(): void {
279
+ const listeners = [...this.closeListeners];
280
+ this.closeListeners.clear();
281
+ for (const listener of listeners) listener();
282
+ }
283
+ }