@ibgib/web-gib 0.0.28 → 0.0.30

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 (36) hide show
  1. package/dist/AUTO-GENERATED-version.d.mts +1 -1
  2. package/dist/AUTO-GENERATED-version.mjs +1 -1
  3. package/dist/app-bootstrap/bootstrap.d.mts +90 -0
  4. package/dist/app-bootstrap/bootstrap.d.mts.map +1 -0
  5. package/dist/app-bootstrap/bootstrap.mjs +404 -0
  6. package/dist/app-bootstrap/bootstrap.mjs.map +1 -0
  7. package/dist/app-bootstrap/init-orchestration.d.mts +75 -0
  8. package/dist/app-bootstrap/init-orchestration.d.mts.map +1 -0
  9. package/dist/app-bootstrap/init-orchestration.mjs +211 -0
  10. package/dist/app-bootstrap/init-orchestration.mjs.map +1 -0
  11. package/dist/app-bootstrap/types.d.mts +163 -0
  12. package/dist/app-bootstrap/types.d.mts.map +1 -0
  13. package/dist/app-bootstrap/types.mjs +15 -0
  14. package/dist/app-bootstrap/types.mjs.map +1 -0
  15. package/dist/constants.d.mts +10 -0
  16. package/dist/constants.d.mts.map +1 -1
  17. package/dist/constants.mjs +10 -0
  18. package/dist/constants.mjs.map +1 -1
  19. package/dist/helpers.d.mts +0 -7
  20. package/dist/helpers.d.mts.map +1 -1
  21. package/dist/helpers.mjs +0 -25
  22. package/dist/helpers.mjs.map +1 -1
  23. package/dist/index.d.mts +16 -1
  24. package/dist/index.d.mts.map +1 -1
  25. package/dist/index.mjs +15 -2
  26. package/dist/index.mjs.map +1 -1
  27. package/package.json +1 -1
  28. package/src/AUTO-GENERATED-version.mts +1 -1
  29. package/src/app-bootstrap/bootstrap.mts +570 -0
  30. package/src/app-bootstrap/init-orchestration.mts +244 -0
  31. package/src/app-bootstrap/types.mts +190 -0
  32. package/src/constants.mts +10 -0
  33. package/src/helpers.mts +1 -23
  34. package/src/index.mts +28 -2
  35. package/tools/auto-generated-agent-skills/skills/ibgib-create-app-core/SKILL.md +7 -7
  36. package/tools/auto-generated-agent-skills/skills/ibgib-create-app-core/templates/types.mts.template +0 -3
