@ibgib/web-gib 0.0.28 → 0.0.29

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.
@@ -0,0 +1,243 @@
1
+ /**
2
+ * @module helpers.web
3
+ *
4
+ * Shared web helper functions for ibgib-based frontend apps.
5
+ *
6
+ * ## what belongs here
7
+ *
8
+ * Functions that are identical across all ibgib apps and depend only on the
9
+ * IbGibAmbientContextConfig seam. App-specific logic (typed globalThis
10
+ * accessors, component helpers) belongs in each app's own helpers.web.mts.
11
+ */
12
+
13
+ import { delay, extractErrorMsg } from "@ibgib/helper-gib/dist/helpers/utils-helper.mjs";
14
+
15
+ import { storageGet } from "../storage/storage-helpers.web.mjs";
16
+ import { initAppStorage } from "../helpers.web.mjs";
17
+ import { getGlobalMetaspace_waitIfNeeded } from "../helpers.mjs";
18
+ import { IbGibDynamicComponentMetaCtorOpts } from "../ui/component/component-types.mjs";
19
+ import { IbGibGlobalThisInfo } from "../types.mjs";
20
+ import { IbGibAmbientContextConfig, IbGibGlobalThis_Common, IbGibGlobalThis_IbGibApp } from "./types.mjs";
21
+
22
+ // ---------------------------------------------------------------------------
23
+ // Storage initialization
24
+ // ---------------------------------------------------------------------------
25
+
26
+ /**
27
+ * Initializes the IndexedDB stores required by an ibgib app.
28
+ *
29
+ * Call once on DOMContentLoaded, before the bootstrap script is dynamically
30
+ * loaded. This ensures storage is available when the metaspace initializes.
31
+ */
32
+ export async function initIbGibStorage(config: IbGibAmbientContextConfig): Promise<void> {
33
+ const lc = `[${initIbGibStorage.name}]`;
34
+ try {
35
+ await initAppStorage({
36
+ infos: [
37
+ {
38
+ dbName: config.dbName,
39
+ storeNames: [config.storeName, ...(config.additionalStoreNames ?? [])],
40
+ },
41
+ ],
42
+ });
43
+ } catch (error) {
44
+ console.error(`${lc} ${extractErrorMsg(error)}`);
45
+ throw error;
46
+ }
47
+ }
48
+
49
+ // ---------------------------------------------------------------------------
50
+ // globalThis initialization
51
+ // ---------------------------------------------------------------------------
52
+
53
+ /**
54
+ * Sets up globalThis.ibgib.[globalThisKey] for the given app config.
55
+ *
56
+ * Idempotent — safe to call multiple times. The root `globalThis.ibgib` object
57
+ * is created if it doesn't exist, then the per-app sub-key is populated.
58
+ *
59
+ * @param config The app's ambient context config.
60
+ * @param globalThisKey The snake_case key for this app's namespace under
61
+ * `globalThis.ibgib` (e.g. `'my_app'`, `'cool_new_thing'`).
62
+ * @param version Optional semver string (from AUTO_GENERATED_VERSION).
63
+ */
64
+ export function initIbGibGlobalThis(
65
+ config: IbGibAmbientContextConfig,
66
+ globalThisKey: string,
67
+ version?: string,
68
+ ): void {
69
+ const lc = `[${initIbGibGlobalThis.name}]`;
70
+ try {
71
+ // Ensure the root ibgib object exists
72
+ if (!(globalThis as any).ibgib) {
73
+ (globalThis as any).ibgib = {
74
+ dbName_hack: config.dbName,
75
+ apiKeyName_hack: config.apiKeyName,
76
+ storeName_hack: config.storeName,
77
+ } satisfies IbGibGlobalThisInfo;
78
+ }
79
+
80
+ // Populate the per-app namespace
81
+ if (!(globalThis as any).ibgib[globalThisKey]) {
82
+ (globalThis as any).ibgib[globalThisKey] = {
83
+ version,
84
+ spaceShim: {},
85
+ fnDefaultGetAPIKey: async () => {
86
+ const apiKey = await storageGet({
87
+ dbName: config.dbName,
88
+ storeName: config.storeName,
89
+ key: config.apiKeyName,
90
+ });
91
+ return apiKey ?? '';
92
+ },
93
+ dbName_hack: config.dbName,
94
+ apiKeyName_hack: config.apiKeyName,
95
+ storeName_hack: config.storeName,
96
+ } satisfies IbGibGlobalThis_IbGibApp;
97
+ }
98
+ } catch (error) {
99
+ console.error(`${lc} ${extractErrorMsg(error)}`);
100
+ throw error;
101
+ }
102
+ }
103
+
104
+ // ---------------------------------------------------------------------------
105
+ // globalThis accessors
106
+ // ---------------------------------------------------------------------------
107
+
108
+ /**
109
+ * Returns the raw IbGibGlobalThis_Common for the given key.
110
+ * Typed as the Common base — use the app-specific typed accessor for
111
+ * full-type access.
112
+ */
113
+ export function getIbGibGlobalThis_Common(globalThisKey: string): IbGibGlobalThis_Common {
114
+ const lc = `[${getIbGibGlobalThis_Common.name}]`;
115
+ const g = (globalThis as any).ibgib?.[globalThisKey];
116
+ if (!g) {
117
+ throw new Error(`${lc} (UNEXPECTED) globalThis.ibgib.${globalThisKey} not initialized. (E: 8d621732ibgib-commonfe25)`);
118
+ }
119
+ return g as IbGibGlobalThis_Common;
120
+ }
121
+
122
+ /**
123
+ * Returns the IbGibGlobalThis_IbGibApp for the given key, initializing it
124
+ * from config if not yet present.
125
+ */
126
+ export function getIbGibGlobalThis_IbGibApp(
127
+ globalThisKey: string,
128
+ config?: IbGibAmbientContextConfig,
129
+ version?: string,
130
+ ): IbGibGlobalThis_IbGibApp {
131
+ if (!(globalThis as any).ibgib?.[globalThisKey]) {
132
+ if (!config) {
133
+ throw new Error(
134
+ `Global context not initialized and config not provided for key '${globalThisKey}'.`
135
+ );
136
+ }
137
+ initIbGibGlobalThis(config, globalThisKey, version);
138
+ }
139
+ return (globalThis as any).ibgib[globalThisKey] as IbGibGlobalThis_IbGibApp;
140
+ }
141
+
142
+ // ---------------------------------------------------------------------------
143
+ // API key helper
144
+ // ---------------------------------------------------------------------------
145
+
146
+ /**
147
+ * Returns a function that retrieves the AI API key from storage at call time.
148
+ * The globalThisKey links back to the per-app namespace for DB/store/key names.
149
+ */
150
+ export function getDefaultFnGetAPIKey(
151
+ globalThisKey: string,
152
+ ): () => Promise<string> {
153
+ return async () => {
154
+ const gb = getIbGibGlobalThis_IbGibApp(globalThisKey);
155
+ return (await storageGet({
156
+ dbName: gb.dbName_hack,
157
+ storeName: gb.storeName_hack,
158
+ key: gb.apiKeyName_hack,
159
+ })) ?? '';
160
+ };
161
+ }
162
+
163
+ // ---------------------------------------------------------------------------
164
+ // Dynamic bootstrap loading
165
+ // ---------------------------------------------------------------------------
166
+
167
+ /**
168
+ * Dynamically imports the bootstrap module at the given path and calls the
169
+ * named export bootstrap function.
170
+ *
171
+ * Called after DOMContentLoaded so the heavy ibgib import graph does not block
172
+ * first paint.
173
+ *
174
+ * @param path Path to bootstrap.mjs (relative to the app's index.mts).
175
+ * @param bootstrapFnName The exported async function to call. Defaults to
176
+ * `'bootstrapApp'` — override with the app-specific function name
177
+ * (e.g. `'bootstrapMyAppApp'`).
178
+ */
179
+ export async function dynamicallyLoadBootstrapScript(
180
+ path: string,
181
+ bootstrapFnName: string = 'bootstrapApp',
182
+ ): Promise<void> {
183
+ const lc = `[${dynamicallyLoadBootstrapScript.name}]`;
184
+ try {
185
+ const module = await import(path);
186
+ const fn = module[bootstrapFnName];
187
+ if (typeof fn !== 'function') {
188
+ throw new Error(
189
+ `${lc} Bootstrap function '${bootstrapFnName}' not found in '${path}'. ` +
190
+ `Available exports: ${Object.keys(module).join(', ')} (E: 9136085eibgib-common26)`
191
+ );
192
+ }
193
+ await fn();
194
+ } catch (error) {
195
+ console.error(`${lc} ${extractErrorMsg(error)}`);
196
+ throw error;
197
+ }
198
+ }
199
+
200
+ // ---------------------------------------------------------------------------
201
+ // Component constructor args helper
202
+ // ---------------------------------------------------------------------------
203
+
204
+ /**
205
+ * Returns the standard ctor options object for IbGib dynamic components.
206
+ *
207
+ * Provides two things components need at construction time:
208
+ * 1. `fnGetMetaspace` — accesses the global metaspace once initialized
209
+ * 2. `bootstrapPromise` — components can await this to defer their own init
210
+ * until the App witness and metaspace are fully ready
211
+ *
212
+ * @param globalThisKey The per-app namespace key used to find the bootstrap
213
+ * promise on globalThis.
214
+ */
215
+ export function getComponentCtorArg(globalThisKey: string): IbGibDynamicComponentMetaCtorOpts {
216
+ const fnGetMetaspace = async () => getGlobalMetaspace_waitIfNeeded();
217
+
218
+ const bootstrapPromiseWrapper = new Promise<void>(async (resolve, reject) => {
219
+ try {
220
+ let maxTries = 100_000;
221
+ let counter = 0;
222
+ let bootstrapPromise: Promise<void> | undefined;
223
+ do {
224
+ bootstrapPromise =
225
+ getIbGibGlobalThis_IbGibApp(globalThisKey)?.bootstrapPromise;
226
+ counter++;
227
+ if (counter > maxTries) { break; }
228
+ await delay(10);
229
+ } while (bootstrapPromise === undefined);
230
+ if (!bootstrapPromise) {
231
+ throw new Error(
232
+ `Timed out waiting for bootstrapPromise on key '${globalThisKey}'. (E: 4048ce92ibgib-common26)`
233
+ );
234
+ }
235
+ await bootstrapPromise;
236
+ resolve();
237
+ } catch (error) {
238
+ reject(error);
239
+ }
240
+ });
241
+
242
+ return { fnGetMetaspace, bootstrapPromise: bootstrapPromiseWrapper };
243
+ }
@@ -0,0 +1,190 @@
1
+ /**
2
+ * @module types
3
+ *
4
+ * Shared type definitions for ibgib-based frontend apps.
5
+ *
6
+ * ## notes
7
+ *
8
+ * Three layers of globalThis state:
9
+ * IbGibGlobalThisInfo (@ibgib/web-gib — the root ibgib slot)
10
+ * └── IbGibGlobalThis_Common (bootstrap promise tracking)
11
+ * └── IbGibGlobalThis_IbGibApp (all ibgib App witness apps)
12
+ * └── IbGibGlobalThis_[AppName] (per-app extensions)
13
+ */
14
+
15
+ import { IbGibRel8ns_V1, IbGib_V1 } from "@ibgib/ts-gib/dist/V1/types.mjs";
16
+ import { CommentData_V1 } from "@ibgib/core-gib/dist/common/comment/comment-types.mjs";
17
+ import { RCLIArgInfo } from "@ibgib/helper-gib/dist/rcli/rcli-types.mjs";
18
+
19
+ import { LiveProxyIbGib } from "../witness/live-proxy-ibgib/live-proxy-ibgib-one-file.mjs";
20
+ import { IbGibGlobalThisInfo } from "../types.mjs";
21
+
22
+ // ---------------------------------------------------------------------------
23
+ // App ambient context config
24
+ // ---------------------------------------------------------------------------
25
+
26
+ /**
27
+ * Configuration object that parameterizes an ibgib app's storage substrates.
28
+ *
29
+ * This is the primary seam between the framework layer and a specific app.
30
+ * Every downstream app creates exactly one APP_CONFIG and passes it to the
31
+ * framework helpers.
32
+ *
33
+ * ## notes
34
+ *
35
+ * Intended to be upstreamed into @ibgib/web-gib.
36
+ */
37
+ export interface IbGibAmbientContextConfig {
38
+ /** Target IndexedDB database name */
39
+ dbName: string;
40
+ /** Primary object store name */
41
+ storeName: string;
42
+ /** Additional stores to initialize alongside the primary (e.g., ZERO_SPACE_ID) */
43
+ additionalStoreNames?: string[];
44
+ /** The IndexedDB key under which the AI API key is persisted */
45
+ apiKeyName: string;
46
+ }
47
+
48
+ // ---------------------------------------------------------------------------
49
+ // Space shim (RCLI pathing compat layer)
50
+ // ---------------------------------------------------------------------------
51
+
52
+ /**
53
+ * Per-space shim that mimics a filesystem CWD context for ibgib RCLI compat.
54
+ */
55
+ export interface SpaceShimGlobalInfo {
56
+ /**
57
+ * The initial cwd() value when the RCLI was started. Used as the context
58
+ * path the user types commands from.
59
+ */
60
+ initialCwd: string;
61
+ /** The active CWD, updated by pass-through `cd` commands. */
62
+ cwd: string;
63
+ }
64
+
65
+ // ---------------------------------------------------------------------------
66
+ // Bootstrap request comment (RCLI plumbing)
67
+ // ---------------------------------------------------------------------------
68
+
69
+ /** @see {@link RequestCommentIbGib_V1} */
70
+ export interface RequestCommentData_V1 extends CommentData_V1 {
71
+ /** As close to the raw args as we can get. */
72
+ args: string[];
73
+ /** Interpreted infos for args. */
74
+ interpretedArgInfos: RCLIArgInfo[];
75
+ }
76
+
77
+ export interface RequestCommentRel8ns_V1 extends IbGibRel8ns_V1 { }
78
+
79
+ /**
80
+ * Special Comment ibgib that acts as a "request" — the command analog from RCLI.
81
+ *
82
+ * The raw text of the command is `text` from `CommentData_V1`.
83
+ */
84
+ export interface RequestCommentIbGib_V1
85
+ extends IbGib_V1<RequestCommentData_V1, RequestCommentRel8ns_V1> {
86
+ /** Kluge for one-off commandlines during initial development. */
87
+ oneOff: boolean;
88
+ }
89
+
90
+ // ---------------------------------------------------------------------------
91
+ // Identity (optional — WIP as of April 2026)
92
+ // ---------------------------------------------------------------------------
93
+
94
+ /**
95
+ * Framework-level identity interface.
96
+ *
97
+ * ## design notes
98
+ *
99
+ * The implementation is entirely consumer-supplied. The framework defines only
100
+ * the shape of the slot so shells and components can react to auth state
101
+ * without coupling to a specific identity mechanism.
102
+ *
103
+ * For ibgib apps, the typical implementation will be keystone-based (a Merkle
104
+ * DAG proof of cryptographic identity continuity), not token-based.
105
+ *
106
+ * ## WIP caveat (atow April 2026)
107
+ *
108
+ * The keystone identity implementation in @ibgib/core-gib is still under
109
+ * active development. This interface represents the intended framework
110
+ * contract. Check the ibgib-libs sync/keystone module for the current
111
+ * implementation status before using.
112
+ */
113
+ export interface IbGibIdentityContext {
114
+ /** Whether identity resolution has completed (does not imply authenticated). */
115
+ resolved: boolean;
116
+ /** Whether the current session has a verified identity. */
117
+ authenticated: boolean;
118
+ /**
119
+ * App-specific identity payload.
120
+ * For ibgib: typically `{ keystoneAddr, userIbGibAddr }`.
121
+ */
122
+ data?: Record<string, any>;
123
+ }
124
+
125
+ // ---------------------------------------------------------------------------
126
+ // GlobalThis layers
127
+ // ---------------------------------------------------------------------------
128
+
129
+ /**
130
+ * Layer 1: Fields shared across ALL ibgib web apps.
131
+ *
132
+ * Extends IbGibGlobalThisInfo from @ibgib/web-gib (the root ibgib slot).
133
+ * Intended to be upstreamed into @ibgib/web-gib.
134
+ */
135
+ export interface IbGibGlobalThis_Common extends IbGibGlobalThisInfo {
136
+ /**
137
+ * Set to true when bootstrap has been triggered.
138
+ * There is no "bootstrapComplete" — await {@link bootstrapPromise} instead.
139
+ */
140
+ bootstrapStarted?: boolean;
141
+ /**
142
+ * The promise returned by the bootstrap function. Components await this
143
+ * to defer initialization until the App witness and metaspace are ready.
144
+ */
145
+ bootstrapPromise?: Promise<void>;
146
+ /**
147
+ * Singleton chronologys component. Should not be destroyed for the
148
+ * duration of the page session.
149
+ */
150
+ chronologysComponent?: any;
151
+ }
152
+
153
+ /**
154
+ * Layer 2: Fields shared by any app that follows the ibgib App witness pattern.
155
+ *
156
+ * Apps using the bootstrap factory pattern extend this interface and add only
157
+ * their app-specific fields.
158
+ *
159
+ * ## notes
160
+ *
161
+ * Intended to be upstreamed into @ibgib/web-gib as IbGibGlobalThis_IbGibApp.
162
+ */
163
+ export interface IbGibGlobalThis_IbGibApp extends IbGibGlobalThis_Common {
164
+ /** Semver string generated at build time. */
165
+ version?: string;
166
+ /** Per-space RCLI pathing shim. */
167
+ spaceShim: { [spaceId: string]: SpaceShimGlobalInfo };
168
+ /** When true, verbose output is enabled for the session. */
169
+ verbose?: boolean;
170
+ /**
171
+ * Looks up the AI API key from IndexedDB storage.
172
+ * Stored as a function so the key is never cached in memory at the global level.
173
+ */
174
+ fnDefaultGetAPIKey: () => Promise<string>;
175
+ /** The initial "command" comment ibgib created at bootstrap time. */
176
+ initialCommentIbGib?: IbGib_V1;
177
+ /** Live proxy wrapping {@link initialCommentIbGib} for reactive subscriptions. */
178
+ initialCommentIbGibProxy?: LiveProxyIbGib;
179
+ /**
180
+ * Optional identity context. Populated by the consumer's resolveIdentity()
181
+ * callback in the bootstrap options.
182
+ *
183
+ * - `undefined`: no identity mechanism configured
184
+ * - `null`: identity resolution was attempted; user is unauthenticated
185
+ * - `IbGibIdentityContext`: identity resolved (check `.authenticated`)
186
+ *
187
+ * @see {@link IbGibIdentityContext}
188
+ */
189
+ identity?: IbGibIdentityContext | null;
190
+ }
@@ -36,13 +36,13 @@ This skill is often the first step in creating a new app, or used to "ibgib-ify"
36
36
 
