@descope/sdk-mixins 0.25.0 → 0.26.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/dist/cjs/index.js +5 -0
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/mixins/componentsConditionsMixin/applier.js +71 -0
- package/dist/cjs/mixins/componentsConditionsMixin/applier.js.map +1 -0
- package/dist/cjs/mixins/componentsConditionsMixin/componentsConditionsMixin.js +103 -0
- package/dist/cjs/mixins/componentsConditionsMixin/componentsConditionsMixin.js.map +1 -0
- package/dist/esm/index.js +2 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/mixins/componentsConditionsMixin/applier.js +68 -0
- package/dist/esm/mixins/componentsConditionsMixin/applier.js.map +1 -0
- package/dist/esm/mixins/componentsConditionsMixin/componentsConditionsMixin.js +101 -0
- package/dist/esm/mixins/componentsConditionsMixin/componentsConditionsMixin.js.map +1 -0
- package/dist/index.d.ts +664 -280
- package/dist/types/index.d.ts +1 -0
- package/dist/types/mixins/componentsConditionsMixin/applier.d.ts +4 -0
- package/dist/types/mixins/componentsConditionsMixin/applier.test.d.ts +1 -0
- package/dist/types/mixins/componentsConditionsMixin/componentsConditionsMixin.d.ts +379 -0
- package/dist/types/mixins/componentsConditionsMixin/componentsConditionsMixin.test.d.ts +1 -0
- package/dist/types/mixins/componentsConditionsMixin/index.d.ts +2 -0
- package/dist/types/mixins/configMixin/configMixin.d.ts +14 -14
- package/dist/types/mixins/createStateManagementMixin.d.ts +2 -2
- package/dist/types/mixins/createValidateAttributesMixin/createValidateAttributesMixin.d.ts +5 -5
- package/dist/types/mixins/cspNonceMixin.d.ts +5 -5
- package/dist/types/mixins/debuggerMixin/debugger-wc.d.ts +5 -5
- package/dist/types/mixins/debuggerMixin/debuggerMixin.d.ts +8 -8
- package/dist/types/mixins/descopeUiMixin/descopeUiMixin.d.ts +19 -19
- package/dist/types/mixins/fetchWidgetPagesMixin/createFetchWidgetPagesMixin.d.ts +15 -15
- package/dist/types/mixins/flowIdMixin.d.ts +5 -5
- package/dist/types/mixins/flowInputMixin/flowInputMixin.d.ts +41 -41
- package/dist/types/mixins/formMixin.d.ts +2 -2
- package/dist/types/mixins/initElementMixin.d.ts +5 -5
- package/dist/types/mixins/initLifecycleMixin.d.ts +1 -1
- package/dist/types/mixins/injectNpmLibMixin/injectNpmLibMixin.d.ts +2 -2
- package/dist/types/mixins/injectStyleMixin.d.ts +5 -5
- package/dist/types/mixins/loggerMixin/loggerMixin.d.ts +2 -2
- package/dist/types/mixins/modalMixin/modalMixin.d.ts +23 -23
- package/dist/types/mixins/notificationsMixin/notificationsMixin.d.ts +23 -23
- package/dist/types/mixins/observeAttributesMixin/observeAttributesMixin.d.ts +4 -4
- package/dist/types/mixins/projectIdMixin.d.ts +5 -5
- package/dist/types/mixins/resetMixin.d.ts +6 -6
- package/dist/types/mixins/staticResourcesMixin/staticResourcesMixin.d.ts +10 -10
- package/dist/types/mixins/telemetryMixin/telemetryMixin.d.ts +24 -24
- package/dist/types/mixins/tenantIdMixin.d.ts +5 -5
- package/dist/types/mixins/themeMixin/themeMixin.d.ts +40 -40
- package/dist/types/mixins/widgetConfigMixin/widgetConfigMixin.d.ts +14 -14
- package/dist/types/mixins/widgetIdMixin.d.ts +5 -5
- package/package.json +1 -1
package/dist/cjs/index.js
CHANGED
|
@@ -28,6 +28,8 @@ var injectStyleMixin = require('./mixins/injectStyleMixin.js');
|
|
|
28
28
|
var cspNonceMixin = require('./mixins/cspNonceMixin.js');
|
|
29
29
|
var telemetryMixin = require('./mixins/telemetryMixin/telemetryMixin.js');
|
|
30
30
|
var flowInputMixin = require('./mixins/flowInputMixin/flowInputMixin.js');
|
|
31
|
+
var componentsConditionsMixin = require('./mixins/componentsConditionsMixin/componentsConditionsMixin.js');
|
|
32
|
+
var applier = require('./mixins/componentsConditionsMixin/applier.js');
|
|
31
33
|
|
|
32
34
|
|
|
33
35
|
|
|
@@ -60,4 +62,7 @@ exports.injectStyleMixin = injectStyleMixin.injectStyleMixin;
|
|
|
60
62
|
exports.cspNonceMixin = cspNonceMixin.cspNonceMixin;
|
|
61
63
|
exports.telemetryMixin = telemetryMixin.telemetryMixin;
|
|
62
64
|
exports.flowInputMixin = flowInputMixin.flowInputMixin;
|
|
65
|
+
exports.componentsConditionsMixin = componentsConditionsMixin.componentsConditionsMixin;
|
|
66
|
+
exports.applyComponentsState = applier.applyComponentsState;
|
|
67
|
+
exports.clearComponentsState = applier.clearComponentsState;
|
|
63
68
|
//# sourceMappingURL=index.js.map
|
package/dist/cjs/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// Applies server-computed component conditions (hide / disable / read-only) to a
|
|
4
|
+
// widget's DOM. Mirrors the flow web-component applier, but targets widget
|
|
5
|
+
// components by their `data-id` (widgets address components that way) and hides
|
|
6
|
+
// via inline style + the `hidden` attribute rather than a `.hidden` class, which
|
|
7
|
+
// widgets do not inject into their shadow DOM.
|
|
8
|
+
const ATTR_SELECTOR_ESCAPE_PATTERN = /(["\\])/g;
|
|
9
|
+
// CSS.escape with a narrow fallback for environments that lack it.
|
|
10
|
+
function escapeSelector(value) {
|
|
11
|
+
if (typeof CSS !== 'undefined' && typeof CSS.escape === 'function') {
|
|
12
|
+
return CSS.escape(value);
|
|
13
|
+
}
|
|
14
|
+
return value.replace(ATTR_SELECTOR_ESCAPE_PATTERN, '\\$1');
|
|
15
|
+
}
|
|
16
|
+
// IDs can repeat, so match all.
|
|
17
|
+
function findComponents(root, id) {
|
|
18
|
+
return Array.from(root.querySelectorAll(`[data-id="${escapeSelector(id)}"]`));
|
|
19
|
+
}
|
|
20
|
+
function applyAction(el, action) {
|
|
21
|
+
var _a;
|
|
22
|
+
switch (action) {
|
|
23
|
+
case 'hide':
|
|
24
|
+
el.setAttribute('hidden', '');
|
|
25
|
+
(_a = el.style) === null || _a === void 0 ? void 0 : _a.setProperty('display', 'none', 'important');
|
|
26
|
+
break;
|
|
27
|
+
case 'disable':
|
|
28
|
+
el.setAttribute('disabled', 'true');
|
|
29
|
+
break;
|
|
30
|
+
case 'read-only':
|
|
31
|
+
el.setAttribute('readonly', 'true');
|
|
32
|
+
break;
|
|
33
|
+
// Unknown action - ignore. A bad action shouldn't make it past the server.
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
function clearAction(el, action) {
|
|
37
|
+
var _a;
|
|
38
|
+
switch (action) {
|
|
39
|
+
case 'hide':
|
|
40
|
+
el.removeAttribute('hidden');
|
|
41
|
+
(_a = el.style) === null || _a === void 0 ? void 0 : _a.removeProperty('display');
|
|
42
|
+
break;
|
|
43
|
+
case 'disable':
|
|
44
|
+
el.removeAttribute('disabled');
|
|
45
|
+
break;
|
|
46
|
+
case 'read-only':
|
|
47
|
+
el.removeAttribute('readonly');
|
|
48
|
+
break;
|
|
49
|
+
// no-op
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
/** Applies the componentId -> action map to the matching elements under root. */
|
|
53
|
+
function applyComponentsState(root, componentsState) {
|
|
54
|
+
if (!root || !componentsState)
|
|
55
|
+
return;
|
|
56
|
+
Object.entries(componentsState).forEach(([id, action]) => {
|
|
57
|
+
findComponents(root, id).forEach((el) => applyAction(el, action));
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
/** Reverts a previously-applied componentId -> action map. */
|
|
61
|
+
function clearComponentsState(root, componentsState) {
|
|
62
|
+
if (!root || !componentsState)
|
|
63
|
+
return;
|
|
64
|
+
Object.entries(componentsState).forEach(([id, action]) => {
|
|
65
|
+
findComponents(root, id).forEach((el) => clearAction(el, action));
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
exports.applyComponentsState = applyComponentsState;
|
|
70
|
+
exports.clearComponentsState = clearComponentsState;
|
|
71
|
+
//# sourceMappingURL=applier.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"applier.js","sources":["../../../../src/mixins/componentsConditionsMixin/applier.ts"],"sourcesContent":["// Applies server-computed component conditions (hide / disable / read-only) to a\n// widget's DOM. Mirrors the flow web-component applier, but targets widget\n// components by their `data-id` (widgets address components that way) and hides\n// via inline style + the `hidden` attribute rather than a `.hidden` class, which\n// widgets do not inject into their shadow DOM.\n\nconst ATTR_SELECTOR_ESCAPE_PATTERN = /([\"\\\\])/g;\n\n// CSS.escape with a narrow fallback for environments that lack it.\nfunction escapeSelector(value: string): string {\n if (typeof CSS !== 'undefined' && typeof CSS.escape === 'function') {\n return CSS.escape(value);\n }\n return value.replace(ATTR_SELECTOR_ESCAPE_PATTERN, '\\\\$1');\n}\n\n// IDs can repeat, so match all.\nfunction findComponents(root: ParentNode, id: string): Element[] {\n return Array.from(root.querySelectorAll(`[data-id=\"${escapeSelector(id)}\"]`));\n}\n\nfunction applyAction(el: Element, action: string): void {\n switch (action) {\n case 'hide':\n el.setAttribute('hidden', '');\n (el as HTMLElement).style?.setProperty('display', 'none', 'important');\n break;\n case 'disable':\n el.setAttribute('disabled', 'true');\n break;\n case 'read-only':\n el.setAttribute('readonly', 'true');\n break;\n default:\n // Unknown action - ignore. A bad action shouldn't make it past the server.\n }\n}\n\nfunction clearAction(el: Element, action: string): void {\n switch (action) {\n case 'hide':\n el.removeAttribute('hidden');\n (el as HTMLElement).style?.removeProperty('display');\n break;\n case 'disable':\n el.removeAttribute('disabled');\n break;\n case 'read-only':\n el.removeAttribute('readonly');\n break;\n default:\n // no-op\n }\n}\n\n/** Applies the componentId -> action map to the matching elements under root. */\nexport function applyComponentsState(\n root: ParentNode | null | undefined,\n componentsState: Record<string, string> | undefined,\n): void {\n if (!root || !componentsState) return;\n Object.entries(componentsState).forEach(([id, action]) => {\n findComponents(root, id).forEach((el) => applyAction(el, action));\n });\n}\n\n/** Reverts a previously-applied componentId -> action map. */\nexport function clearComponentsState(\n root: ParentNode | null | undefined,\n componentsState: Record<string, string> | undefined,\n): void {\n if (!root || !componentsState) return;\n Object.entries(componentsState).forEach(([id, action]) => {\n findComponents(root, id).forEach((el) => clearAction(el, action));\n });\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var tslib = require('tslib');
|
|
4
|
+
var sdkHelpers = require('@descope/sdk-helpers');
|
|
5
|
+
var loggerMixin = require('../loggerMixin/loggerMixin.js');
|
|
6
|
+
var initElementMixin = require('../initElementMixin.js');
|
|
7
|
+
var applier = require('./applier.js');
|
|
8
|
+
|
|
9
|
+
const COMPONENTS_STATE_PATH = '/v1/mgmt/widget/components-state';
|
|
10
|
+
/**
|
|
11
|
+
* Shared widget mixin that fetches the server-computed component conditions
|
|
12
|
+
* verdict for the current user and applies hide/disable/read-only to the widget
|
|
13
|
+
* DOM. Server-side only - no client-side re-evaluation. Compose it LAST in a
|
|
14
|
+
* widget's initMixin so its init wraps the widget's render.
|
|
15
|
+
*
|
|
16
|
+
* It reuses the widget's existing web-js-sdk httpClient (`this.api.httpClient`),
|
|
17
|
+
* so `apiMixin` must be composed and `createSdk` must return `httpClient`. A
|
|
18
|
+
* missing client is a wiring mistake, surfaced loudly (throw) rather than
|
|
19
|
+
* silently rendering everything; a runtime fetch failure fails open instead.
|
|
20
|
+
*/
|
|
21
|
+
const componentsConditionsMixin = sdkHelpers.createSingletonMixin((superclass) => {
|
|
22
|
+
var _ComponentsConditionsMixinClass_instances, _ComponentsConditionsMixinClass_statePromise, _ComponentsConditionsMixinClass_appliedState, _ComponentsConditionsMixinClass_requireHttpClient, _ComponentsConditionsMixinClass_fetchComponentsState, _a;
|
|
23
|
+
// NB: do not compose initLifecycleMixin here - its empty init() has no
|
|
24
|
+
// super.init() call and would shadow the widget's render init.
|
|
25
|
+
const BaseClass = sdkHelpers.compose(loggerMixin.loggerMixin, initElementMixin.initElementMixin)(superclass);
|
|
26
|
+
return _a = class ComponentsConditionsMixinClass extends BaseClass {
|
|
27
|
+
constructor() {
|
|
28
|
+
super(...arguments);
|
|
29
|
+
_ComponentsConditionsMixinClass_instances.add(this);
|
|
30
|
+
_ComponentsConditionsMixinClass_statePromise.set(this, void 0);
|
|
31
|
+
_ComponentsConditionsMixinClass_appliedState.set(this, {});
|
|
32
|
+
}
|
|
33
|
+
async init() {
|
|
34
|
+
var _b, _c;
|
|
35
|
+
if (this.getAttribute('mock') === 'true') {
|
|
36
|
+
// Mock mode (demos / e2e) has no backend - skip the fetch, hide nothing.
|
|
37
|
+
tslib.__classPrivateFieldSet(this, _ComponentsConditionsMixinClass_statePromise, Promise.resolve({}), "f");
|
|
38
|
+
await ((_b = super.init) === null || _b === void 0 ? void 0 : _b.call(this));
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
// Resolve the client up front so a wiring mistake throws before the
|
|
42
|
+
// fetch's fail-open path can swallow it.
|
|
43
|
+
const httpClient = tslib.__classPrivateFieldGet(this, _ComponentsConditionsMixinClass_instances, "m", _ComponentsConditionsMixinClass_requireHttpClient).call(this);
|
|
44
|
+
// Fire immediately so it overlaps the page/data fetch super.init runs.
|
|
45
|
+
tslib.__classPrivateFieldSet(this, _ComponentsConditionsMixinClass_statePromise, tslib.__classPrivateFieldGet(this, _ComponentsConditionsMixinClass_instances, "m", _ComponentsConditionsMixinClass_fetchComponentsState).call(this, httpClient), "f");
|
|
46
|
+
await ((_c = super.init) === null || _c === void 0 ? void 0 : _c.call(this));
|
|
47
|
+
}
|
|
48
|
+
async onWidgetRootReady() {
|
|
49
|
+
var _b, _c;
|
|
50
|
+
// @ts-expect-error onWidgetRootReady is provided by the widget's initWidgetRootMixin
|
|
51
|
+
await ((_b = super.onWidgetRootReady) === null || _b === void 0 ? void 0 : _b.call(this));
|
|
52
|
+
// Apply once the DOM exists and component values are populated, before the
|
|
53
|
+
// widget dispatches 'ready'. Awaiting here bounds any added latency to
|
|
54
|
+
// max(0, conditionsFetch - pageFetch).
|
|
55
|
+
tslib.__classPrivateFieldSet(this, _ComponentsConditionsMixinClass_appliedState, (_c = (await tslib.__classPrivateFieldGet(this, _ComponentsConditionsMixinClass_statePromise, "f"))) !== null && _c !== void 0 ? _c : {}, "f");
|
|
56
|
+
applier.applyComponentsState(this.contentRootElement, tslib.__classPrivateFieldGet(this, _ComponentsConditionsMixinClass_appliedState, "f"));
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Re-fetch and re-apply after the widget mutates data the conditions read
|
|
60
|
+
* (e.g. a profile save). Public hook for the upcoming re-evaluate-on-mutation
|
|
61
|
+
* work - no widget wires it to its mutating flows yet.
|
|
62
|
+
*/
|
|
63
|
+
async reevaluateComponentsState() {
|
|
64
|
+
const httpClient = tslib.__classPrivateFieldGet(this, _ComponentsConditionsMixinClass_instances, "m", _ComponentsConditionsMixinClass_requireHttpClient).call(this);
|
|
65
|
+
applier.clearComponentsState(this.contentRootElement, tslib.__classPrivateFieldGet(this, _ComponentsConditionsMixinClass_appliedState, "f"));
|
|
66
|
+
tslib.__classPrivateFieldSet(this, _ComponentsConditionsMixinClass_appliedState, await tslib.__classPrivateFieldGet(this, _ComponentsConditionsMixinClass_instances, "m", _ComponentsConditionsMixinClass_fetchComponentsState).call(this, httpClient), "f");
|
|
67
|
+
applier.applyComponentsState(this.contentRootElement, tslib.__classPrivateFieldGet(this, _ComponentsConditionsMixinClass_appliedState, "f"));
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
_ComponentsConditionsMixinClass_statePromise = new WeakMap(),
|
|
71
|
+
_ComponentsConditionsMixinClass_appliedState = new WeakMap(),
|
|
72
|
+
_ComponentsConditionsMixinClass_instances = new WeakSet(),
|
|
73
|
+
_ComponentsConditionsMixinClass_requireHttpClient = function _ComponentsConditionsMixinClass_requireHttpClient() {
|
|
74
|
+
var _b;
|
|
75
|
+
const httpClient = (_b = this.api) === null || _b === void 0 ? void 0 : _b.httpClient;
|
|
76
|
+
if (!httpClient) {
|
|
77
|
+
// Developer misconfiguration (apiMixin not composed, or createSdk does
|
|
78
|
+
// not expose httpClient) - fail loud so it is caught in development.
|
|
79
|
+
throw new Error('componentsConditionsMixin: the widget sdk must expose `httpClient`. Compose apiMixin and return `webSdk.httpClient` from createSdk.');
|
|
80
|
+
}
|
|
81
|
+
return httpClient;
|
|
82
|
+
},
|
|
83
|
+
_ComponentsConditionsMixinClass_fetchComponentsState = async function _ComponentsConditionsMixinClass_fetchComponentsState(httpClient) {
|
|
84
|
+
var _b;
|
|
85
|
+
try {
|
|
86
|
+
const res = await httpClient.get(COMPONENTS_STATE_PATH);
|
|
87
|
+
if (!(res === null || res === void 0 ? void 0 : res.ok))
|
|
88
|
+
return {};
|
|
89
|
+
const body = await res.json();
|
|
90
|
+
return (_b = body === null || body === void 0 ? void 0 : body.componentsState) !== null && _b !== void 0 ? _b : {};
|
|
91
|
+
}
|
|
92
|
+
catch (e) {
|
|
93
|
+
// Fail open: render everything. UI hiding is presentation, not access
|
|
94
|
+
// control - sensitive data stays gated by the widget's data API.
|
|
95
|
+
this.logger.debug('components-conditions: failed to fetch, showing all components', e === null || e === void 0 ? void 0 : e.message);
|
|
96
|
+
return {};
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
_a;
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
exports.componentsConditionsMixin = componentsConditionsMixin;
|
|
103
|
+
//# sourceMappingURL=componentsConditionsMixin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"componentsConditionsMixin.js","sources":["../../../../src/mixins/componentsConditionsMixin/componentsConditionsMixin.ts"],"sourcesContent":["import { compose, createSingletonMixin } from '@descope/sdk-helpers';\nimport { loggerMixin } from '../loggerMixin';\nimport { initElementMixin } from '../initElementMixin';\nimport { applyComponentsState, clearComponentsState } from './applier';\n\ntype ComponentsState = Record<string, string>;\n\nconst COMPONENTS_STATE_PATH = '/v1/mgmt/widget/components-state';\n\n// The widget's sdk (built by apiMixin) is expected to expose the client here.\ninterface ComponentsConditionsHost {\n api?: { httpClient?: ConditionsHttpClient };\n}\n\n// Minimal client contract the mixin needs. Widgets expose their existing\n// web-js-sdk httpClient (the same instance used for every other API call),\n// narrowed to this so no web-js-sdk/core-js-sdk type leaks into the widget's\n// public sdk surface. Declared last (with the mixin) to satisfy import/exports-last.\nexport interface ConditionsHttpClient {\n get: (path: string) => Promise<Response>;\n}\n\n/**\n * Shared widget mixin that fetches the server-computed component conditions\n * verdict for the current user and applies hide/disable/read-only to the widget\n * DOM. Server-side only - no client-side re-evaluation. Compose it LAST in a\n * widget's initMixin so its init wraps the widget's render.\n *\n * It reuses the widget's existing web-js-sdk httpClient (`this.api.httpClient`),\n * so `apiMixin` must be composed and `createSdk` must return `httpClient`. A\n * missing client is a wiring mistake, surfaced loudly (throw) rather than\n * silently rendering everything; a runtime fetch failure fails open instead.\n */\nexport const componentsConditionsMixin = createSingletonMixin(\n <T extends CustomElementConstructor>(superclass: T) => {\n // NB: do not compose initLifecycleMixin here - its empty init() has no\n // super.init() call and would shadow the widget's render init.\n const BaseClass = compose(loggerMixin, initElementMixin)(superclass);\n\n return class ComponentsConditionsMixinClass extends BaseClass {\n #statePromise?: Promise<ComponentsState>;\n\n #appliedState: ComponentsState = {};\n\n #requireHttpClient(): ConditionsHttpClient {\n const httpClient = (this as unknown as ComponentsConditionsHost).api\n ?.httpClient;\n if (!httpClient) {\n // Developer misconfiguration (apiMixin not composed, or createSdk does\n // not expose httpClient) - fail loud so it is caught in development.\n throw new Error(\n 'componentsConditionsMixin: the widget sdk must expose `httpClient`. Compose apiMixin and return `webSdk.httpClient` from createSdk.',\n );\n }\n return httpClient;\n }\n\n async #fetchComponentsState(\n httpClient: ConditionsHttpClient,\n ): Promise<ComponentsState> {\n try {\n const res = await httpClient.get(COMPONENTS_STATE_PATH);\n if (!res?.ok) return {};\n const body = await res.json();\n return body?.componentsState ?? {};\n } catch (e) {\n // Fail open: render everything. UI hiding is presentation, not access\n // control - sensitive data stays gated by the widget's data API.\n this.logger.debug(\n 'components-conditions: failed to fetch, showing all components',\n (e as Error)?.message,\n );\n return {};\n }\n }\n\n async init() {\n if (this.getAttribute('mock') === 'true') {\n // Mock mode (demos / e2e) has no backend - skip the fetch, hide nothing.\n this.#statePromise = Promise.resolve({});\n await super.init?.();\n return;\n }\n // Resolve the client up front so a wiring mistake throws before the\n // fetch's fail-open path can swallow it.\n const httpClient = this.#requireHttpClient();\n // Fire immediately so it overlaps the page/data fetch super.init runs.\n this.#statePromise = this.#fetchComponentsState(httpClient);\n await super.init?.();\n }\n\n async onWidgetRootReady() {\n // @ts-expect-error onWidgetRootReady is provided by the widget's initWidgetRootMixin\n await super.onWidgetRootReady?.();\n // Apply once the DOM exists and component values are populated, before the\n // widget dispatches 'ready'. Awaiting here bounds any added latency to\n // max(0, conditionsFetch - pageFetch).\n this.#appliedState = (await this.#statePromise) ?? {};\n applyComponentsState(this.contentRootElement, this.#appliedState);\n }\n\n /**\n * Re-fetch and re-apply after the widget mutates data the conditions read\n * (e.g. a profile save). Public hook for the upcoming re-evaluate-on-mutation\n * work - no widget wires it to its mutating flows yet.\n */\n async reevaluateComponentsState() {\n const httpClient = this.#requireHttpClient();\n clearComponentsState(this.contentRootElement, this.#appliedState);\n this.#appliedState = await this.#fetchComponentsState(httpClient);\n applyComponentsState(this.contentRootElement, this.#appliedState);\n }\n };\n },\n);\n"],"names":["createSingletonMixin","compose","loggerMixin","initElementMixin","__classPrivateFieldSet","__classPrivateFieldGet","applyComponentsState","clearComponentsState"],"mappings":"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"}
|
package/dist/esm/index.js
CHANGED
|
@@ -26,4 +26,6 @@ export { injectStyleMixin } from './mixins/injectStyleMixin.js';
|
|
|
26
26
|
export { cspNonceMixin } from './mixins/cspNonceMixin.js';
|
|
27
27
|
export { telemetryMixin } from './mixins/telemetryMixin/telemetryMixin.js';
|
|
28
28
|
export { flowInputMixin } from './mixins/flowInputMixin/flowInputMixin.js';
|
|
29
|
+
export { componentsConditionsMixin } from './mixins/componentsConditionsMixin/componentsConditionsMixin.js';
|
|
30
|
+
export { applyComponentsState, clearComponentsState } from './mixins/componentsConditionsMixin/applier.js';
|
|
29
31
|
//# sourceMappingURL=index.js.map
|
package/dist/esm/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
// Applies server-computed component conditions (hide / disable / read-only) to a
|
|
2
|
+
// widget's DOM. Mirrors the flow web-component applier, but targets widget
|
|
3
|
+
// components by their `data-id` (widgets address components that way) and hides
|
|
4
|
+
// via inline style + the `hidden` attribute rather than a `.hidden` class, which
|
|
5
|
+
// widgets do not inject into their shadow DOM.
|
|
6
|
+
const ATTR_SELECTOR_ESCAPE_PATTERN = /(["\\])/g;
|
|
7
|
+
// CSS.escape with a narrow fallback for environments that lack it.
|
|
8
|
+
function escapeSelector(value) {
|
|
9
|
+
if (typeof CSS !== 'undefined' && typeof CSS.escape === 'function') {
|
|
10
|
+
return CSS.escape(value);
|
|
11
|
+
}
|
|
12
|
+
return value.replace(ATTR_SELECTOR_ESCAPE_PATTERN, '\\$1');
|
|
13
|
+
}
|
|
14
|
+
// IDs can repeat, so match all.
|
|
15
|
+
function findComponents(root, id) {
|
|
16
|
+
return Array.from(root.querySelectorAll(`[data-id="${escapeSelector(id)}"]`));
|
|
17
|
+
}
|
|
18
|
+
function applyAction(el, action) {
|
|
19
|
+
var _a;
|
|
20
|
+
switch (action) {
|
|
21
|
+
case 'hide':
|
|
22
|
+
el.setAttribute('hidden', '');
|
|
23
|
+
(_a = el.style) === null || _a === void 0 ? void 0 : _a.setProperty('display', 'none', 'important');
|
|
24
|
+
break;
|
|
25
|
+
case 'disable':
|
|
26
|
+
el.setAttribute('disabled', 'true');
|
|
27
|
+
break;
|
|
28
|
+
case 'read-only':
|
|
29
|
+
el.setAttribute('readonly', 'true');
|
|
30
|
+
break;
|
|
31
|
+
// Unknown action - ignore. A bad action shouldn't make it past the server.
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
function clearAction(el, action) {
|
|
35
|
+
var _a;
|
|
36
|
+
switch (action) {
|
|
37
|
+
case 'hide':
|
|
38
|
+
el.removeAttribute('hidden');
|
|
39
|
+
(_a = el.style) === null || _a === void 0 ? void 0 : _a.removeProperty('display');
|
|
40
|
+
break;
|
|
41
|
+
case 'disable':
|
|
42
|
+
el.removeAttribute('disabled');
|
|
43
|
+
break;
|
|
44
|
+
case 'read-only':
|
|
45
|
+
el.removeAttribute('readonly');
|
|
46
|
+
break;
|
|
47
|
+
// no-op
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
/** Applies the componentId -> action map to the matching elements under root. */
|
|
51
|
+
function applyComponentsState(root, componentsState) {
|
|
52
|
+
if (!root || !componentsState)
|
|
53
|
+
return;
|
|
54
|
+
Object.entries(componentsState).forEach(([id, action]) => {
|
|
55
|
+
findComponents(root, id).forEach((el) => applyAction(el, action));
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
/** Reverts a previously-applied componentId -> action map. */
|
|
59
|
+
function clearComponentsState(root, componentsState) {
|
|
60
|
+
if (!root || !componentsState)
|
|
61
|
+
return;
|
|
62
|
+
Object.entries(componentsState).forEach(([id, action]) => {
|
|
63
|
+
findComponents(root, id).forEach((el) => clearAction(el, action));
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export { applyComponentsState, clearComponentsState };
|
|
68
|
+
//# sourceMappingURL=applier.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"applier.js","sources":["../../../../src/mixins/componentsConditionsMixin/applier.ts"],"sourcesContent":["// Applies server-computed component conditions (hide / disable / read-only) to a\n// widget's DOM. Mirrors the flow web-component applier, but targets widget\n// components by their `data-id` (widgets address components that way) and hides\n// via inline style + the `hidden` attribute rather than a `.hidden` class, which\n// widgets do not inject into their shadow DOM.\n\nconst ATTR_SELECTOR_ESCAPE_PATTERN = /([\"\\\\])/g;\n\n// CSS.escape with a narrow fallback for environments that lack it.\nfunction escapeSelector(value: string): string {\n if (typeof CSS !== 'undefined' && typeof CSS.escape === 'function') {\n return CSS.escape(value);\n }\n return value.replace(ATTR_SELECTOR_ESCAPE_PATTERN, '\\\\$1');\n}\n\n// IDs can repeat, so match all.\nfunction findComponents(root: ParentNode, id: string): Element[] {\n return Array.from(root.querySelectorAll(`[data-id=\"${escapeSelector(id)}\"]`));\n}\n\nfunction applyAction(el: Element, action: string): void {\n switch (action) {\n case 'hide':\n el.setAttribute('hidden', '');\n (el as HTMLElement).style?.setProperty('display', 'none', 'important');\n break;\n case 'disable':\n el.setAttribute('disabled', 'true');\n break;\n case 'read-only':\n el.setAttribute('readonly', 'true');\n break;\n default:\n // Unknown action - ignore. A bad action shouldn't make it past the server.\n }\n}\n\nfunction clearAction(el: Element, action: string): void {\n switch (action) {\n case 'hide':\n el.removeAttribute('hidden');\n (el as HTMLElement).style?.removeProperty('display');\n break;\n case 'disable':\n el.removeAttribute('disabled');\n break;\n case 'read-only':\n el.removeAttribute('readonly');\n break;\n default:\n // no-op\n }\n}\n\n/** Applies the componentId -> action map to the matching elements under root. */\nexport function applyComponentsState(\n root: ParentNode | null | undefined,\n componentsState: Record<string, string> | undefined,\n): void {\n if (!root || !componentsState) return;\n Object.entries(componentsState).forEach(([id, action]) => {\n findComponents(root, id).forEach((el) => applyAction(el, action));\n });\n}\n\n/** Reverts a previously-applied componentId -> action map. */\nexport function clearComponentsState(\n root: ParentNode | null | undefined,\n componentsState: Record<string, string> | undefined,\n): void {\n if (!root || !componentsState) return;\n Object.entries(componentsState).forEach(([id, action]) => {\n findComponents(root, id).forEach((el) => clearAction(el, action));\n });\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { __classPrivateFieldSet, __classPrivateFieldGet } from 'tslib';
|
|
2
|
+
import { createSingletonMixin, compose } from '@descope/sdk-helpers';
|
|
3
|
+
import { loggerMixin } from '../loggerMixin/loggerMixin.js';
|
|
4
|
+
import { initElementMixin } from '../initElementMixin.js';
|
|
5
|
+
import { applyComponentsState, clearComponentsState } from './applier.js';
|
|
6
|
+
|
|
7
|
+
const COMPONENTS_STATE_PATH = '/v1/mgmt/widget/components-state';
|
|
8
|
+
/**
|
|
9
|
+
* Shared widget mixin that fetches the server-computed component conditions
|
|
10
|
+
* verdict for the current user and applies hide/disable/read-only to the widget
|
|
11
|
+
* DOM. Server-side only - no client-side re-evaluation. Compose it LAST in a
|
|
12
|
+
* widget's initMixin so its init wraps the widget's render.
|
|
13
|
+
*
|
|
14
|
+
* It reuses the widget's existing web-js-sdk httpClient (`this.api.httpClient`),
|
|
15
|
+
* so `apiMixin` must be composed and `createSdk` must return `httpClient`. A
|
|
16
|
+
* missing client is a wiring mistake, surfaced loudly (throw) rather than
|
|
17
|
+
* silently rendering everything; a runtime fetch failure fails open instead.
|
|
18
|
+
*/
|
|
19
|
+
const componentsConditionsMixin = createSingletonMixin((superclass) => {
|
|
20
|
+
var _ComponentsConditionsMixinClass_instances, _ComponentsConditionsMixinClass_statePromise, _ComponentsConditionsMixinClass_appliedState, _ComponentsConditionsMixinClass_requireHttpClient, _ComponentsConditionsMixinClass_fetchComponentsState, _a;
|
|
21
|
+
// NB: do not compose initLifecycleMixin here - its empty init() has no
|
|
22
|
+
// super.init() call and would shadow the widget's render init.
|
|
23
|
+
const BaseClass = compose(loggerMixin, initElementMixin)(superclass);
|
|
24
|
+
return _a = class ComponentsConditionsMixinClass extends BaseClass {
|
|
25
|
+
constructor() {
|
|
26
|
+
super(...arguments);
|
|
27
|
+
_ComponentsConditionsMixinClass_instances.add(this);
|
|
28
|
+
_ComponentsConditionsMixinClass_statePromise.set(this, void 0);
|
|
29
|
+
_ComponentsConditionsMixinClass_appliedState.set(this, {});
|
|
30
|
+
}
|
|
31
|
+
async init() {
|
|
32
|
+
var _b, _c;
|
|
33
|
+
if (this.getAttribute('mock') === 'true') {
|
|
34
|
+
// Mock mode (demos / e2e) has no backend - skip the fetch, hide nothing.
|
|
35
|
+
__classPrivateFieldSet(this, _ComponentsConditionsMixinClass_statePromise, Promise.resolve({}), "f");
|
|
36
|
+
await ((_b = super.init) === null || _b === void 0 ? void 0 : _b.call(this));
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
// Resolve the client up front so a wiring mistake throws before the
|
|
40
|
+
// fetch's fail-open path can swallow it.
|
|
41
|
+
const httpClient = __classPrivateFieldGet(this, _ComponentsConditionsMixinClass_instances, "m", _ComponentsConditionsMixinClass_requireHttpClient).call(this);
|
|
42
|
+
// Fire immediately so it overlaps the page/data fetch super.init runs.
|
|
43
|
+
__classPrivateFieldSet(this, _ComponentsConditionsMixinClass_statePromise, __classPrivateFieldGet(this, _ComponentsConditionsMixinClass_instances, "m", _ComponentsConditionsMixinClass_fetchComponentsState).call(this, httpClient), "f");
|
|
44
|
+
await ((_c = super.init) === null || _c === void 0 ? void 0 : _c.call(this));
|
|
45
|
+
}
|
|
46
|
+
async onWidgetRootReady() {
|
|
47
|
+
var _b, _c;
|
|
48
|
+
// @ts-expect-error onWidgetRootReady is provided by the widget's initWidgetRootMixin
|
|
49
|
+
await ((_b = super.onWidgetRootReady) === null || _b === void 0 ? void 0 : _b.call(this));
|
|
50
|
+
// Apply once the DOM exists and component values are populated, before the
|
|
51
|
+
// widget dispatches 'ready'. Awaiting here bounds any added latency to
|
|
52
|
+
// max(0, conditionsFetch - pageFetch).
|
|
53
|
+
__classPrivateFieldSet(this, _ComponentsConditionsMixinClass_appliedState, (_c = (await __classPrivateFieldGet(this, _ComponentsConditionsMixinClass_statePromise, "f"))) !== null && _c !== void 0 ? _c : {}, "f");
|
|
54
|
+
applyComponentsState(this.contentRootElement, __classPrivateFieldGet(this, _ComponentsConditionsMixinClass_appliedState, "f"));
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Re-fetch and re-apply after the widget mutates data the conditions read
|
|
58
|
+
* (e.g. a profile save). Public hook for the upcoming re-evaluate-on-mutation
|
|
59
|
+
* work - no widget wires it to its mutating flows yet.
|
|
60
|
+
*/
|
|
61
|
+
async reevaluateComponentsState() {
|
|
62
|
+
const httpClient = __classPrivateFieldGet(this, _ComponentsConditionsMixinClass_instances, "m", _ComponentsConditionsMixinClass_requireHttpClient).call(this);
|
|
63
|
+
clearComponentsState(this.contentRootElement, __classPrivateFieldGet(this, _ComponentsConditionsMixinClass_appliedState, "f"));
|
|
64
|
+
__classPrivateFieldSet(this, _ComponentsConditionsMixinClass_appliedState, await __classPrivateFieldGet(this, _ComponentsConditionsMixinClass_instances, "m", _ComponentsConditionsMixinClass_fetchComponentsState).call(this, httpClient), "f");
|
|
65
|
+
applyComponentsState(this.contentRootElement, __classPrivateFieldGet(this, _ComponentsConditionsMixinClass_appliedState, "f"));
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
_ComponentsConditionsMixinClass_statePromise = new WeakMap(),
|
|
69
|
+
_ComponentsConditionsMixinClass_appliedState = new WeakMap(),
|
|
70
|
+
_ComponentsConditionsMixinClass_instances = new WeakSet(),
|
|
71
|
+
_ComponentsConditionsMixinClass_requireHttpClient = function _ComponentsConditionsMixinClass_requireHttpClient() {
|
|
72
|
+
var _b;
|
|
73
|
+
const httpClient = (_b = this.api) === null || _b === void 0 ? void 0 : _b.httpClient;
|
|
74
|
+
if (!httpClient) {
|
|
75
|
+
// Developer misconfiguration (apiMixin not composed, or createSdk does
|
|
76
|
+
// not expose httpClient) - fail loud so it is caught in development.
|
|
77
|
+
throw new Error('componentsConditionsMixin: the widget sdk must expose `httpClient`. Compose apiMixin and return `webSdk.httpClient` from createSdk.');
|
|
78
|
+
}
|
|
79
|
+
return httpClient;
|
|
80
|
+
},
|
|
81
|
+
_ComponentsConditionsMixinClass_fetchComponentsState = async function _ComponentsConditionsMixinClass_fetchComponentsState(httpClient) {
|
|
82
|
+
var _b;
|
|
83
|
+
try {
|
|
84
|
+
const res = await httpClient.get(COMPONENTS_STATE_PATH);
|
|
85
|
+
if (!(res === null || res === void 0 ? void 0 : res.ok))
|
|
86
|
+
return {};
|
|
87
|
+
const body = await res.json();
|
|
88
|
+
return (_b = body === null || body === void 0 ? void 0 : body.componentsState) !== null && _b !== void 0 ? _b : {};
|
|
89
|
+
}
|
|
90
|
+
catch (e) {
|
|
91
|
+
// Fail open: render everything. UI hiding is presentation, not access
|
|
92
|
+
// control - sensitive data stays gated by the widget's data API.
|
|
93
|
+
this.logger.debug('components-conditions: failed to fetch, showing all components', e === null || e === void 0 ? void 0 : e.message);
|
|
94
|
+
return {};
|
|
95
|
+
}
|
|
96
|
+
},
|
|
97
|
+
_a;
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
export { componentsConditionsMixin };
|
|
101
|
+
//# sourceMappingURL=componentsConditionsMixin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"componentsConditionsMixin.js","sources":["../../../../src/mixins/componentsConditionsMixin/componentsConditionsMixin.ts"],"sourcesContent":["import { compose, createSingletonMixin } from '@descope/sdk-helpers';\nimport { loggerMixin } from '../loggerMixin';\nimport { initElementMixin } from '../initElementMixin';\nimport { applyComponentsState, clearComponentsState } from './applier';\n\ntype ComponentsState = Record<string, string>;\n\nconst COMPONENTS_STATE_PATH = '/v1/mgmt/widget/components-state';\n\n// The widget's sdk (built by apiMixin) is expected to expose the client here.\ninterface ComponentsConditionsHost {\n api?: { httpClient?: ConditionsHttpClient };\n}\n\n// Minimal client contract the mixin needs. Widgets expose their existing\n// web-js-sdk httpClient (the same instance used for every other API call),\n// narrowed to this so no web-js-sdk/core-js-sdk type leaks into the widget's\n// public sdk surface. Declared last (with the mixin) to satisfy import/exports-last.\nexport interface ConditionsHttpClient {\n get: (path: string) => Promise<Response>;\n}\n\n/**\n * Shared widget mixin that fetches the server-computed component conditions\n * verdict for the current user and applies hide/disable/read-only to the widget\n * DOM. Server-side only - no client-side re-evaluation. Compose it LAST in a\n * widget's initMixin so its init wraps the widget's render.\n *\n * It reuses the widget's existing web-js-sdk httpClient (`this.api.httpClient`),\n * so `apiMixin` must be composed and `createSdk` must return `httpClient`. A\n * missing client is a wiring mistake, surfaced loudly (throw) rather than\n * silently rendering everything; a runtime fetch failure fails open instead.\n */\nexport const componentsConditionsMixin = createSingletonMixin(\n <T extends CustomElementConstructor>(superclass: T) => {\n // NB: do not compose initLifecycleMixin here - its empty init() has no\n // super.init() call and would shadow the widget's render init.\n const BaseClass = compose(loggerMixin, initElementMixin)(superclass);\n\n return class ComponentsConditionsMixinClass extends BaseClass {\n #statePromise?: Promise<ComponentsState>;\n\n #appliedState: ComponentsState = {};\n\n #requireHttpClient(): ConditionsHttpClient {\n const httpClient = (this as unknown as ComponentsConditionsHost).api\n ?.httpClient;\n if (!httpClient) {\n // Developer misconfiguration (apiMixin not composed, or createSdk does\n // not expose httpClient) - fail loud so it is caught in development.\n throw new Error(\n 'componentsConditionsMixin: the widget sdk must expose `httpClient`. Compose apiMixin and return `webSdk.httpClient` from createSdk.',\n );\n }\n return httpClient;\n }\n\n async #fetchComponentsState(\n httpClient: ConditionsHttpClient,\n ): Promise<ComponentsState> {\n try {\n const res = await httpClient.get(COMPONENTS_STATE_PATH);\n if (!res?.ok) return {};\n const body = await res.json();\n return body?.componentsState ?? {};\n } catch (e) {\n // Fail open: render everything. UI hiding is presentation, not access\n // control - sensitive data stays gated by the widget's data API.\n this.logger.debug(\n 'components-conditions: failed to fetch, showing all components',\n (e as Error)?.message,\n );\n return {};\n }\n }\n\n async init() {\n if (this.getAttribute('mock') === 'true') {\n // Mock mode (demos / e2e) has no backend - skip the fetch, hide nothing.\n this.#statePromise = Promise.resolve({});\n await super.init?.();\n return;\n }\n // Resolve the client up front so a wiring mistake throws before the\n // fetch's fail-open path can swallow it.\n const httpClient = this.#requireHttpClient();\n // Fire immediately so it overlaps the page/data fetch super.init runs.\n this.#statePromise = this.#fetchComponentsState(httpClient);\n await super.init?.();\n }\n\n async onWidgetRootReady() {\n // @ts-expect-error onWidgetRootReady is provided by the widget's initWidgetRootMixin\n await super.onWidgetRootReady?.();\n // Apply once the DOM exists and component values are populated, before the\n // widget dispatches 'ready'. Awaiting here bounds any added latency to\n // max(0, conditionsFetch - pageFetch).\n this.#appliedState = (await this.#statePromise) ?? {};\n applyComponentsState(this.contentRootElement, this.#appliedState);\n }\n\n /**\n * Re-fetch and re-apply after the widget mutates data the conditions read\n * (e.g. a profile save). Public hook for the upcoming re-evaluate-on-mutation\n * work - no widget wires it to its mutating flows yet.\n */\n async reevaluateComponentsState() {\n const httpClient = this.#requireHttpClient();\n clearComponentsState(this.contentRootElement, this.#appliedState);\n this.#appliedState = await this.#fetchComponentsState(httpClient);\n applyComponentsState(this.contentRootElement, this.#appliedState);\n }\n };\n },\n);\n"],"names":[],"mappings":"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"}
|