package/dist/index.mjs CHANGED
@@ -1,3 +1,16 @@
1
- import { fooFn } from './foo.mjs';
2
- await fooFn();
1
+ /**
2
+ * @ibgib/web-gib
3
+ *
4
+ * Front Door: Streamlined App Bootstrapping
5
+ */
6
+ export { bootstrapIbGibApp } from "./app-bootstrap/bootstrap.mjs";
7
+ export { initIbGibStorage, dynamicallyLoadBootstrapScript, getComponentCtorArg } from "./app-bootstrap/init-orchestration.mjs";
8
+ /**
9
+ * Toolbox: Library Internals
10
+ * (Only export what's necessary for advanced users)
11
+ */
12
+ export * from "./constants.mjs";
13
+ export * from "./types.mjs";
14
+ export * from "./helpers.web.mjs";
15
+ export * from "./helpers.mjs";
3
16
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","sourceRoot":"","sources":["../src/index.mts"],"names":[],"mappings":"AAAA,OAAO,EAAC,KAAK,EAAC,MAAM,WAAW,CAAC;AAEhC,MAAM,KAAK,EAAE,CAAC"}
1
+ {"version":3,"file":"index.mjs","sourceRoot":"","sources":["../src/index.mts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EACH,iBAAiB,EACpB,MAAM,+BAA+B,CAAC;AAOvC,OAAO,EACH,gBAAgB,EAChB,8BAA8B,EAC9B,mBAAmB,EACtB,MAAM,wCAAwC,CAAC;AAEhD;;;GAGG;AACH,cAAc,iBAAiB,CAAC;AAChC,cAAc,aAAa,CAAC;AAC5B,cAAc,mBAAmB,CAAC;AAClC,cAAc,eAAe,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ibgib/web-gib",
3
- "version": "0.0.28",
3
+ "version": "0.0.30",
4
4
  "description": "Framework for creating agentic ibGib web apps. Contains plumbing for ibgib components, agentic framework (currently only Gemini implemented), web-based IndexedDB storage substrate, and more.",
5
5
  "funding": {
6
6
  "type": "individual",
@@ -11,4 +11,4 @@
11
11
  /**
12
12
  * this is the version of this package, auto-updated in the build process
13
13
  */
14
- export const AUTO_GENERATED_VERSION = '0.0.28';
14
+ export const AUTO_GENERATED_VERSION = '0.0.30';
@@ -0,0 +1,570 @@
1
+ /**
2
+ * @module bootstrap
3
+ *
4
+ * Shared bootstrap entry point for ibgib-based web apps.
5
+ * Collapses the metaspace, globalThis, and shell-init boilerplate.
6
+ */
7
+
8
+ import {
9
+ clone, extractErrorMsg, getTimestamp, getUUID, pickRandom_Letters,
10
+ } from "@ibgib/helper-gib/dist/helpers/utils-helper.mjs";
11
+ import { RCLIArgInfo, RCLIParamInfo } from "@ibgib/helper-gib/dist/rcli/rcli-types.mjs";
12
+ import { buildArgInfos } from "@ibgib/helper-gib/dist/rcli/rcli-helper.mjs";
13
+ import { IbGibAddr, TransformResult } from "@ibgib/ts-gib/dist/types.mjs";
14
+ import { getIbAndGib, getIbGibAddr } from "@ibgib/ts-gib/dist/helper.mjs";
15
+ import { Factory_V1 as factory } from "@ibgib/ts-gib/dist/V1/factory.mjs";
16
+ import { Rel8n } from "@ibgib/ts-gib/dist/V1/types.mjs";
17
+ import { IbGibAppAny, AppBase_V1 } from "@ibgib/core-gib/dist/witness/app/app-base-v1.mjs";
18
+ import { getFromSpace } from "@ibgib/core-gib/dist/witness/space/space-helper.mjs";
19
+ import { createNewApp, getAppIb, getInfoFromAppIb } from "@ibgib/core-gib/dist/witness/app/app-helper.mjs";
20
+ import { MetaspaceService } from "@ibgib/core-gib/dist/witness/space/metaspace/metaspace-types.mjs";
21
+ import { IbGibSpaceAny } from "@ibgib/core-gib/dist/witness/space/space-base-v1.mjs";
22
+ import { APP_REL8N_NAME } from '@ibgib/core-gib/dist/witness/app/app-constants.mjs';
23
+ import { TAG_REL8N_NAME } from "@ibgib/core-gib/dist/common/tag/tag-constants.mjs";
24
+ import { TagIbGib_V1 } from "@ibgib/core-gib/dist/common/tag/tag-types.mjs";
25
+ import { SPACE_NAME_REGEXP } from "@ibgib/core-gib/dist/witness/space/space-constants.mjs";
26
+ import { SpecialIbGibType } from "@ibgib/core-gib/dist/common/other/other-types.mjs";
27
+
28
+ import { alertUser, promptForSecret, promptForText } from "../helpers.web.mjs";
29
+ import { Metaspace_Webspace } from "../witness/space/metaspace/metaspace-webspace/metaspace-webspace.mjs";
30
+ import { LiveProxyIbGib } from "../witness/live-proxy-ibgib/live-proxy-ibgib-one-file.mjs";
31
+ import { storageGet } from "../storage/storage-helpers.web.mjs";
32
+ import { getIbGibGlobalThis_IbGib } from "../helpers.mjs";
33
+
34
+ import {
35
+ IbGibAmbientContextConfig,
36
+ IbGibGlobalThis_IbGibApp,
37
+ IbGibIdentityContext,
38
+ RequestCommentData_V1,
39
+ RequestCommentIbGib_V1,
40
+ } from "./types.mjs";
41
+
42
+ // ---------------------------------------------------------------------------
43
+ // Options
44
+ // ---------------------------------------------------------------------------
45
+
46
+ export interface TagConfig {
47
+ text: string;
48
+ icon: string;
49
+ description: string;
50
+ }
51
+
52
+ export interface BootstrapIbGibAppOptions<TApp extends AppBase_V1<any, any, any, any, any, any, any, any>> {
53
+ /** Application context configuration (IndexedDB names, etc.) */
54
+ config: IbGibAmbientContextConfig;
55
+
56
+ /** Accessor for the application's typed globalThis slot */
57
+ getGlobalThis: (config?: IbGibAmbientContextConfig) => IbGibGlobalThis_IbGibApp;
58
+
59
+ /** The main application witness class */
60
+ AppClass: new (data?: any, rel8ns?: any) => TApp;
61
+
62
+ /**
63
+ * Default data used when generating the very first version of this app.
64
+ * Only applied if `fnGetOrCreateApp` is omitted.
65
+ */
66
+ defaultAppData: any;
67
+
68
+ /**
69
+ * Optional. Provide an array of parameters the application supports.
70
+ */
71
+ paramInfos?: RCLIParamInfo[];
72
+
73
+ /**
74
+ * Optional. Provide initial args for bootstrap.
75
+ * Defaults to `['--interactive']`.
76
+ */
77
+ getOpeningArgs?: () => Promise<string[]>;
78
+
79
+ /**
80
+ * Optional. RCLI argument validation hook.
81
+ */
82
+ validateArgInfos?: (opts: { argInfos: RCLIArgInfo[] }) => string | undefined;
83
+
84
+ /**
85
+ * Optional. Hook called before initializing the app, but after metaspace is ready.
86
+ * Can be used to check environment, space state, etc.
87
+ */
88
+ onBeforeCreate?: (metaspace: MetaspaceService, space: IbGibSpaceAny) => Promise<void>;
89
+
90
+ /**
91
+ * Optional. Hook called immediately after the application witness is loaded or created.
92
+ * A good place to execute migrations before returning the 'ready' app.
93
+ */
94
+ onAppLoaded?: (app: TApp, isNew: boolean) => Promise<TApp>;
95
+
96
+ /**
97
+ * Nuclear escape hatch. Provide this to replace the entire app get-or-create flow.
98
+ * If provided, `onBeforeCreate`, `onAppLoaded`, and `defaultAppData` are ignored.
99
+ */
100
+ fnGetOrCreateApp?: (metaspace: MetaspaceService, space: IbGibSpaceAny) => Promise<TApp>;
101
+
102
+ /**
103
+ * Optional. Hook called to initialize identity context (e.g., keystone based auth).
104
+ */
105
+ resolveIdentity?: (metaspace: MetaspaceService) => Promise<IbGibIdentityContext | undefined>;
106
+
107
+ /**
108
+ * Optional. The point at which the application logic dictates its readiness to UI components.
109
+ * Typically this executes `getAppShellSvc().onEngineReady()`.
110
+ */
111
+ onReady?: (app: TApp) => void | Promise<void>;
112
+
113
+ /**
114
+ * Tags verified to exist in the global user space at startup.
115
+ */
116
+ ensureTags?: TagConfig[];
117
+
118
+ /**
119
+ * The key prefix used for allocating new spaces (e.g. `test_app_space`).
120
+ */
121
+ localSpaceNamePrefix?: string;
122
+
123
+ /**
124
+ * Optional hook to register AI agent function tracking and deprecations.
125
+ */
126
+ registerAgentFunctionInfos?: () => void;
127
+ }
128
+
129
+ // ---------------------------------------------------------------------------
130
+ // Main Flow
131
+ // ---------------------------------------------------------------------------
132
+
133
+ export async function bootstrapIbGibApp<TApp extends AppBase_V1<any, any, any, any, any, any, any, any>>(
134
+ opts: BootstrapIbGibAppOptions<TApp>
135
+ ): Promise<void> {
136
+ const lc = `[${bootstrapIbGibApp.name}]`;
137
+ const logalot = true; // Use a general log flag or derive from config
138
+
139
+ try {
140
+ if (logalot) { console.log(`${lc} starting...`); }
141
+
142
+ const ibGibGlobalThis = opts.getGlobalThis(opts.config);
143
+
144
+ if (ibGibGlobalThis.bootstrapStarted) {
145
+ if (ibGibGlobalThis.bootstrapPromise) {
146
+ console.warn(`${lc} already bootstrapping — awaiting that promise.`);
147
+ } else {
148
+ throw new Error(`(UNEXPECTED) bootstrapStarted but bootstrapPromise is falsy?`);
149
+ }
150
+ } else {
151
+ ibGibGlobalThis.bootstrapStarted = true;
152
+ ibGibGlobalThis.bootstrapPromise = new Promise<void>(async (resolve, reject) => {
153
+ try {
154
+ const args = opts.getOpeningArgs
155
+ ? await opts.getOpeningArgs()
156
+ : ['--interactive'];
157
+
158
+ const paramInfos = opts.paramInfos ?? [{ name: 'interactive', isFlag: true, argTypeName: 'boolean' }];
159
+
160
+ const paramInfosErrors = validateParamInfos({ paramInfos });
161
+ if (paramInfosErrors.length > 0) {
162
+ throw new Error(`(UNEXPECTED) invalid PARAM_INFOS: ${paramInfosErrors.join('|')}`);
163
+ }
164
+
165
+ const argInfos = buildArgInfos({ args, paramInfos, logalot });
166
+
167
+ if (opts.validateArgInfos) {
168
+ const validationErrors = opts.validateArgInfos({ argInfos });
169
+ if (validationErrors) {
170
+ throw new Error(`Arg validation errors: ${validationErrors}`);
171
+ }
172
+ }
173
+
174
+ await execFromArgs({ opts, args, argInfos, logalot });
175
+ resolve();
176
+ } catch (error) {
177
+ reject(error);
178
+ }
179
+ });
180
+ }
181
+
182
+ await ibGibGlobalThis.bootstrapPromise;
183
+
184
+ // Execute onReady securely outside the promise resolution to prevent deadlock
185
+ // when getComponentInstance is used by shell service.
186
+ if (opts.onReady) {
187
+
188
+ // Re-fetch the loaded app wrapper dynamically
189
+ const getApp = () => ((ibGibGlobalThis as any).appInstance as TApp) || null;
190
+ await opts.onReady(getApp());
191
+ }
192
+
193
+ } catch (error) {
194
+ console.error(`${lc} ${extractErrorMsg(error)}`);
195
+ throw error;
196
+ } finally {
197
+ if (logalot) { console.log(`\n${lc} complete.`); }
198
+ }
199
+ }
200
+
201
+ async function execFromArgs<TApp extends AppBase_V1<any, any, any, any, any, any, any, any>>({
202
+ opts, args, argInfos, logalot,
203
+ }: {
204
+ opts: BootstrapIbGibAppOptions<TApp>,
205
+ args: string[],
206
+ argInfos: RCLIArgInfo[],
207
+ logalot: boolean
208
+ }): Promise<void> {
209
+ const lc = `[${execFromArgs.name}]`;
210
+ try {
211
+ if (logalot) { console.log(`${lc} starting...`); }
212
+
213
+ const metaspace = await initMetaspace({ opts, logalot });
214
+
215
+ const ibgibGlobalThis_core = getIbGibGlobalThis_IbGib();
216
+ ibgibGlobalThis_core.metaspace = metaspace;
217
+
218
+ const ibgibGlobalThis = opts.getGlobalThis(opts.config);
219
+ ibgibGlobalThis.metaspace = metaspace;
220
+
221
+ if (opts.ensureTags) {
222
+ await ensureTagsExist({ metaspace, tags: opts.ensureTags, logalot });
223
+ }
224
+
225
+ if (opts.registerAgentFunctionInfos) {
226
+ opts.registerAgentFunctionInfos();
227
+ }
228
+
229
+ const resRequestCommentIbGib = await createRequestCommentIbGib_Common({
230
+ args, interpretedArgInfos: argInfos,
231
+ });
232
+
233
+ await metaspace.persistTransformResult({ resTransform: resRequestCommentIbGib });
234
+ ibgibGlobalThis.initialCommentIbGib = resRequestCommentIbGib.newIbGib;
235
+
236
+ const proxy = new LiveProxyIbGib();
237
+ await proxy.initialized;
238
+ proxy.setWrappedIbGib({ ibGib: resRequestCommentIbGib.newIbGib });
239
+ ibgibGlobalThis.initialCommentIbGibProxy = proxy;
240
+
241
+ if (opts.resolveIdentity) {
242
+ ibgibGlobalThis.identity = await opts.resolveIdentity(metaspace);
243
+ }
244
+
245
+ await execInteractive({ opts, resRequestCommentIbGib, metaspace, logalot });
246
+
247
+ } catch (error) {
248
+ console.error(`${lc} ${extractErrorMsg(error)}`);
249
+ throw error;
250
+ } finally {
251
+ if (logalot) { console.log(`\n${lc} complete.`); }
252
+ }
253
+ }
254
+
255
+ async function initMetaspace<TApp extends AppBase_V1<any, any, any, any, any, any, any, any>>({
256
+ opts, logalot
257
+ }: {
258
+ opts: BootstrapIbGibAppOptions<TApp>,
259
+ logalot: boolean
260
+ }): Promise<MetaspaceService> {
261
+ const lc = `[${initMetaspace.name}]`;
262
+ try {
263
+ if (logalot) { console.log(`${lc} starting...`); }
264
+
265
+ let metaspace = new Metaspace_Webspace(/*cacheSvc*/undefined);
266
+
267
+ const localSpaceNameKey = opts.localSpaceNamePrefix
268
+ ? `${opts.localSpaceNamePrefix}_local_space_name`
269
+ : `local_space_name`;
270
+
271
+ const existingLocalSpaceName = await storageGet({
272
+ dbName: opts.config.dbName,
273
+ storeName: opts.config.storeName,
274
+ key: localSpaceNameKey,
275
+ });
276
+
277
+ const prefix = opts.localSpaceNamePrefix ?? 'app_space';
278
+ const spaceName = existingLocalSpaceName
279
+ ? undefined
280
+ : await generateLocalSpaceName(prefix);
281
+
282
+ await metaspace.initialize({
283
+ spaceName,
284
+ metaspaceFactory: undefined,
285
+ getFnAlert: () => ({ title, msg }) => alertUser({ title, msg }),
286
+ getFnPrompt: () => ({ title, msg }) => promptForText({ title, msg, confirm: false }),
287
+ getFnPromptPassword: () => () => promptForSecret({ confirm: true }),
288
+ });
289
+
290
+ return metaspace;
291
+ } catch (error) {
292
+ console.error(`${lc} ${extractErrorMsg(error)}`);
293
+ throw error;
294
+ } finally {
295
+ if (logalot) { console.log(`${lc} complete.`); }
296
+ }
297
+ }
298
+
299
+ async function generateLocalSpaceName(prefix: string): Promise<string> {
300
+ const name = prefix + '_' + pickRandom_Letters({ count: 12 });
301
+ if (!SPACE_NAME_REGEXP.test(name)) {
302
+ throw new Error(`(UNEXPECTED) generated space name (${name}) failed regexp`);
303
+ }
304
+ return name;
305
+ }
306
+
307
+ async function execInteractive<TApp extends AppBase_V1<any, any, any, any, any, any, any, any>>({
308
+ opts, resRequestCommentIbGib, metaspace, logalot
309
+ }: {
310
+ opts: BootstrapIbGibAppOptions<TApp>,
311
+ resRequestCommentIbGib: TransformResult<RequestCommentIbGib_V1>,
312
+ metaspace: MetaspaceService,
313
+ logalot: boolean
314
+ }): Promise<void> {
315
+ const lc = `[${execInteractive.name}]`;
316
+ try {
317
+ if (logalot) { console.log(`${lc} starting...`); }
318
+
319
+ const localUserSpace = await metaspace.getLocalUserSpace({ lock: false });
320
+ if (!localUserSpace) {
321
+ throw new Error(`(UNEXPECTED) localUserSpace falsy?`);
322
+ }
323
+
324
+ const app = opts.fnGetOrCreateApp
325
+ ? await opts.fnGetOrCreateApp(metaspace, localUserSpace)
326
+ : await defaultGetOrCreateApp({
327
+ AppClass: opts.AppClass,
328
+ defaultAppData: opts.defaultAppData,
329
+ onBeforeCreate: opts.onBeforeCreate,
330
+ onAppLoaded: opts.onAppLoaded,
331
+ metaspace,
332
+ space: localUserSpace,
333
+ });
334
+
335
+ // Track the app so `onReady` can access it cleanly
336
+ const ibgibGlobalThis = opts.getGlobalThis(opts.config);
337
+ (ibgibGlobalThis as any).appInstance = app;
338
+
339
+ app.metaspace = metaspace;
340
+ await app.witness(resRequestCommentIbGib.newIbGib);
341
+ } catch (error) {
342
+ console.error(`${lc} ${extractErrorMsg(error)}`);
343
+ throw error;
344
+ } finally {
345
+ if (logalot) { console.log(`${lc} complete.`); }
346
+ }
347
+ }
348
+
349
+ async function defaultGetOrCreateApp<TApp extends AppBase_V1<any, any, any, any, any, any, any, any>>({
350
+ AppClass,
351
+ defaultAppData,
352
+ onBeforeCreate,
353
+ onAppLoaded,
354
+ metaspace,
355
+ space,
356
+ }: {
357
+ AppClass: new (data?: any, rel8ns?: any) => TApp,
358
+ defaultAppData: any,
359
+ onBeforeCreate?: (metaspace: MetaspaceService, space: IbGibSpaceAny) => Promise<void>,
360
+ onAppLoaded?: (app: TApp, isNew: boolean) => Promise<TApp>,
361
+ metaspace: MetaspaceService,
362
+ space: IbGibSpaceAny,
363
+ }): Promise<TApp> {
364
+ const lc = `[${defaultGetOrCreateApp.name}]`;
365
+ const logalot = true;
366
+
367
+ try {
368
+ if (logalot) { console.log(`${lc} starting...`); }
369
+
370
+ let appsIbGib = await metaspace.getSpecialIbGib({
371
+ type: SpecialIbGibType.apps,
372
+ initialize: false, lock: false, space,
373
+ });
374
+
375
+ if (!appsIbGib) { throw new Error(`(UNEXPECTED) no apps index ibgib`); }
376
+ if (!appsIbGib.rel8ns) { throw new Error(`(UNEXPECTED) appsIbGib.rel8ns falsy`); }
377
+
378
+ const appAddrs = (appsIbGib.rel8ns[APP_REL8N_NAME] ?? [] as IbGibAddr[])
379
+ .filter(addr => {
380
+ const { ib } = getIbAndGib({ ibGibAddr: addr });
381
+ return getInfoFromAppIb({ appIb: ib }).appClassname === AppClass.name;
382
+ });
383
+
384
+ let app: TApp;
385
+ let appAddr: IbGibAddr;
386
+ let isNew = false;
387
+
388
+ if (appAddrs.length === 0) {
389
+ isNew = true;
390
+ if (onBeforeCreate) {
391
+ await onBeforeCreate(metaspace, space);
392
+ }
393
+ app = await createAndSaveNewApp({ AppClass, defaultAppData, metaspace, space });
394
+ appAddr = getIbGibAddr({ ibGib: app });
395
+ } else if (appAddrs.length === 1) {
396
+ appAddr = appAddrs[0];
397
+ } else {
398
+ const list = appAddrs
399
+ .map(a => getIbAndGib({ ibGibAddr: a }).ib)
400
+ .map((ib, idx) => `\n${idx}) ${ib}`);
401
+
402
+ let idx: number | undefined;
403
+ let retry = false;
404
+ do {
405
+ const response = await promptForText({
406
+ title: 'Which App?',
407
+ msg: `${retry ? 'huh? ' : ''}Multiple apps found. Which one?${list}`,
408
+ });
409
+ const parsed = Number.parseInt(response);
410
+ retry = Number.isNaN(parsed) || parsed >= appAddrs.length;
411
+ if (!retry) { idx = parsed; }
412
+ } while (retry);
413
+ appAddr = appAddrs[idx!];
414
+ }
415
+
416
+ const resGet = await getFromSpace({ addr: appAddr, space });
417
+ if (resGet.success && resGet.ibGibs?.length === 1) {
418
+ app = resGet.ibGibs[0] as TApp;
419
+ } else {
420
+ throw new Error(`Couldn't get app (${appAddr}).`);
421
+ }
422
+
423
+ if (!app.witness) {
424
+ const witness = new AppClass(undefined, undefined);
425
+ await witness.loadIbGibDto(app);
426
+ app = witness;
427
+ }
428
+
429
+ if (onAppLoaded) {
430
+ app = await onAppLoaded(app, isNew);
431
+ }
432
+
433
+ return app;
434
+ } catch (error) {
435
+ console.error(`${lc} ${extractErrorMsg(error)}`);
436
+ throw error;
437
+ } finally {
438
+ if (logalot) { console.log(`${lc} complete.`); }
439
+ }
440
+ }
441
+
442
+ async function createAndSaveNewApp<TApp extends AppBase_V1<any, any, any, any, any, any, any, any>>({
443
+ AppClass,
444
+ defaultAppData,
445
+ metaspace,
446
+ space,
447
+ }: {
448
+ AppClass: new (data?: any, rel8ns?: any) => TApp,
449
+ defaultAppData: any,
450
+ metaspace: MetaspaceService,
451
+ space: IbGibSpaceAny
452
+ }): Promise<TApp> {
453
+ const lc = `[${createAndSaveNewApp.name}]`;
454
+ const logalot = true;
455
+
456
+ try {
457
+ if (logalot) { console.log(`${lc} starting...`); }
458
+
459
+ const fnPromptApp = (() => {
460
+ return async () => {
461
+ const name = `${AppClass.name}_${pickRandom_Letters({ count: 8 })}`;
462
+ const data = clone(defaultAppData);
463
+ data.name = name;
464
+ data.uuid = await getUUID();
465
+ const rel8ns = undefined; // App templates usually have DEFAULT_APP_REL8NS as undefined
466
+
467
+ const { classname } = data;
468
+ const ib = getAppIb({ appData: data, classname });
469
+ return await factory.firstGen({
470
+ ib,
471
+ parentIbGib: factory.primitive({ ib: `app ${classname}` }),
472
+ data, rel8ns,
473
+ dna: true,
474
+ linkedRel8ns: [Rel8n.ancestor, Rel8n.past],
475
+ nCounter: true,
476
+ tjp: { timestamp: true },
477
+ }) as TransformResult<IbGibAppAny>;
478
+ };
479
+ })();
480
+
481
+ const app = await createNewApp({
482
+ fnPromptApp,
483
+ space,
484
+ ibgibs: metaspace,
485
+ });
486
+
487
+ if (!app) { throw new Error(`Failed to create app (falsy).`); }
488
+ if (app?.data?.classname !== AppClass.name) {
489
+ throw new Error(`(UNEXPECTED) wrong app classname.`);
490
+ }
491
+ return app as TApp;
492
+ } catch (error) {
493
+ console.error(`${lc} ${extractErrorMsg(error)}`);
494
+ throw error;
495
+ } finally {
496
+ if (logalot) { console.log(`${lc} complete.`); }
497
+ }
498
+ }
499
+
500
+ async function ensureTagsExist({
501
+ metaspace, tags, logalot
502
+ }: {
503
+ metaspace: MetaspaceService,
504
+ tags: TagConfig[],
505
+ logalot: boolean
506
+ }): Promise<void> {
507
+ const lc = `[${ensureTagsExist.name}]`;
508
+ try {
509
+ if (logalot) { console.log(`${lc} starting...`); }
510
+ const tagIbGibs = await metaspace.getSpecialRel8dIbGibs({
511
+ type: "tags", rel8nName: TAG_REL8N_NAME,
512
+ }) as TagIbGib_V1[];
513
+
514
+ for (const tag of tags) {
515
+ if (!tagIbGibs.some(x => x.data?.text === tag.text)) {
516
+ await metaspace.createTagIbGib({
517
+ text: tag.text,
518
+ icon: tag.icon,
519
+ description: tag.description,
520
+ space: undefined,
521
+ });
522
+ }
523
+ }
524
+ } catch (error) {
525
+ console.error(`${lc} ${extractErrorMsg(error)}`);
526
+ throw error;
527
+ } finally {
528
+ if (logalot) { console.log(`${lc} complete.`); }
529
+ }
530
+ }
531
+
532
+ export async function createRequestCommentIbGib_Common({
533
+ args, interpretedArgInfos,
534
+ }: {
535
+ args: string[],
536
+ interpretedArgInfos: RCLIArgInfo[],
537
+ }): Promise<TransformResult<RequestCommentIbGib_V1>> {
538
+ const text = args.join(' ');
539
+ const data: RequestCommentData_V1 = {
540
+ uuid: await getUUID(),
541
+ text,
542
+ args,
543
+ interpretedArgInfos,
544
+ textTimestamp: getTimestamp(),
545
+ };
546
+
547
+ return await factory.firstGen({
548
+ parentIbGib: factory.primitive({ ib: 'comment' }),
549
+ ib: `comment ${text.substring(0, 32).replace(/ /g, '_')}`,
550
+ data,
551
+ dna: true,
552
+ tjp: { uuid: true, timestamp: true },
553
+ nCounter: true,
554
+ }) as TransformResult<RequestCommentIbGib_V1>;
555
+ }
556
+
557
+ export function validateParamInfos({ paramInfos }: { paramInfos: RCLIParamInfo[] }): string[] {
558
+ const errors: string[] = [];
559
+ const identifiers: string[] = [];
560
+ paramInfos.forEach(p => {
561
+ const names = [p.name, ...(p.synonyms ?? [])];
562
+ names.forEach(name => {
563
+ if (identifiers.includes(name.toLowerCase())) {
564
+ errors.push(`Duplicate identifier found: ${name}`);
565
+ }
566
+ identifiers.push(name.toLowerCase());
567
+ });
568
+ });
569
+ return errors;
570
+ }