37
37
  | Token | Example value | How it's computed |
38
38
  |---|---|---|
39
- | `{{APP_HUMAN_NAME}}` | `PulmonIQ Learning` | Asked of developer |
40
- | `{{APP_NAME_UPPER}}` | `PULMONIQ_LEARNING` | Uppercase + underscores of app name |
41
- | `{{APP_CLASSNAME_PREFIX}}` | `PulmoniqLearning` | PascalCase, no suffix |
42
- | `{{APP_DIR_NAME}}` | `pulmoniq-learning` | kebab-case |
43
- | `{{DB_NAME}}` | `pulmoniq_learning_db` | Asked of developer |
44
- | `{{STORE_NAME}}` | `pulmoniq_learning_store` | Asked of developer |
45
- | `{{APP_UUID}}` | `8d6217327e6490650169be9cb22a0b25` | **Generated fresh** with `generate-id.js`. |
39
+ | `{{APP_HUMAN_NAME}}` | `My App` | Asked of developer |
40
+ | `{{APP_NAME_UPPER}}` | `MY_APP` | Uppercase + underscores of app name |
41
+ | `{{APP_CLASSNAME_PREFIX}}` | `MyApp` | PascalCase, no suffix |
42
+ | `{{APP_DIR_NAME}}` | `my-app` | kebab-case |
43
+ | `{{DB_NAME}}` | `my_app_db` | Asked of developer |
44
+ | `{{STORE_NAME}}` | `my_app_store` | Asked of developer |
45
+ | `{{APP_UUID}}` | `3a95fe65cbda2833b59cadff4e279826` | **Generated fresh** with `generate-id.js`. |
46
46
 
47
47
  ---
48
48
 
@@ -7,9 +7,6 @@ import { IbGibGlobalThisInfo } from "@ibgib/web-gib/dist/types.mjs";
7
7
 
8
8
  // ---------------------------------------------------------------------------
9
9
  // App-level ambient context config
10
- //
11
- // NOTE: This interface is intentionally agnostic to pulmoniq or any other
12
- // domain. It should eventually be upstreamed into @ibgib/web-gib.
13
10
  // ---------------------------------------------------------------------------
14
11
 
15
12
  /**