@egen-civitas/esm-extensions 1.0.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.
package/README.md ADDED
@@ -0,0 +1,4 @@
1
+ # egen-esm-extensions
2
+
3
+ `egen-esm-extensions` connects Extensions with Extension Slots.
4
+ It is part of the Egen Frontend [Extension System](https://github.com/egen-civitas/egen-rfc-frontend/pull/27/files).
@@ -0,0 +1,92 @@
1
+ /** @module @category Extension */
2
+ import { type AssignedExtension, type ExtensionInternalStore, type ExtensionRegistration, type ExtensionSlotCustomState } from './store.js';
3
+ /**
4
+ * Given an extension ID, which is a string uniquely identifying
5
+ * an instance of an extension within an extension slot, this
6
+ * returns the extension name.
7
+ *
8
+ * @example
9
+ * ```js
10
+ * getExtensionNameFromId("foo#bar")
11
+ * --> "foo"
12
+ * getExtensionNameFromId("baz")
13
+ * --> "baz"
14
+ * ```
15
+ */
16
+ export declare function getExtensionNameFromId(extensionId: string): string;
17
+ export declare function getExtensionRegistrationFrom(state: ExtensionInternalStore, extensionId: string): ExtensionRegistration | undefined;
18
+ export declare function getExtensionRegistration(extensionId: string): ExtensionRegistration | undefined;
19
+ /**
20
+ * Extensions must be registered in order to be rendered.
21
+ * This is handled by the app shell, when extensions are provided
22
+ * via the `routes.json` file and registered through `registerApp()`.
23
+ * @internal
24
+ */
25
+ export declare const registerExtension: (extensionRegistration: ExtensionRegistration) => void;
26
+ /**
27
+ * Attach an extension to an extension slot.
28
+ *
29
+ * This will cause the extension to be rendered into the specified
30
+ * extension slot, unless it is removed by configuration. Using
31
+ * `attach` is an alternative to specifying the `slot` or `slots`
32
+ * in the extension declaration.
33
+ *
34
+ * It is particularly useful when creating a slot into which
35
+ * you want to render an existing extension. This enables you
36
+ * to do so without modifying the extension's declaration, which
37
+ * may be impractical or inappropriate, for example if you are
38
+ * writing a module for a specific implementation.
39
+ *
40
+ * @param slotName a name uniquely identifying the slot
41
+ * @param extensionId an extension name, with an optional #-suffix
42
+ * to distinguish it from other instances of the same extension
43
+ * attached to the same slot.
44
+ */
45
+ export declare function attach(slotName: string, extensionId: string): void;
46
+ /**
47
+ * Detaches an extension from an extension slot.
48
+ *
49
+ * @param extensionSlotName The name of the extension slot to detach from.
50
+ * @param extensionId The ID of the extension to detach.
51
+ *
52
+ * @deprecated Avoid using this. Extension attachments should be considered declarative.
53
+ */
54
+ export declare function detach(extensionSlotName: string, extensionId: string): void;
55
+ /**
56
+ * Detaches all extensions from an extension slot.
57
+ *
58
+ * @param extensionSlotName The name of the extension slot to clear.
59
+ *
60
+ * @deprecated Avoid using this. Extension attachments should be considered declarative.
61
+ */
62
+ export declare function detachAll(extensionSlotName: string): void;
63
+ /**
64
+ * Gets the list of extensions assigned to a given slot
65
+ *
66
+ * @param slotName The slot to load the assigned extensions for
67
+ * @returns An array of extensions assigned to the named slot
68
+ */
69
+ export declare function getAssignedExtensions(slotName: string): Array<AssignedExtension>;
70
+ /**
71
+ * Used by by extension slots at mount time.
72
+ *
73
+ * @param moduleName The name of the module that contains the extension slot
74
+ * @param slotName The extension slot name that is actually used
75
+ * @param state Optional custom state for the slot, which will be stored in the extension store.
76
+ * @internal
77
+ */
78
+ export declare const registerExtensionSlot: (moduleName: string, slotName: string, state?: ExtensionSlotCustomState) => void;
79
+ /**
80
+ * Used by extension slots to update the copy of the state for the extension slot
81
+ *
82
+ * @param slotName The name of the slot with state to update
83
+ * @param state A copy of the new state
84
+ * @param partial Whether this should be applied as a partial
85
+ */
86
+ export declare function updateExtensionSlotState(slotName: string, state: ExtensionSlotCustomState, partial?: boolean): void;
87
+ /**
88
+ * @internal
89
+ * Just for testing.
90
+ */
91
+ export declare const reset: () => void;
92
+ //# sourceMappingURL=extensions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extensions.d.ts","sourceRoot":"","sources":["../src/extensions.ts"],"names":[],"mappings":"AAAA,kCAAkC;AA4BlC,OAAO,EACL,KAAK,iBAAiB,EACtB,KAAK,sBAAsB,EAC3B,KAAK,qBAAqB,EAC1B,KAAK,wBAAwB,EAM9B,MAAM,YAAY,CAAC;AAsHpB;;;;;;;;;;;;GAYG;AACH,wBAAgB,sBAAsB,CAAC,WAAW,EAAE,MAAM,UAGzD;AAED,wBAAgB,4BAA4B,CAC1C,KAAK,EAAE,sBAAsB,EAC7B,WAAW,EAAE,MAAM,GAClB,qBAAqB,GAAG,SAAS,CAGnC;AAED,wBAAgB,wBAAwB,CAAC,WAAW,EAAE,MAAM,GAAG,qBAAqB,GAAG,SAAS,CAG/F;AAED;;;;;GAKG;AACH,eAAO,MAAM,iBAAiB,EAAE,CAAC,qBAAqB,EAAE,qBAAqB,KAAK,IAO9E,CAAC;AAEL;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,QA4B3D;AAED;;;;;;;GAOG;AACH,wBAAgB,MAAM,CAAC,iBAAiB,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,QAmBpE;AAED;;;;;;GAMG;AACH,wBAAgB,SAAS,CAAC,iBAAiB,EAAE,MAAM,QAmBlD;AAqHD;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,GAAG,KAAK,CAAC,iBAAiB,CAAC,CAoBhF;AAwBD;;;;;;;GAOG;AACH,eAAO,MAAM,qBAAqB,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,wBAAwB,KAAK,IA2C5G,CAAC;AAEL;;;;;;GAMG;AACH,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,wBAAwB,EAAE,OAAO,GAAE,OAAe,QAcnH;AAED;;;GAGG;AACH,eAAO,MAAM,KAAK,EAAE,MAAM,IAMtB,CAAC"}
@@ -0,0 +1,394 @@
1
+ /** @module @category Extension */ /*
2
+ * We have the following extension modes:
3
+ *
4
+ * - attached (set via code in form of: attach, detach, ...)
5
+ * - configured (set via configuration in form of: added, removed, ...)
6
+ * - assigned (computed from attached and configured)
7
+ * - connected (computed from assigned using connectivity and online / offline)
8
+ */ import { sessionStore, userHasAccess } from "@egen-civitas/esm-api";
9
+ import { getExtensionConfigFromExtensionSlotStore, getExtensionConfigFromStore, getExtensionSlotConfig, getExtensionSlotConfigFromStore, getExtensionSlotsConfigStore, getExtensionsConfigStore } from "@egen-civitas/esm-config";
10
+ import { evaluateAsBoolean } from "@egen-civitas/esm-expression-evaluator";
11
+ import { featureFlagsStore } from "@egen-civitas/esm-feature-flags";
12
+ import { subscribeConnectivityChanged } from "@egen-civitas/esm-globals";
13
+ import { isOnline as isOnlineFn } from "@egen-civitas/esm-utils";
14
+ import { isEqual, merge } from "lodash-es";
15
+ import { checkStatusFor } from "./helpers.js";
16
+ import { getExtensionInternalStore, getExtensionStore, updateInternalExtensionStore } from "./store.js";
17
+ const extensionInternalStore = getExtensionInternalStore();
18
+ const extensionStore = getExtensionStore();
19
+ const slotsConfigStore = getExtensionSlotsConfigStore();
20
+ const extensionsConfigStore = getExtensionsConfigStore();
21
+ // Keep the output store updated
22
+ function updateExtensionOutputStore(internalState, extensionSlotConfigs, extensionsConfigStore, featureFlagStore, sessionStore) {
23
+ const slots = {};
24
+ const isOnline = isOnlineFn();
25
+ const enabledFeatureFlags = Object.entries(featureFlagStore.flags).filter(([, { enabled }])=>enabled).map(([name])=>name);
26
+ for (let [slotName, slot] of Object.entries(internalState.slots)){
27
+ const { config } = getExtensionSlotConfigFromStore(extensionSlotConfigs, slot.name);
28
+ const assignedExtensions = getAssignedExtensionsFromSlotData(slotName, internalState, config, extensionsConfigStore, enabledFeatureFlags, isOnline, sessionStore.session);
29
+ slots[slotName] = {
30
+ moduleName: slot.moduleName,
31
+ assignedExtensions
32
+ };
33
+ }
34
+ if (!isEqual(extensionStore.getState().slots, slots)) {
35
+ extensionStore.setState({
36
+ slots
37
+ });
38
+ }
39
+ }
40
+ extensionInternalStore.subscribe((internalStore)=>{
41
+ updateExtensionOutputStore(internalStore, slotsConfigStore.getState(), extensionsConfigStore.getState(), featureFlagsStore.getState(), sessionStore.getState());
42
+ });
43
+ slotsConfigStore.subscribe((slotConfigs)=>{
44
+ updateExtensionOutputStore(extensionInternalStore.getState(), slotConfigs, extensionsConfigStore.getState(), featureFlagsStore.getState(), sessionStore.getState());
45
+ });
46
+ extensionsConfigStore.subscribe((extensionConfigs)=>{
47
+ updateExtensionOutputStore(extensionInternalStore.getState(), slotsConfigStore.getState(), extensionConfigs, featureFlagsStore.getState(), sessionStore.getState());
48
+ });
49
+ featureFlagsStore.subscribe((featureFlagStore)=>{
50
+ updateExtensionOutputStore(extensionInternalStore.getState(), slotsConfigStore.getState(), extensionsConfigStore.getState(), featureFlagStore, sessionStore.getState());
51
+ });
52
+ sessionStore.subscribe((session)=>{
53
+ updateExtensionOutputStore(extensionInternalStore.getState(), slotsConfigStore.getState(), extensionsConfigStore.getState(), featureFlagsStore.getState(), session);
54
+ });
55
+ function updateOutputStoreToCurrent() {
56
+ updateExtensionOutputStore(extensionInternalStore.getState(), slotsConfigStore.getState(), extensionsConfigStore.getState(), featureFlagsStore.getState(), sessionStore.getState());
57
+ }
58
+ updateOutputStoreToCurrent();
59
+ subscribeConnectivityChanged(updateOutputStoreToCurrent);
60
+ function createNewExtensionSlotInfo(slotName, moduleName, state) {
61
+ return {
62
+ moduleName,
63
+ name: slotName,
64
+ attachedIds: [],
65
+ config: null,
66
+ state
67
+ };
68
+ }
69
+ /**
70
+ * Given an extension ID, which is a string uniquely identifying
71
+ * an instance of an extension within an extension slot, this
72
+ * returns the extension name.
73
+ *
74
+ * @example
75
+ * ```js
76
+ * getExtensionNameFromId("foo#bar")
77
+ * --> "foo"
78
+ * getExtensionNameFromId("baz")
79
+ * --> "baz"
80
+ * ```
81
+ */ export function getExtensionNameFromId(extensionId) {
82
+ const [extensionName] = extensionId.split('#');
83
+ return extensionName;
84
+ }
85
+ export function getExtensionRegistrationFrom(state, extensionId) {
86
+ const name = getExtensionNameFromId(extensionId);
87
+ return state.extensions[name];
88
+ }
89
+ export function getExtensionRegistration(extensionId) {
90
+ const state = extensionInternalStore.getState();
91
+ return getExtensionRegistrationFrom(state, extensionId);
92
+ }
93
+ /**
94
+ * Extensions must be registered in order to be rendered.
95
+ * This is handled by the app shell, when extensions are provided
96
+ * via the `routes.json` file and registered through `registerApp()`.
97
+ * @internal
98
+ */ export const registerExtension = (extensionRegistration)=>extensionInternalStore.setState((state)=>{
99
+ state.extensions[extensionRegistration.name] = {
100
+ ...extensionRegistration,
101
+ instances: []
102
+ };
103
+ return state;
104
+ });
105
+ /**
106
+ * Attach an extension to an extension slot.
107
+ *
108
+ * This will cause the extension to be rendered into the specified
109
+ * extension slot, unless it is removed by configuration. Using
110
+ * `attach` is an alternative to specifying the `slot` or `slots`
111
+ * in the extension declaration.
112
+ *
113
+ * It is particularly useful when creating a slot into which
114
+ * you want to render an existing extension. This enables you
115
+ * to do so without modifying the extension's declaration, which
116
+ * may be impractical or inappropriate, for example if you are
117
+ * writing a module for a specific implementation.
118
+ *
119
+ * @param slotName a name uniquely identifying the slot
120
+ * @param extensionId an extension name, with an optional #-suffix
121
+ * to distinguish it from other instances of the same extension
122
+ * attached to the same slot.
123
+ */ export function attach(slotName, extensionId) {
124
+ updateInternalExtensionStore((state)=>{
125
+ const existingSlot = state.slots[slotName];
126
+ if (!existingSlot) {
127
+ return {
128
+ ...state,
129
+ slots: {
130
+ ...state.slots,
131
+ [slotName]: {
132
+ ...createNewExtensionSlotInfo(slotName),
133
+ attachedIds: [
134
+ extensionId
135
+ ]
136
+ }
137
+ }
138
+ };
139
+ } else {
140
+ return {
141
+ ...state,
142
+ slots: {
143
+ ...state.slots,
144
+ [slotName]: {
145
+ ...existingSlot,
146
+ attachedIds: [
147
+ ...existingSlot.attachedIds,
148
+ extensionId
149
+ ]
150
+ }
151
+ }
152
+ };
153
+ }
154
+ });
155
+ }
156
+ /**
157
+ * Detaches an extension from an extension slot.
158
+ *
159
+ * @param extensionSlotName The name of the extension slot to detach from.
160
+ * @param extensionId The ID of the extension to detach.
161
+ *
162
+ * @deprecated Avoid using this. Extension attachments should be considered declarative.
163
+ */ export function detach(extensionSlotName, extensionId) {
164
+ updateInternalExtensionStore((state)=>{
165
+ const existingSlot = state.slots[extensionSlotName];
166
+ if (existingSlot && existingSlot.attachedIds.includes(extensionId)) {
167
+ return {
168
+ ...state,
169
+ slots: {
170
+ ...state.slots,
171
+ [extensionSlotName]: {
172
+ ...existingSlot,
173
+ attachedIds: existingSlot.attachedIds.filter((id)=>id !== extensionId)
174
+ }
175
+ }
176
+ };
177
+ } else {
178
+ return state;
179
+ }
180
+ });
181
+ }
182
+ /**
183
+ * Detaches all extensions from an extension slot.
184
+ *
185
+ * @param extensionSlotName The name of the extension slot to clear.
186
+ *
187
+ * @deprecated Avoid using this. Extension attachments should be considered declarative.
188
+ */ export function detachAll(extensionSlotName) {
189
+ updateInternalExtensionStore((state)=>{
190
+ const existingSlot = state.slots[extensionSlotName];
191
+ if (existingSlot) {
192
+ return {
193
+ ...state,
194
+ slots: {
195
+ ...state.slots,
196
+ [extensionSlotName]: {
197
+ ...existingSlot,
198
+ attachedIds: []
199
+ }
200
+ }
201
+ };
202
+ } else {
203
+ return state;
204
+ }
205
+ });
206
+ }
207
+ /**
208
+ * Get an order index for the extension. This will
209
+ * come from either its configured order, its registered order
210
+ * parameter, or the order in which it happened to be attached.
211
+ */ function getOrder(extensionId, configuredOrder, registeredOrderIndex, attachedOrder) {
212
+ const configuredIndex = configuredOrder.indexOf(extensionId);
213
+ if (configuredIndex !== -1) {
214
+ return configuredIndex;
215
+ } else if (registeredOrderIndex !== undefined) {
216
+ // extensions that don't have a configured order should appear after those that do
217
+ return 1000 + registeredOrderIndex;
218
+ } else {
219
+ const assignedIndex = attachedOrder.indexOf(extensionId);
220
+ if (assignedIndex !== -1) {
221
+ // extensions that have neither a configured nor registered order should appear
222
+ // after all others
223
+ return 2000 + assignedIndex;
224
+ } else {
225
+ return -1;
226
+ }
227
+ }
228
+ }
229
+ function getAssignedExtensionsFromSlotData(slotName, internalState, config, extensionConfigStoreState, enabledFeatureFlags, isOnline, session) {
230
+ const attachedIds = internalState.slots[slotName].attachedIds;
231
+ const assignedIds = calculateAssignedIds(config, attachedIds);
232
+ const extensions = [];
233
+ // Create context once for all extensions in this slot
234
+ const slotState = internalState.slots[slotName]?.state;
235
+ const expressionContext = slotState && typeof slotState === 'object' ? {
236
+ session,
237
+ ...slotState
238
+ } : {
239
+ session
240
+ };
241
+ for (let id of assignedIds){
242
+ const { config: rawExtensionConfig } = getExtensionConfigFromStore(extensionConfigStoreState, slotName, id);
243
+ const rawExtensionSlotExtensionConfig = getExtensionConfigFromExtensionSlotStore(config, slotName, id);
244
+ const extensionConfig = merge(rawExtensionConfig, rawExtensionSlotExtensionConfig);
245
+ const name = getExtensionNameFromId(id);
246
+ const extension = internalState.extensions[name];
247
+ // if the extension has not been registered yet, do not include it
248
+ if (extension) {
249
+ const requiredPrivileges = extensionConfig?.['Display conditions']?.privileges ?? extension.privileges ?? [];
250
+ if (requiredPrivileges && (typeof requiredPrivileges === 'string' || Array.isArray(requiredPrivileges) && requiredPrivileges.length > 0)) {
251
+ if (!session?.user) {
252
+ continue;
253
+ }
254
+ if (!userHasAccess(requiredPrivileges, session.user)) {
255
+ continue;
256
+ }
257
+ }
258
+ const displayConditionExpression = extensionConfig?.['Display conditions']?.expression || extension.displayExpression;
259
+ if (displayConditionExpression !== undefined && typeof displayConditionExpression === 'string' && displayConditionExpression.trim().length > 0) {
260
+ try {
261
+ if (!evaluateAsBoolean(displayConditionExpression, expressionContext)) {
262
+ continue;
263
+ }
264
+ } catch (e) {
265
+ console.error(`Error while evaluating expression '${displayConditionExpression}' for extension ${name} in slot ${slotName}`, e);
266
+ continue;
267
+ }
268
+ }
269
+ if (extension.featureFlag && !enabledFeatureFlags.includes(extension.featureFlag)) {
270
+ continue;
271
+ }
272
+ if (window.offlineEnabled && !checkStatusFor(isOnline, extension.online, extension.offline)) {
273
+ continue;
274
+ }
275
+ extensions.push({
276
+ id,
277
+ name,
278
+ moduleName: extension.moduleName,
279
+ config: extensionConfig,
280
+ featureFlag: extension.featureFlag,
281
+ meta: extension.meta,
282
+ online: extensionConfig?.['Display conditions']?.online ?? extension.online ?? true,
283
+ offline: extensionConfig?.['Display conditions']?.offline ?? extension.offline ?? false
284
+ });
285
+ }
286
+ }
287
+ return extensions;
288
+ }
289
+ /**
290
+ * Gets the list of extensions assigned to a given slot
291
+ *
292
+ * @param slotName The slot to load the assigned extensions for
293
+ * @returns An array of extensions assigned to the named slot
294
+ */ export function getAssignedExtensions(slotName) {
295
+ const internalState = extensionInternalStore.getState();
296
+ const { config: slotConfig } = getExtensionSlotConfig(slotName);
297
+ const extensionStoreState = extensionsConfigStore.getState();
298
+ const featureFlagState = featureFlagsStore.getState();
299
+ const sessionState = sessionStore.getState();
300
+ const isOnline = isOnlineFn();
301
+ const enabledFeatureFlags = Object.entries(featureFlagState.flags).filter(([, { enabled }])=>enabled).map(([name])=>name);
302
+ return getAssignedExtensionsFromSlotData(slotName, internalState, slotConfig, extensionStoreState, enabledFeatureFlags, isOnline, sessionState.session);
303
+ }
304
+ function calculateAssignedIds(config, attachedIds) {
305
+ const addedIds = config.add || [];
306
+ const removedIds = config.remove || [];
307
+ const idOrder = config.order || [];
308
+ const { extensions } = extensionInternalStore.getState();
309
+ return [
310
+ ...attachedIds,
311
+ ...addedIds
312
+ ].filter((id)=>!removedIds.includes(id)).sort((idA, idB)=>{
313
+ const ai = getOrder(idA, idOrder, extensions[getExtensionNameFromId(idA)]?.order, attachedIds);
314
+ const bi = getOrder(idB, idOrder, extensions[getExtensionNameFromId(idB)]?.order, attachedIds);
315
+ if (bi === -1) {
316
+ return -1;
317
+ } else if (ai === -1) {
318
+ return 1;
319
+ } else {
320
+ return ai - bi;
321
+ }
322
+ });
323
+ }
324
+ /**
325
+ * Used by by extension slots at mount time.
326
+ *
327
+ * @param moduleName The name of the module that contains the extension slot
328
+ * @param slotName The extension slot name that is actually used
329
+ * @param state Optional custom state for the slot, which will be stored in the extension store.
330
+ * @internal
331
+ */ export const registerExtensionSlot = (moduleName, slotName, state)=>extensionInternalStore.setState((currentState)=>{
332
+ const existingModuleName = currentState.slots[slotName]?.moduleName;
333
+ if (existingModuleName && existingModuleName != moduleName) {
334
+ console.warn(`An extension slot with the name '${slotName}' already exists. Refusing to register the same slot name twice (in "registerExtensionSlot"). The existing one is from module ${existingModuleName}.`);
335
+ return currentState;
336
+ }
337
+ if (existingModuleName && existingModuleName == moduleName) {
338
+ // Re-rendering an existing slot
339
+ return currentState;
340
+ }
341
+ if (currentState.slots[slotName]) {
342
+ return {
343
+ ...currentState,
344
+ slots: {
345
+ ...currentState.slots,
346
+ [slotName]: {
347
+ ...currentState.slots[slotName],
348
+ moduleName,
349
+ state
350
+ }
351
+ }
352
+ };
353
+ }
354
+ const slot = createNewExtensionSlotInfo(slotName, moduleName, state);
355
+ return {
356
+ ...currentState,
357
+ slots: {
358
+ ...currentState.slots,
359
+ [slotName]: {
360
+ ...slot
361
+ }
362
+ }
363
+ };
364
+ });
365
+ /**
366
+ * Used by extension slots to update the copy of the state for the extension slot
367
+ *
368
+ * @param slotName The name of the slot with state to update
369
+ * @param state A copy of the new state
370
+ * @param partial Whether this should be applied as a partial
371
+ */ export function updateExtensionSlotState(slotName, state, partial = false) {
372
+ extensionInternalStore.setState((currentState)=>{
373
+ const newState = partial ? merge(currentState.slots[slotName].state, state) : state;
374
+ return {
375
+ ...currentState,
376
+ slots: {
377
+ ...currentState.slots,
378
+ [slotName]: {
379
+ ...currentState.slots[slotName],
380
+ state: newState
381
+ }
382
+ }
383
+ };
384
+ });
385
+ }
386
+ /**
387
+ * @internal
388
+ * Just for testing.
389
+ */ export const reset = ()=>extensionStore.setState(()=>{
390
+ return {
391
+ slots: {},
392
+ extensions: {}
393
+ };
394
+ });
@@ -0,0 +1,3 @@
1
+ export declare function checkStatus(online?: boolean | object, offline?: boolean | object): boolean;
2
+ export declare function checkStatusFor(status: boolean, online?: boolean | object, offline?: boolean | object): boolean;
3
+ //# sourceMappingURL=helpers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../src/helpers.ts"],"names":[],"mappings":"AAEA,wBAAgB,WAAW,CAAC,MAAM,GAAE,OAAO,GAAG,MAAa,EAAE,OAAO,GAAE,OAAO,GAAG,MAAc,WAE7F;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,OAAO,EAAE,MAAM,GAAE,OAAO,GAAG,MAAa,EAAE,OAAO,GAAE,OAAO,GAAG,MAAc,WAEjH"}
@@ -0,0 +1,7 @@
1
+ import { isOnline } from "@egen-civitas/esm-utils";
2
+ export function checkStatus(online = true, offline = false) {
3
+ return checkStatusFor(isOnline(), online, offline);
4
+ }
5
+ export function checkStatusFor(status, online = true, offline = false) {
6
+ return Boolean(status ? online : offline);
7
+ }
@@ -0,0 +1,10 @@
1
+ export * from './extensions.js';
2
+ export * from './helpers.js';
3
+ export * from './left-nav.js';
4
+ export * from './modals.js';
5
+ export * from './workspaces.js';
6
+ export * from './workspaces2.js';
7
+ export * from './render.js';
8
+ export * from './store.js';
9
+ export * from './types.js';
10
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,iBAAiB,CAAC;AAChC,cAAc,cAAc,CAAC;AAC7B,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,iBAAiB,CAAC;AAChC,cAAc,kBAAkB,CAAC;AACjC,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC;AAC3B,cAAc,YAAY,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,9 @@
1
+ export * from "./extensions.js";
2
+ export * from "./helpers.js";
3
+ export * from "./left-nav.js";
4
+ export * from "./modals.js";
5
+ export * from "./workspaces.js";
6
+ export * from "./workspaces2.js";
7
+ export * from "./render.js";
8
+ export * from "./store.js";
9
+ export * from "./types.js";
@@ -0,0 +1,38 @@
1
+ import { type ComponentConfig } from './types.js';
2
+ import { type ExtensionSlotState } from './store.js';
3
+ type LeftNavMode = 'normal' | 'collapsed' | 'hidden';
4
+ export interface LeftNavStore {
5
+ slotName: string | null;
6
+ basePath: string;
7
+ mode: LeftNavMode;
8
+ componentContext?: ComponentConfig;
9
+ state?: ExtensionSlotState;
10
+ }
11
+ /** @internal */
12
+ export declare const leftNavStore: import("zustand").StoreApi<LeftNavStore>;
13
+ export interface SetLeftNavParams {
14
+ name: string;
15
+ basePath: string;
16
+ /**
17
+ * In normal mode, the left nav is shown in desktop mode, and collapse into hamburger menu button in tablet mode
18
+ * In collapsed mode, the left nav is always collapsed, regardless of desktop / tablet mode.
19
+ * In hidden mode, the left nav is not shown at all.
20
+ */
21
+ mode?: LeftNavMode;
22
+ componentContext?: ComponentConfig;
23
+ state?: ExtensionSlotState;
24
+ }
25
+ /**
26
+ * Sets the current left nav context. Must be paired with {@link unsetLeftNav}.
27
+ *
28
+ * @deprecated Please use {@link useLeftNav} instead. This function will be made internal in a future release.
29
+ */
30
+ export declare function setLeftNav({ name, basePath, mode, componentContext, state }: SetLeftNavParams): void;
31
+ /**
32
+ * Unsets the left nav context if the current context is for the supplied name.
33
+ *
34
+ * @deprecated Please use {@link useLeftNav} instead. This function will be made internal in a future release.
35
+ */
36
+ export declare function unsetLeftNav(name: string): void;
37
+ export {};
38
+ //# sourceMappingURL=left-nav.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"left-nav.d.ts","sourceRoot":"","sources":["../src/left-nav.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,eAAe,EAAE,MAAM,YAAY,CAAC;AAClD,OAAO,EAAE,KAAK,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAErD,KAAK,WAAW,GAAG,QAAQ,GAAG,WAAW,GAAG,QAAQ,CAAC;AACrD,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,WAAW,CAAC;IAClB,gBAAgB,CAAC,EAAE,eAAe,CAAC;IACnC,KAAK,CAAC,EAAE,kBAAkB,CAAC;CAC5B;AAED,gBAAgB;AAChB,eAAO,MAAM,YAAY,0CAIvB,CAAC;AAEH,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB;;;;OAIG;IACH,IAAI,CAAC,EAAE,WAAW,CAAC;IACnB,gBAAgB,CAAC,EAAE,eAAe,CAAC;IACnC,KAAK,CAAC,EAAE,kBAAkB,CAAC;CAC5B;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,gBAAgB,EAAE,KAAK,EAAE,EAAE,gBAAgB,QAE7F;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,QAIxC"}
@@ -0,0 +1,28 @@
1
+ import { createGlobalStore } from "@egen-civitas/esm-state";
2
+ /** @internal */ export const leftNavStore = createGlobalStore('left-nav', {
3
+ slotName: null,
4
+ basePath: window.spaBase,
5
+ mode: 'normal'
6
+ });
7
+ /**
8
+ * Sets the current left nav context. Must be paired with {@link unsetLeftNav}.
9
+ *
10
+ * @deprecated Please use {@link useLeftNav} instead. This function will be made internal in a future release.
11
+ */ export function setLeftNav({ name, basePath, mode, componentContext, state }) {
12
+ leftNavStore.setState({
13
+ slotName: name,
14
+ basePath,
15
+ mode: mode ?? 'normal',
16
+ componentContext,
17
+ state
18
+ });
19
+ }
20
+ /**
21
+ * Unsets the left nav context if the current context is for the supplied name.
22
+ *
23
+ * @deprecated Please use {@link useLeftNav} instead. This function will be made internal in a future release.
24
+ */ export function unsetLeftNav(name) {
25
+ if (leftNavStore.getState().slotName === name) {
26
+ leftNavStore.setState(leftNavStore.getInitialState(), true);
27
+ }
28
+ }
@@ -0,0 +1,12 @@
1
+ import { type LifeCycles } from 'single-spa';
2
+ /** @internal */
3
+ export interface ModalRegistration {
4
+ name: string;
5
+ load(): Promise<LifeCycles>;
6
+ moduleName: string;
7
+ }
8
+ /** @internal */
9
+ export declare function registerModal(modalRegistration: ModalRegistration): void;
10
+ /** @internal */
11
+ export declare function getModalRegistration(modalName: string): ModalRegistration | undefined;
12
+ //# sourceMappingURL=modals.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"modals.d.ts","sourceRoot":"","sources":["../src/modals.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,YAAY,CAAC;AAI7C,gBAAgB;AAChB,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,IAAI,OAAO,CAAC,UAAU,CAAC,CAAC;IAC5B,UAAU,EAAE,MAAM,CAAC;CACpB;AAWD,gBAAgB;AAChB,wBAAgB,aAAa,CAAC,iBAAiB,EAAE,iBAAiB,QAKjE;AAED,gBAAgB;AAChB,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,MAAM,GAAG,iBAAiB,GAAG,SAAS,CAkBrF"}
package/dist/modals.js ADDED
@@ -0,0 +1,28 @@
1
+ import { createGlobalStore } from "@egen-civitas/esm-state";
2
+ import { getExtensionRegistration } from "./index.js";
3
+ const modalRegistryStore = createGlobalStore('modalRegistry', {
4
+ modals: {}
5
+ });
6
+ /** @internal */ export function registerModal(modalRegistration) {
7
+ modalRegistryStore.setState((state)=>{
8
+ state.modals[modalRegistration.name] = modalRegistration;
9
+ return state;
10
+ });
11
+ }
12
+ /** @internal */ export function getModalRegistration(modalName) {
13
+ let modalRegistration = modalRegistryStore.getState().modals[modalName];
14
+ if (!modalRegistration) {
15
+ const extensionRegistration = getExtensionRegistration(modalName);
16
+ if (extensionRegistration) {
17
+ modalRegistration = {
18
+ name: modalName,
19
+ load: extensionRegistration.load,
20
+ moduleName: extensionRegistration.moduleName
21
+ };
22
+ console.warn(`Modal ${modalName} was registered as an extension. This is deprecated and will be removed in the future. Please register it in the "modals" section of routes.json instead of the "extensions" section.`);
23
+ // Register it so the warning only appears once
24
+ registerModal(modalRegistration);
25
+ }
26
+ }
27
+ return modalRegistration;
28
+ }
@@ -0,0 +1,7 @@
1
+ export { getExtensionNameFromId, registerExtension, attach, detach, detachAll, getAssignedExtensions, registerExtensionSlot, } from './extensions.js';
2
+ export { type LeftNavStore, setLeftNav, unsetLeftNav, type SetLeftNavParams } from './left-nav.js';
3
+ export { type CancelLoading, renderExtension } from './render.js';
4
+ export { type ExtensionMeta, type ExtensionRegistration, type ExtensionStore, type AssignedExtension, type ConnectedExtension, type ExtensionSlotState, getExtensionStore, } from './store.js';
5
+ export { type WorkspaceRegistration } from './workspaces.js';
6
+ export { type ExtensionData, type ComponentConfig } from './types.js';
7
+ //# sourceMappingURL=public.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"public.d.ts","sourceRoot":"","sources":["../src/public.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,sBAAsB,EACtB,iBAAiB,EACjB,MAAM,EACN,MAAM,EACN,SAAS,EACT,qBAAqB,EACrB,qBAAqB,GACtB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,KAAK,YAAY,EAAE,UAAU,EAAE,YAAY,EAAE,KAAK,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACnG,OAAO,EAAE,KAAK,aAAa,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAClE,OAAO,EACL,KAAK,aAAa,EAClB,KAAK,qBAAqB,EAC1B,KAAK,cAAc,EACnB,KAAK,iBAAiB,EACtB,KAAK,kBAAkB,EACvB,KAAK,kBAAkB,EACvB,iBAAiB,GAClB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,KAAK,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AAC7D,OAAO,EAAE,KAAK,aAAa,EAAE,KAAK,eAAe,EAAE,MAAM,YAAY,CAAC"}