pict-microapp 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/LICENSE +21 -0
- package/README.md +180 -0
- package/package.json +59 -0
- package/source/Pict-MicroApp-Compose.js +149 -0
- package/source/Pict-MicroApp-Manifest.js +249 -0
- package/source/Pict-MicroApp.js +486 -0
- package/types/Pict-MicroApp-Compose.d.ts +24 -0
- package/types/Pict-MicroApp-Compose.d.ts.map +1 -0
- package/types/Pict-MicroApp-Manifest.d.ts +88 -0
- package/types/Pict-MicroApp-Manifest.d.ts.map +1 -0
- package/types/Pict-MicroApp.d.ts +192 -0
- package/types/Pict-MicroApp.d.ts.map +1 -0
|
@@ -0,0 +1,486 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pict MicroApp — present a large pict application as a small, focused one.
|
|
3
|
+
*
|
|
4
|
+
* A "micro app" is not a fork and not a rebuild: it is the SAME application, booted the same way,
|
|
5
|
+
* with a smaller surface. You keep every view, provider, template, style and fix the host app
|
|
6
|
+
* already has — and an enhancement to the host lands in every micro app on its next build — but
|
|
7
|
+
* the routes, the navigation and the landing page are the micro app's own.
|
|
8
|
+
*
|
|
9
|
+
* The seam is narrow on purpose. A host pict application registers every route it owns through one
|
|
10
|
+
* funnel, `PictRouter.addRoute`. This provider wraps that funnel with an allow list taken from a
|
|
11
|
+
* declarative manifest, swaps the navigation graph, and re-points the landing route. Nothing else
|
|
12
|
+
* about the host's boot sequence changes, which is what keeps a micro app cheap to build and cheap
|
|
13
|
+
* to keep working as the host evolves.
|
|
14
|
+
*
|
|
15
|
+
* What this module deliberately does NOT do is make the JavaScript bundle smaller — the host's
|
|
16
|
+
* module graph is still bundled whole. It slims the app the user sees, not the bytes the browser
|
|
17
|
+
* downloads. See the README for the reasoning and for when that tradeoff stops being the right one.
|
|
18
|
+
*
|
|
19
|
+
* @author steven velozo <steven@velozo.com>
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
const libPictProvider = require('pict-provider');
|
|
23
|
+
|
|
24
|
+
const libManifest = require('./Pict-MicroApp-Manifest.js');
|
|
25
|
+
|
|
26
|
+
const _DEFAULT_PROVIDER_CONFIGURATION =
|
|
27
|
+
{
|
|
28
|
+
ProviderIdentifier: 'MicroApp',
|
|
29
|
+
|
|
30
|
+
AutoInitialize: true,
|
|
31
|
+
AutoInitializeOrdinal: 0,
|
|
32
|
+
|
|
33
|
+
// The micro app manifest. See Pict-MicroApp-Manifest.js for the shape and defaults.
|
|
34
|
+
Manifest: {},
|
|
35
|
+
|
|
36
|
+
// Service hash of the host's pict-router provider. Every host that uses pict-router registers
|
|
37
|
+
// it under this hash.
|
|
38
|
+
RouterProviderHash: 'PictRouter',
|
|
39
|
+
|
|
40
|
+
// Service hash of the host's pict-section-navigation provider, when it has one.
|
|
41
|
+
NavigationProviderHash: 'Pict-Navigation',
|
|
42
|
+
|
|
43
|
+
// Log each route the gate blocks. Noisy but invaluable while authoring a manifest, so it is on.
|
|
44
|
+
LogBlockedRoutes: true
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
class PictMicroApp extends libPictProvider
|
|
48
|
+
{
|
|
49
|
+
/**
|
|
50
|
+
* @param {Object} pFable - the fable instance
|
|
51
|
+
* @param {Object} [pOptions] - provider options, including the Manifest
|
|
52
|
+
* @param {String} [pServiceHash] - the service hash
|
|
53
|
+
*/
|
|
54
|
+
constructor(pFable, pOptions, pServiceHash)
|
|
55
|
+
{
|
|
56
|
+
const tmpOptions = Object.assign({}, _DEFAULT_PROVIDER_CONFIGURATION, pOptions);
|
|
57
|
+
super(pFable, tmpOptions, pServiceHash);
|
|
58
|
+
|
|
59
|
+
/** @type {any} */
|
|
60
|
+
this.pict;
|
|
61
|
+
|
|
62
|
+
/** The normalized manifest — always fully populated, whatever the author wrote. */
|
|
63
|
+
this.manifest = libManifest.normalizeManifest(this.options.Manifest);
|
|
64
|
+
|
|
65
|
+
/** Route patterns the gate refused, in registration order. Read this when a screen is missing. */
|
|
66
|
+
this.blockedRoutes = [];
|
|
67
|
+
/** Route patterns the gate admitted with an entity guard. */
|
|
68
|
+
this.guardedRoutes = [];
|
|
69
|
+
/** Route patterns that survived untouched. */
|
|
70
|
+
this.allowedRoutes = [];
|
|
71
|
+
|
|
72
|
+
/** The original addRoute, kept so releaseRouter() can put the host back the way it was. */
|
|
73
|
+
this._originalAddRoute = null;
|
|
74
|
+
/** The original navigo `on`, for the same reason. */
|
|
75
|
+
this._originalRouterOn = null;
|
|
76
|
+
/** The router provider the gate is currently installed on. */
|
|
77
|
+
this._gatedRouter = null;
|
|
78
|
+
/** True while the provider-level gate is delegating into the navigo-level one. */
|
|
79
|
+
this._delegating = false;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* The host's pict-router provider, or null before it exists.
|
|
84
|
+
*
|
|
85
|
+
* @return {Object|null} the router provider
|
|
86
|
+
*/
|
|
87
|
+
get pictRouter()
|
|
88
|
+
{
|
|
89
|
+
const tmpProviders = (this.pict && this.pict.providers) ? this.pict.providers : {};
|
|
90
|
+
return tmpProviders[this.options.RouterProviderHash] || null;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* The host's navigation provider, or null when the host has none.
|
|
95
|
+
*
|
|
96
|
+
* @return {Object|null} the navigation provider
|
|
97
|
+
*/
|
|
98
|
+
get navigationProvider()
|
|
99
|
+
{
|
|
100
|
+
const tmpProviders = (this.pict && this.pict.providers) ? this.pict.providers : {};
|
|
101
|
+
return tmpProviders[this.options.NavigationProviderHash] || null;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Replace the manifest after construction. Only meaningful before the gate is installed.
|
|
106
|
+
*
|
|
107
|
+
* @param {Object} pManifest - a micro app manifest
|
|
108
|
+
* @return {Object} the normalized manifest
|
|
109
|
+
*/
|
|
110
|
+
setManifest(pManifest)
|
|
111
|
+
{
|
|
112
|
+
this.manifest = libManifest.normalizeManifest(pManifest);
|
|
113
|
+
return this.manifest;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* What would the gate do with this route? Exposed so an app (or a test) can ask without
|
|
118
|
+
* registering anything.
|
|
119
|
+
*
|
|
120
|
+
* @param {String} pRoute - a route pattern
|
|
121
|
+
* @return {{Mode: String, Route: String, Reason: String}} the decision
|
|
122
|
+
*/
|
|
123
|
+
routeDecision(pRoute)
|
|
124
|
+
{
|
|
125
|
+
return libManifest.decideRoute(this.manifest, pRoute);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Install the route gate on the host's router.
|
|
130
|
+
*
|
|
131
|
+
* MUST be called after the router provider exists but BEFORE the host registers its routes. In
|
|
132
|
+
* a pict application that means after the provider that owns routing has been added (usually in
|
|
133
|
+
* onInitializeAsync) and before the login/data-load cycle that registers them.
|
|
134
|
+
*
|
|
135
|
+
* Wrapping the instance method rather than the prototype keeps the blast radius to this one
|
|
136
|
+
* router: a host that shares pict-router with an embedded module is unaffected.
|
|
137
|
+
*
|
|
138
|
+
* TWO funnels are wrapped, not one. Most code registers through the provider's `addRoute`, but
|
|
139
|
+
* some modules reach past it and call the underlying router's `on` directly — pict-section-
|
|
140
|
+
* recordset's list, dashboard, associate and bulk-delete views all do, which is every one of its
|
|
141
|
+
* highest-traffic screens. Gating only `addRoute` silently leaves those wide open, and because
|
|
142
|
+
* they still work you would never notice. The provider-level wrapper sets `_delegating` while it
|
|
143
|
+
* calls through, so a route that came in via `addRoute` is decided once, not twice.
|
|
144
|
+
*
|
|
145
|
+
* @param {Object} [pPictRouter] - the router provider; defaults to the configured one
|
|
146
|
+
* @return {Boolean} whether the gate was installed
|
|
147
|
+
*/
|
|
148
|
+
gateRouter(pPictRouter)
|
|
149
|
+
{
|
|
150
|
+
const tmpRouter = pPictRouter || this.pictRouter;
|
|
151
|
+
if (!tmpRouter || (typeof tmpRouter.addRoute !== 'function'))
|
|
152
|
+
{
|
|
153
|
+
this.pict.log.warn(`MicroApp [${ this.manifest.Name }] could not gate routes: no router provider at '${ this.options.RouterProviderHash }'.`);
|
|
154
|
+
return false;
|
|
155
|
+
}
|
|
156
|
+
if (this._originalAddRoute)
|
|
157
|
+
{
|
|
158
|
+
// Already gated. Re-gating would double-wrap every handler.
|
|
159
|
+
return true;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
this._gatedRouter = tmpRouter;
|
|
163
|
+
this._originalAddRoute = tmpRouter.addRoute.bind(tmpRouter);
|
|
164
|
+
|
|
165
|
+
tmpRouter.addRoute = (pRoute, pRenderable, pTitle, pHooks) =>
|
|
166
|
+
{
|
|
167
|
+
const tmpDecision = this.routeDecision(pRoute);
|
|
168
|
+
if (tmpDecision.Mode === 'Block')
|
|
169
|
+
{
|
|
170
|
+
this._noteBlocked(tmpDecision);
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
const tmpGuarded = (tmpDecision.Mode === 'Guard');
|
|
175
|
+
if (tmpGuarded)
|
|
176
|
+
{
|
|
177
|
+
this.guardedRoutes.push(tmpDecision.Route);
|
|
178
|
+
}
|
|
179
|
+
else
|
|
180
|
+
{
|
|
181
|
+
this.allowedRoutes.push(tmpDecision.Route);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
this._delegating = true;
|
|
185
|
+
try
|
|
186
|
+
{
|
|
187
|
+
return this._originalAddRoute(tmpDecision.Route,
|
|
188
|
+
tmpGuarded ? this._guardRenderable(tmpDecision.Route, pRenderable) : pRenderable, pTitle, pHooks);
|
|
189
|
+
}
|
|
190
|
+
finally
|
|
191
|
+
{
|
|
192
|
+
this._delegating = false;
|
|
193
|
+
}
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
// The second funnel: direct registrations against the underlying router.
|
|
197
|
+
if (tmpRouter.router && (typeof tmpRouter.router.on === 'function'))
|
|
198
|
+
{
|
|
199
|
+
this._originalRouterOn = tmpRouter.router.on.bind(tmpRouter.router);
|
|
200
|
+
tmpRouter.router.on = (pRoute, pHandler, pHooks) =>
|
|
201
|
+
{
|
|
202
|
+
// Already decided by the provider-level wrapper, or one of the router's other
|
|
203
|
+
// `on` shapes (a bare handler, or a route map) that carries no path to decide on.
|
|
204
|
+
if (this._delegating || (typeof pRoute !== 'string'))
|
|
205
|
+
{
|
|
206
|
+
return this._originalRouterOn(pRoute, pHandler, pHooks);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
const tmpDecision = this.routeDecision(pRoute);
|
|
210
|
+
if (tmpDecision.Mode === 'Block')
|
|
211
|
+
{
|
|
212
|
+
this._noteBlocked(tmpDecision);
|
|
213
|
+
return this._gatedRouter.router;
|
|
214
|
+
}
|
|
215
|
+
if (tmpDecision.Mode === 'Guard')
|
|
216
|
+
{
|
|
217
|
+
this.guardedRoutes.push(tmpDecision.Route);
|
|
218
|
+
return this._originalRouterOn(tmpDecision.Route, this._guardRenderable(tmpDecision.Route, pHandler), pHooks);
|
|
219
|
+
}
|
|
220
|
+
this.allowedRoutes.push(tmpDecision.Route);
|
|
221
|
+
return this._originalRouterOn(pRoute, pHandler, pHooks);
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
return true;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Record (and optionally log) a route the gate refused.
|
|
230
|
+
*
|
|
231
|
+
* @param {{Route: String, Reason: String}} pDecision - the decision that blocked it
|
|
232
|
+
*/
|
|
233
|
+
_noteBlocked(pDecision)
|
|
234
|
+
{
|
|
235
|
+
this.blockedRoutes.push(pDecision.Route);
|
|
236
|
+
if (this.options.LogBlockedRoutes)
|
|
237
|
+
{
|
|
238
|
+
this.pict.log.trace(`MicroApp [${ this.manifest.Name }] route not exposed: ${ pDecision.Route } (${ pDecision.Reason })`);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Put the host's router back the way it was. Primarily for tests.
|
|
244
|
+
*
|
|
245
|
+
* @return {Boolean} whether a gate was removed
|
|
246
|
+
*/
|
|
247
|
+
releaseRouter()
|
|
248
|
+
{
|
|
249
|
+
if (!this._gatedRouter || !this._originalAddRoute)
|
|
250
|
+
{
|
|
251
|
+
return false;
|
|
252
|
+
}
|
|
253
|
+
this._gatedRouter.addRoute = this._originalAddRoute;
|
|
254
|
+
if (this._originalRouterOn && this._gatedRouter.router)
|
|
255
|
+
{
|
|
256
|
+
this._gatedRouter.router.on = this._originalRouterOn;
|
|
257
|
+
}
|
|
258
|
+
this._originalAddRoute = null;
|
|
259
|
+
this._originalRouterOn = null;
|
|
260
|
+
this._gatedRouter = null;
|
|
261
|
+
return true;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Wrap an entity-parameterized route's handler so an entity the micro app does not expose
|
|
266
|
+
* bounces to the fallback instead of rendering.
|
|
267
|
+
*
|
|
268
|
+
* Wildcard record-set routes ('/PSRS/:RecordSet/List') are registered once by the host and fan
|
|
269
|
+
* out over every entity it knows. They cannot be refused per-entity at registration time, so
|
|
270
|
+
* this is where the subsetting has to happen.
|
|
271
|
+
*
|
|
272
|
+
* @param {String} pRoute - the canonical route pattern
|
|
273
|
+
* @param {Function|String} pRenderable - the host's handler
|
|
274
|
+
* @return {Function|String} the guarded handler, or the renderable untouched when it is a template
|
|
275
|
+
*/
|
|
276
|
+
_guardRenderable(pRoute, pRenderable)
|
|
277
|
+
{
|
|
278
|
+
if (typeof pRenderable !== 'function')
|
|
279
|
+
{
|
|
280
|
+
return pRenderable;
|
|
281
|
+
}
|
|
282
|
+
const tmpEntityParameter = this.manifest.Routes.EntityParameter;
|
|
283
|
+
return (pRouteData) =>
|
|
284
|
+
{
|
|
285
|
+
const tmpEntity = (pRouteData && pRouteData.data) ? pRouteData.data[tmpEntityParameter] : null;
|
|
286
|
+
if (tmpEntity && !libManifest.entityPermitted(this.manifest, tmpEntity))
|
|
287
|
+
{
|
|
288
|
+
this.pict.log.trace(`MicroApp [${ this.manifest.Name }] entity not exposed on ${ pRoute }: ${ tmpEntity } — routing to ${ this.manifest.Routes.Fallback }.`);
|
|
289
|
+
return this.navigate(this.manifest.Routes.Fallback);
|
|
290
|
+
}
|
|
291
|
+
return pRenderable(pRouteData);
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* Send the browser to a route. Uses the host router when it is available so hooks and history
|
|
297
|
+
* behave the same as any other navigation.
|
|
298
|
+
*
|
|
299
|
+
* @param {String} pRoute - the route to navigate to
|
|
300
|
+
* @return {Boolean} whether a navigation was attempted
|
|
301
|
+
*/
|
|
302
|
+
navigate(pRoute)
|
|
303
|
+
{
|
|
304
|
+
const tmpRoute = libManifest.normalizeRoute(pRoute);
|
|
305
|
+
const tmpRouter = this.pictRouter;
|
|
306
|
+
if (tmpRouter && (typeof tmpRouter.navigate === 'function'))
|
|
307
|
+
{
|
|
308
|
+
tmpRouter.navigate(tmpRoute);
|
|
309
|
+
return true;
|
|
310
|
+
}
|
|
311
|
+
if ((typeof window !== 'undefined') && window.location)
|
|
312
|
+
{
|
|
313
|
+
window.location.hash = tmpRoute;
|
|
314
|
+
return true;
|
|
315
|
+
}
|
|
316
|
+
return false;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* Point a hash that matched no surviving route at the fallback, so a stale bookmark into a
|
|
321
|
+
* screen this micro app does not carry lands somewhere real instead of on a blank page.
|
|
322
|
+
*
|
|
323
|
+
* @param {Object} [pPictRouter] - the router provider; defaults to the configured one
|
|
324
|
+
* @return {Boolean} whether the handler was installed
|
|
325
|
+
*/
|
|
326
|
+
installNotFound(pPictRouter)
|
|
327
|
+
{
|
|
328
|
+
if (!this.manifest.Routes.CatchUnmatched)
|
|
329
|
+
{
|
|
330
|
+
return false;
|
|
331
|
+
}
|
|
332
|
+
const tmpRouter = pPictRouter || this.pictRouter;
|
|
333
|
+
// `router` is the underlying navigo instance the pict provider wraps.
|
|
334
|
+
if (!tmpRouter || !tmpRouter.router || (typeof tmpRouter.router.notFound !== 'function'))
|
|
335
|
+
{
|
|
336
|
+
return false;
|
|
337
|
+
}
|
|
338
|
+
tmpRouter.router.notFound(() =>
|
|
339
|
+
{
|
|
340
|
+
this.pict.log.trace(`MicroApp [${ this.manifest.Name }] unmatched route — routing to ${ this.manifest.Routes.Fallback }.`);
|
|
341
|
+
this.navigate(this.manifest.Routes.Fallback);
|
|
342
|
+
});
|
|
343
|
+
return true;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
/**
|
|
347
|
+
* Swap the host's navigation graph for the micro app's, and PIN it.
|
|
348
|
+
*
|
|
349
|
+
* Pinning is not paranoia. A host application of any size rebuilds its own navigation after the
|
|
350
|
+
* fact — as configuration loads, as permissions resolve, as dynamically registered sections
|
|
351
|
+
* appear — and each rebuild calls `setNavigationGraph` with the host's graph, silently undoing
|
|
352
|
+
* the swap. The symptom is the worst kind: the micro app's menu is correct at first paint and
|
|
353
|
+
* then quietly becomes the host's, usually after login, where nobody is looking.
|
|
354
|
+
*
|
|
355
|
+
* So rather than racing the host for the last write, the setter itself is wrapped: later calls
|
|
356
|
+
* still re-normalize and re-render, they just always land on the micro app's sections.
|
|
357
|
+
*
|
|
358
|
+
* @param {Object} [pNavigationProvider] - the navigation provider; defaults to the configured one
|
|
359
|
+
* @return {Boolean} whether the graph was swapped
|
|
360
|
+
*/
|
|
361
|
+
applyNavigationGraph(pNavigationProvider)
|
|
362
|
+
{
|
|
363
|
+
const tmpSections = this.manifest.Navigation.Sections;
|
|
364
|
+
if (!tmpSections || (tmpSections.length < 1))
|
|
365
|
+
{
|
|
366
|
+
return false;
|
|
367
|
+
}
|
|
368
|
+
const tmpProvider = pNavigationProvider || this.navigationProvider;
|
|
369
|
+
if (!tmpProvider || (typeof tmpProvider.setNavigationGraph !== 'function'))
|
|
370
|
+
{
|
|
371
|
+
this.pict.log.warn(`MicroApp [${ this.manifest.Name }] could not set the navigation graph: no navigation provider at '${ this.options.NavigationProviderHash }'.`);
|
|
372
|
+
return false;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
if (this.manifest.Navigation.PinGraph && !tmpProvider._microAppGraphPinned)
|
|
376
|
+
{
|
|
377
|
+
const fSetNavigationGraph = tmpProvider.setNavigationGraph.bind(tmpProvider);
|
|
378
|
+
tmpProvider.setNavigationGraph = () => fSetNavigationGraph(tmpSections);
|
|
379
|
+
tmpProvider._microAppGraphPinned = true;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
tmpProvider.setNavigationGraph(tmpSections);
|
|
383
|
+
return true;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
/**
|
|
387
|
+
* Install the micro app's navigation visibility gate.
|
|
388
|
+
*
|
|
389
|
+
* A host that serves many audiences usually trims its own (large) graph hard — the Headlight
|
|
390
|
+
* platform app, for instance, shows platform super-users only the handful of cross-customer
|
|
391
|
+
* destinations. Applied to a micro app graph, which is already the slimmed surface, that trim
|
|
392
|
+
* empties the menu completely. So the default here keeps the host's real gates (session,
|
|
393
|
+
* module, capability) and drops only the audience short-circuit.
|
|
394
|
+
*
|
|
395
|
+
* @param {Function} pHostPermitted - (pItem) => Boolean, the host's own per-item capability test
|
|
396
|
+
* @param {Object} [pOptions] - { IsPrivileged: () => Boolean } to keep privileged sessions unfiltered
|
|
397
|
+
* @return {Boolean} whether the gate was installed
|
|
398
|
+
*/
|
|
399
|
+
applyNavigationGate(pHostPermitted, pOptions)
|
|
400
|
+
{
|
|
401
|
+
const tmpProvider = this.navigationProvider;
|
|
402
|
+
if (!tmpProvider || (typeof tmpProvider.setGlobalFilter !== 'function'))
|
|
403
|
+
{
|
|
404
|
+
return false;
|
|
405
|
+
}
|
|
406
|
+
const tmpOptions = pOptions || {};
|
|
407
|
+
const tmpIsPrivileged = (typeof tmpOptions.IsPrivileged === 'function') ? tmpOptions.IsPrivileged : (() => false);
|
|
408
|
+
const tmpHonorHostGates = this.manifest.Navigation.HonorHostGates;
|
|
409
|
+
const tmpSuperUserSeesEverything = this.manifest.Navigation.SuperUserSeesEverything;
|
|
410
|
+
|
|
411
|
+
tmpProvider.setGlobalFilter((pItem, pCategory) =>
|
|
412
|
+
{
|
|
413
|
+
if (pItem.Hidden || (pCategory && pCategory.Hidden))
|
|
414
|
+
{
|
|
415
|
+
return false;
|
|
416
|
+
}
|
|
417
|
+
if (!tmpHonorHostGates)
|
|
418
|
+
{
|
|
419
|
+
return true;
|
|
420
|
+
}
|
|
421
|
+
// A privileged session holds no entitlements in the customer's own tenancy, so running
|
|
422
|
+
// it through the capability test would blank the menu a second way. Show it everything.
|
|
423
|
+
if (tmpSuperUserSeesEverything && tmpIsPrivileged())
|
|
424
|
+
{
|
|
425
|
+
return true;
|
|
426
|
+
}
|
|
427
|
+
return (typeof pHostPermitted === 'function') ? !!pHostPermitted(pItem, pCategory) : true;
|
|
428
|
+
});
|
|
429
|
+
return true;
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
/**
|
|
433
|
+
* Point the host's routing provider at the micro app's landing route.
|
|
434
|
+
*
|
|
435
|
+
* @param {Object} pHostRouterProvider - the provider that owns `defaultRoute` (often a wrapper
|
|
436
|
+
* around pict-router rather than pict-router itself)
|
|
437
|
+
* @return {Boolean} whether the landing route was set
|
|
438
|
+
*/
|
|
439
|
+
applyDefaultRoute(pHostRouterProvider)
|
|
440
|
+
{
|
|
441
|
+
if (!pHostRouterProvider)
|
|
442
|
+
{
|
|
443
|
+
return false;
|
|
444
|
+
}
|
|
445
|
+
// Hosts spell this without the leading slash (navigo's own convention for a default).
|
|
446
|
+
pHostRouterProvider.defaultRoute = this.manifest.DefaultRoute.replace(/^\//, '');
|
|
447
|
+
return true;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
/**
|
|
451
|
+
* Apply the manifest's branding to the document.
|
|
452
|
+
*
|
|
453
|
+
* @return {Boolean} whether a title was set
|
|
454
|
+
*/
|
|
455
|
+
applyBranding()
|
|
456
|
+
{
|
|
457
|
+
const tmpTitle = this.manifest.Branding.Title || this.manifest.Name;
|
|
458
|
+
if (!tmpTitle || (typeof document === 'undefined'))
|
|
459
|
+
{
|
|
460
|
+
return false;
|
|
461
|
+
}
|
|
462
|
+
document.title = tmpTitle;
|
|
463
|
+
return true;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
/**
|
|
467
|
+
* A one-line summary of what the gate did, for the console and for tests.
|
|
468
|
+
*
|
|
469
|
+
* @return {{Name: String, Allowed: Number, Guarded: Number, Blocked: Number}} the tally
|
|
470
|
+
*/
|
|
471
|
+
get routeTally()
|
|
472
|
+
{
|
|
473
|
+
return (
|
|
474
|
+
{
|
|
475
|
+
Name: this.manifest.Name,
|
|
476
|
+
Allowed: this.allowedRoutes.length,
|
|
477
|
+
Guarded: this.guardedRoutes.length,
|
|
478
|
+
Blocked: this.blockedRoutes.length
|
|
479
|
+
});
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
module.exports = PictMicroApp;
|
|
484
|
+
module.exports.default_configuration = _DEFAULT_PROVIDER_CONFIGURATION;
|
|
485
|
+
module.exports.Manifest = libManifest;
|
|
486
|
+
module.exports.composeMicroApp = require('./Pict-MicroApp-Compose.js');
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export = composeMicroApp;
|
|
2
|
+
/**
|
|
3
|
+
* Build a micro app application class from a host application class and a manifest.
|
|
4
|
+
*
|
|
5
|
+
* @param {Function} pHostApplicationClass - the host pict application class to present a subset of
|
|
6
|
+
* @param {Object} pManifest - the micro app manifest (see Pict-MicroApp-Manifest.js)
|
|
7
|
+
* @param {Object} [pComposeOptions] - the host binding options above
|
|
8
|
+
* @return {Function} a class extending the host application class
|
|
9
|
+
*/
|
|
10
|
+
declare function composeMicroApp(pHostApplicationClass: Function, pManifest: any, pComposeOptions?: any): Function;
|
|
11
|
+
declare namespace composeMicroApp {
|
|
12
|
+
export { _DEFAULT_COMPOSE_OPTIONS as default_compose_options };
|
|
13
|
+
}
|
|
14
|
+
declare namespace _DEFAULT_COMPOSE_OPTIONS {
|
|
15
|
+
let ProviderHash: string;
|
|
16
|
+
let RouterProviderHash: string;
|
|
17
|
+
let NavigationProviderHash: string;
|
|
18
|
+
let HostRouterProviderHash: string;
|
|
19
|
+
let AttachOn: string;
|
|
20
|
+
let NavigationGateMethod: string;
|
|
21
|
+
let HostPermittedMethod: string;
|
|
22
|
+
let IsPrivilegedMethod: string;
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=Pict-MicroApp-Compose.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Pict-MicroApp-Compose.d.ts","sourceRoot":"","sources":["../source/Pict-MicroApp-Compose.js"],"names":[],"mappings":";AAqDA;;;;;;;GAOG;AACH,mHAoFC"}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
declare namespace _DEFAULT_MANIFEST {
|
|
2
|
+
let Name: string;
|
|
3
|
+
let Hash: string;
|
|
4
|
+
let DefaultRoute: string;
|
|
5
|
+
namespace Routes {
|
|
6
|
+
let Allow: any[];
|
|
7
|
+
let Deny: any[];
|
|
8
|
+
let EntityParameter: string;
|
|
9
|
+
let Entities: any[];
|
|
10
|
+
let GuardEntityRoutes: boolean;
|
|
11
|
+
let Fallback: string;
|
|
12
|
+
let CatchUnmatched: boolean;
|
|
13
|
+
let AlwaysAllow: string[];
|
|
14
|
+
}
|
|
15
|
+
namespace Navigation {
|
|
16
|
+
let Sections: any[];
|
|
17
|
+
let PinGraph: boolean;
|
|
18
|
+
let SuperUserSeesEverything: boolean;
|
|
19
|
+
let HonorHostGates: boolean;
|
|
20
|
+
}
|
|
21
|
+
namespace Branding {
|
|
22
|
+
let Title: string;
|
|
23
|
+
let LoginTitle: string;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Deep-ish merge of an author manifest over the defaults. Only the three known sub-objects are
|
|
28
|
+
* merged a level down; everything else is a straight overwrite.
|
|
29
|
+
*
|
|
30
|
+
* @param {Object} [pManifest] - the author's manifest
|
|
31
|
+
* @return {Object} a fully populated manifest
|
|
32
|
+
*/
|
|
33
|
+
export function normalizeManifest(pManifest?: any): any;
|
|
34
|
+
/**
|
|
35
|
+
* Strip a route to the canonical form used for comparison: no leading '#', exactly one leading '/',
|
|
36
|
+
* no trailing '/' (except the bare root).
|
|
37
|
+
*
|
|
38
|
+
* @param {String} pRoute - a route pattern or hash
|
|
39
|
+
* @return {String} the canonical route pattern
|
|
40
|
+
*/
|
|
41
|
+
export function normalizeRoute(pRoute: string): string;
|
|
42
|
+
/**
|
|
43
|
+
* Does a canonical route pattern match one allow/deny entry? An entry ending in '*' is a prefix
|
|
44
|
+
* glob; anything else is an exact pattern match (so '/Asset/Workspace/:ID' matches only the route
|
|
45
|
+
* registered with that exact parameter spelling).
|
|
46
|
+
*
|
|
47
|
+
* @param {String} pRoute - a canonical route pattern
|
|
48
|
+
* @param {String} pEntry - a canonical allow/deny entry
|
|
49
|
+
* @return {Boolean} whether the entry matches
|
|
50
|
+
*/
|
|
51
|
+
export function routeMatchesEntry(pRoute: string, pEntry: string): boolean;
|
|
52
|
+
/**
|
|
53
|
+
* Does this route pattern fan out over the manifest's entity parameter (for example
|
|
54
|
+
* '/PSRS/:RecordSet/List' with an EntityParameter of 'RecordSet')?
|
|
55
|
+
*
|
|
56
|
+
* @param {String} pRoute - a canonical route pattern
|
|
57
|
+
* @param {String} pEntityParameter - the parameter name that names an entity
|
|
58
|
+
* @return {Boolean} whether the route is entity-parameterized
|
|
59
|
+
*/
|
|
60
|
+
export function routeIsEntityParameterized(pRoute: string, pEntityParameter: string): boolean;
|
|
61
|
+
/**
|
|
62
|
+
* Decide what the gate should do with a route the host is trying to register.
|
|
63
|
+
*
|
|
64
|
+
* Returns one of three modes:
|
|
65
|
+
* Allow — register it untouched.
|
|
66
|
+
* Guard — register it, but wrap the handler so disallowed entities bounce to the fallback.
|
|
67
|
+
* Block — do not register it at all.
|
|
68
|
+
*
|
|
69
|
+
* @param {Object} pManifest - a normalized manifest
|
|
70
|
+
* @param {String} pRoute - the route pattern the host passed to addRoute
|
|
71
|
+
* @return {{Mode: String, Route: String, Reason: String}} the decision
|
|
72
|
+
*/
|
|
73
|
+
export function decideRoute(pManifest: any, pRoute: string): {
|
|
74
|
+
Mode: string;
|
|
75
|
+
Route: string;
|
|
76
|
+
Reason: string;
|
|
77
|
+
};
|
|
78
|
+
/**
|
|
79
|
+
* Is an entity name exposed by this micro app? An empty Entities list means every entity is
|
|
80
|
+
* exposed (the manifest author did not want to constrain the record-set surface).
|
|
81
|
+
*
|
|
82
|
+
* @param {Object} pManifest - a normalized manifest
|
|
83
|
+
* @param {String} pEntity - the entity name resolved from the route
|
|
84
|
+
* @return {Boolean} whether the entity is exposed
|
|
85
|
+
*/
|
|
86
|
+
export function entityPermitted(pManifest: any, pEntity: string): boolean;
|
|
87
|
+
export { _DEFAULT_MANIFEST as default_manifest };
|
|
88
|
+
//# sourceMappingURL=Pict-MicroApp-Manifest.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Pict-MicroApp-Manifest.d.ts","sourceRoot":"","sources":["../source/Pict-MicroApp-Manifest.js"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AAqGA;;;;;;GAMG;AACH,wDAiBC;AAlDD;;;;;;GAMG;AACH,uDAiBC;AA4BD;;;;;;;;GAQG;AACH,2EAWC;AAED;;;;;;;GAOG;AACH,8FAOC;AAED;;;;;;;;;;;GAWG;AACH,6DAFY;IAAC,IAAI,SAAS;IAAC,KAAK,SAAS;IAAC,MAAM,SAAQ;CAAC,CA2CxD;AAED;;;;;;;GAOG;AACH,0EAQC"}
|