@fiodos/web-core 0.1.12 → 0.1.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/dist/cjs/adapters/webCredentialAutofillAdapter.d.ts +16 -0
  2. package/dist/cjs/adapters/webCredentialAutofillAdapter.js +47 -0
  3. package/dist/cjs/api/backendClient.d.ts +10 -3
  4. package/dist/cjs/api/backendClient.js +31 -9
  5. package/dist/cjs/config/types.d.ts +22 -2
  6. package/dist/cjs/controller/AgentController.d.ts +14 -0
  7. package/dist/cjs/controller/AgentController.js +65 -2
  8. package/dist/cjs/dropin/createFiodosAgent.d.ts +27 -1
  9. package/dist/cjs/dropin/createFiodosAgent.js +48 -4
  10. package/dist/cjs/embed/dynamics.d.ts +61 -0
  11. package/dist/cjs/embed/dynamics.js +173 -0
  12. package/dist/cjs/embed/global.d.ts +45 -0
  13. package/dist/cjs/embed/global.js +48 -0
  14. package/dist/cjs/index.d.ts +3 -0
  15. package/dist/cjs/index.js +8 -1
  16. package/dist/cjs/orb/mountOrb.js +172 -28
  17. package/dist/cjs/orb/orbView.js +42 -8
  18. package/dist/cjs/orb/publishedConfig.d.ts +4 -2
  19. package/dist/cjs/orb/publishedConfig.js +56 -7
  20. package/dist/cjs/version.d.ts +1 -1
  21. package/dist/cjs/version.js +1 -1
  22. package/dist/embed/fiodos-embed.js +66 -0
  23. package/dist/esm/adapters/webCredentialAutofillAdapter.d.ts +16 -0
  24. package/dist/esm/adapters/webCredentialAutofillAdapter.js +44 -0
  25. package/dist/esm/api/backendClient.d.ts +10 -3
  26. package/dist/esm/api/backendClient.js +32 -10
  27. package/dist/esm/config/types.d.ts +22 -2
  28. package/dist/esm/controller/AgentController.d.ts +14 -0
  29. package/dist/esm/controller/AgentController.js +65 -2
  30. package/dist/esm/dropin/createFiodosAgent.d.ts +27 -1
  31. package/dist/esm/dropin/createFiodosAgent.js +48 -4
  32. package/dist/esm/embed/dynamics.d.ts +61 -0
  33. package/dist/esm/embed/dynamics.js +168 -0
  34. package/dist/esm/embed/global.d.ts +45 -0
  35. package/dist/esm/embed/global.js +46 -0
  36. package/dist/esm/index.d.ts +3 -0
  37. package/dist/esm/index.js +3 -0
  38. package/dist/esm/orb/mountOrb.js +173 -29
  39. package/dist/esm/orb/orbView.js +43 -9
  40. package/dist/esm/orb/publishedConfig.d.ts +4 -2
  41. package/dist/esm/orb/publishedConfig.js +56 -7
  42. package/dist/esm/version.d.ts +1 -1
  43. package/dist/esm/version.js +1 -1
  44. package/package.json +5 -3
@@ -0,0 +1,173 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.normalizeDynamicsId = normalizeDynamicsId;
4
+ exports.dynamicsFormSnapshot = dynamicsFormSnapshot;
5
+ exports.connectDynamicsFormContext = connectDynamicsFormContext;
6
+ // ── Value normalization ──────────────────────────────────────────────────────
7
+ const MAX_VALUE_CHARS = 120;
8
+ function clip(text) {
9
+ const t = text.trim();
10
+ return t.length > MAX_VALUE_CHARS ? `${t.slice(0, MAX_VALUE_CHARS - 1)}…` : t;
11
+ }
12
+ /** Lookup values arrive as [{ id, name, entityType }] — show the names. */
13
+ function lookupNames(value) {
14
+ const names = value
15
+ .map((v) => v && typeof v === 'object' && typeof v.name === 'string'
16
+ ? (v.name)
17
+ : null)
18
+ .filter((n) => !!n);
19
+ return names.length ? names.join(', ') : null;
20
+ }
21
+ function normalizeAttributeValue(attribute) {
22
+ // Optionset / lookup / boolean attributes carry a human label — prefer it.
23
+ try {
24
+ const text = attribute.getText?.();
25
+ if (typeof text === 'string' && text.trim())
26
+ return clip(text);
27
+ if (Array.isArray(text) && text.length)
28
+ return clip(text.join(', '));
29
+ }
30
+ catch {
31
+ /* some attribute types throw on getText — fall through to raw value */
32
+ }
33
+ let value;
34
+ try {
35
+ value = attribute.getValue();
36
+ }
37
+ catch {
38
+ return null;
39
+ }
40
+ if (value == null)
41
+ return null;
42
+ if (typeof value === 'string')
43
+ return value.trim() ? clip(value) : null;
44
+ if (typeof value === 'number' || typeof value === 'boolean')
45
+ return value;
46
+ if (value instanceof Date)
47
+ return value.toISOString();
48
+ if (Array.isArray(value)) {
49
+ const names = lookupNames(value);
50
+ return names ? clip(names) : null;
51
+ }
52
+ return null; // collections / unknown object shapes are skipped, never guessed
53
+ }
54
+ /** "{1D2C…}" → "1d2c…" (Web API URLs want bare lowercase GUIDs). */
55
+ function normalizeDynamicsId(rawId) {
56
+ return rawId.replace(/[{}]/g, '').trim().toLowerCase();
57
+ }
58
+ // ── Snapshot builder ─────────────────────────────────────────────────────────
59
+ /**
60
+ * Builds the AgentScreenSnapshot for the current form state, or null when
61
+ * there is nothing referenceable yet (create form: no record id).
62
+ */
63
+ function dynamicsFormSnapshot(formContext, options = {}) {
64
+ const entity = formContext.data?.entity;
65
+ if (!entity)
66
+ return null;
67
+ const entityName = entity.getEntityName();
68
+ const recordId = normalizeDynamicsId(entity.getId() ?? '');
69
+ const allowlist = options.attributes?.length
70
+ ? new Set(options.attributes.map((a) => a.trim().toLowerCase()).filter(Boolean))
71
+ : null;
72
+ const maxAttributes = options.maxAttributes ?? 12;
73
+ const fields = {};
74
+ let count = 0;
75
+ entity.attributes?.forEach((attribute) => {
76
+ let name;
77
+ try {
78
+ name = attribute.getName();
79
+ }
80
+ catch {
81
+ return;
82
+ }
83
+ if (!name)
84
+ return;
85
+ if (allowlist) {
86
+ if (!allowlist.has(name.toLowerCase()))
87
+ return;
88
+ }
89
+ else if (count >= maxAttributes) {
90
+ return;
91
+ }
92
+ const value = normalizeAttributeValue(attribute);
93
+ if (value == null)
94
+ return;
95
+ fields[name] = value;
96
+ count += 1;
97
+ });
98
+ let title;
99
+ try {
100
+ title = entity.getPrimaryAttributeValue?.() ?? undefined;
101
+ }
102
+ catch {
103
+ title = undefined;
104
+ }
105
+ // Create forms have no id yet: the agent can still SEE the fields, but there
106
+ // is no target for execute actions, so we publish text-only context.
107
+ const item = recordId
108
+ ? { id: recordId, kind: entityName, title, metadata: fields }
109
+ : null;
110
+ const fieldSummary = Object.entries(fields)
111
+ .map(([k, v]) => `${k}: ${v}`)
112
+ .join('; ');
113
+ const textParts = [
114
+ `Dynamics 365 form — entity "${entityName}"`,
115
+ recordId ? `record "${title ?? recordId}" (id ${recordId})` : 'new record (not saved yet)',
116
+ ];
117
+ if (fieldSummary)
118
+ textParts.push(`Fields: ${fieldSummary}`);
119
+ const context = {
120
+ ...(item ? { visibleContent: { items: [item], focusedIndex: 0 } } : {}),
121
+ custom: {
122
+ platform: 'dynamics365',
123
+ entityName,
124
+ ...(recordId ? { recordId } : {}),
125
+ ...(typeof formContext.ui?.getFormType === 'function'
126
+ ? { formType: formContext.ui.getFormType() }
127
+ : {}),
128
+ },
129
+ };
130
+ return { kind: `dynamics-form:${entityName}`, text: textParts.join('. '), context };
131
+ }
132
+ /**
133
+ * Publishes the form snapshot now, republishes after every save (when the
134
+ * form exposes OnPostSave), and clears the context on disconnect. Call it from
135
+ * the form's OnLoad handler.
136
+ */
137
+ function connectDynamicsFormContext(agent, formContext, options = {}) {
138
+ let disconnected = false;
139
+ const publish = () => {
140
+ if (disconnected)
141
+ return;
142
+ const snapshot = dynamicsFormSnapshot(formContext, options);
143
+ if (snapshot)
144
+ agent.setScreenContext(snapshot);
145
+ else
146
+ agent.clearScreenContext();
147
+ };
148
+ publish();
149
+ const entity = formContext.data?.entity;
150
+ if (entity?.addOnPostSave) {
151
+ try {
152
+ entity.addOnPostSave(publish);
153
+ }
154
+ catch {
155
+ /* older clients — the host can still call refresh() manually */
156
+ }
157
+ }
158
+ return {
159
+ refresh: publish,
160
+ disconnect() {
161
+ if (disconnected)
162
+ return;
163
+ disconnected = true;
164
+ try {
165
+ entity?.removeOnPostSave?.(publish);
166
+ }
167
+ catch {
168
+ /* best-effort unsubscribe */
169
+ }
170
+ agent.clearScreenContext();
171
+ },
172
+ };
173
+ }
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Embed entrypoint — single-file IIFE bundle for CLOSED third-party hosts
3
+ * where npm/ESM is not available: Dynamics 365 web resources, WinCC Unified
4
+ * Custom Web Controls, Salesforce LWC iframes, plain <script> tags.
5
+ *
6
+ * Built by `npm run build:embed` into dist/embed/fiodos-embed.js and exposed
7
+ * as the `Fiodos` global:
8
+ *
9
+ * <script src="fiodos-embed.js"></script>
10
+ * <script>
11
+ * const agent = Fiodos.createFiodosAgent({
12
+ * manifest, // e.g. from `fiodos from-odata`
13
+ * registries: Fiodos.buildApiActionRegistries(manifest, {
14
+ * baseUrl: 'https://org.crm.dynamics.com/api/data/v9.2',
15
+ * authProviders: { dataverse: () => ({ Authorization: `Bearer ${token}` }) },
16
+ * }),
17
+ * });
18
+ * </script>
19
+ *
20
+ * The bundle is self-contained (core + web-core inlined) and side-effect-free
21
+ * beyond assigning the global — it never auto-mounts; the host decides when.
22
+ */
23
+ import { createFiodosAgent } from '../dropin/createFiodosAgent';
24
+ import { mountOrb } from '../orb/mountOrb';
25
+ import { AgentController } from '../controller/AgentController';
26
+ import { createScreenContextStore } from '../context/screenContextStore';
27
+ import { connectDynamicsFormContext, dynamicsFormSnapshot, normalizeDynamicsId } from './dynamics';
28
+ import { buildApiActionRegistries, validateManifest } from '@fiodos/core';
29
+ declare const api: {
30
+ readonly version: "0.1.15";
31
+ readonly createFiodosAgent: typeof createFiodosAgent;
32
+ readonly mountOrb: typeof mountOrb;
33
+ readonly AgentController: typeof AgentController;
34
+ readonly createScreenContextStore: typeof createScreenContextStore;
35
+ readonly buildApiActionRegistries: typeof buildApiActionRegistries;
36
+ readonly validateManifest: typeof validateManifest;
37
+ readonly connectDynamicsFormContext: typeof connectDynamicsFormContext;
38
+ readonly dynamicsFormSnapshot: typeof dynamicsFormSnapshot;
39
+ readonly normalizeDynamicsId: typeof normalizeDynamicsId;
40
+ };
41
+ declare global {
42
+ var Fiodos: typeof api | undefined;
43
+ }
44
+ export type FiodosEmbedApi = typeof api;
45
+ export {};
@@ -0,0 +1,48 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ /**
4
+ * Embed entrypoint — single-file IIFE bundle for CLOSED third-party hosts
5
+ * where npm/ESM is not available: Dynamics 365 web resources, WinCC Unified
6
+ * Custom Web Controls, Salesforce LWC iframes, plain <script> tags.
7
+ *
8
+ * Built by `npm run build:embed` into dist/embed/fiodos-embed.js and exposed
9
+ * as the `Fiodos` global:
10
+ *
11
+ * <script src="fiodos-embed.js"></script>
12
+ * <script>
13
+ * const agent = Fiodos.createFiodosAgent({
14
+ * manifest, // e.g. from `fiodos from-odata`
15
+ * registries: Fiodos.buildApiActionRegistries(manifest, {
16
+ * baseUrl: 'https://org.crm.dynamics.com/api/data/v9.2',
17
+ * authProviders: { dataverse: () => ({ Authorization: `Bearer ${token}` }) },
18
+ * }),
19
+ * });
20
+ * </script>
21
+ *
22
+ * The bundle is self-contained (core + web-core inlined) and side-effect-free
23
+ * beyond assigning the global — it never auto-mounts; the host decides when.
24
+ */
25
+ const createFiodosAgent_1 = require("../dropin/createFiodosAgent");
26
+ const mountOrb_1 = require("../orb/mountOrb");
27
+ const AgentController_1 = require("../controller/AgentController");
28
+ const screenContextStore_1 = require("../context/screenContextStore");
29
+ const dynamics_1 = require("./dynamics");
30
+ const version_1 = require("../version");
31
+ const core_1 = require("@fiodos/core");
32
+ const api = {
33
+ version: version_1.SDK_VERSION,
34
+ createFiodosAgent: createFiodosAgent_1.createFiodosAgent,
35
+ mountOrb: mountOrb_1.mountOrb,
36
+ AgentController: AgentController_1.AgentController,
37
+ createScreenContextStore: screenContextStore_1.createScreenContextStore,
38
+ // Closed-software mode: turn ApiExecutionSpec manifests into live handlers,
39
+ // and validate hand-written / from-odata manifests before mounting.
40
+ buildApiActionRegistries: core_1.buildApiActionRegistries,
41
+ validateManifest: core_1.validateManifest,
42
+ // Context bridges: tell the orb what the host is showing. Dynamics gets a
43
+ // dedicated mapper (formContext); any other host calls agent.setScreenContext.
44
+ connectDynamicsFormContext: dynamics_1.connectDynamicsFormContext,
45
+ dynamicsFormSnapshot: dynamics_1.dynamicsFormSnapshot,
46
+ normalizeDynamicsId: dynamics_1.normalizeDynamicsId,
47
+ };
48
+ globalThis.Fiodos = api;
@@ -26,6 +26,7 @@ export { createWebNavigationAdapter, BACK_ROUTE, } from './adapters/webNavigatio
26
26
  export type { WebNavigationAdapter, WebNavigationAdapterOptions, } from './adapters/webNavigationAdapter';
27
27
  export { createWebVoiceAdapter } from './adapters/webVoiceAdapter';
28
28
  export type { WebVoiceAdapterOptions } from './adapters/webVoiceAdapter';
29
+ export { createWebCredentialAutofillAdapter } from './adapters/webCredentialAutofillAdapter';
29
30
  export { createFiodosBackendClient, buildManifestPayload } from './api/backendClient';
30
31
  export type { FiodosBackendClientOptions } from './api/backendClient';
31
32
  export { createFiodosTelemetry } from './api/backendTelemetry';
@@ -36,6 +37,8 @@ export { createBridge } from './bridge/createBridge';
36
37
  export type { Bridge } from './bridge/createBridge';
37
38
  export { createScreenContextStore } from './context/screenContextStore';
38
39
  export type { ScreenContextStore, AgentScreenSnapshot } from './context/screenContextStore';
40
+ export { connectDynamicsFormContext, dynamicsFormSnapshot, normalizeDynamicsId, } from './embed/dynamics';
41
+ export type { DynamicsFormContextLike, DynamicsSnapshotOptions, DynamicsContextBridge, ScreenContextSink, } from './embed/dynamics';
39
42
  export { createSpeechSession } from './speech/speechSession';
40
43
  export type { SpeechSession, SpeechSessionOptions, SpeechSessionState, SpeechSessionSnapshot, } from './speech/speechSession';
41
44
  export { DEFAULT_AGENT_TIMINGS, DEFAULT_AGENT_CHAINING } from './config/types';
package/dist/cjs/index.js CHANGED
@@ -9,7 +9,7 @@
9
9
  * behaviour and security. No framework imports live here.
10
10
  */
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
- exports.classifyPendingReply = exports.decideAction = exports.resolveUiMessages = exports.DEFAULT_AGENT_CHAINING = exports.DEFAULT_AGENT_TIMINGS = exports.createSpeechSession = exports.createScreenContextStore = exports.createBridge = exports.isAgentApiError = exports.AgentApiError = exports.createFiodosTelemetry = exports.buildManifestPayload = exports.createFiodosBackendClient = exports.createWebVoiceAdapter = exports.BACK_ROUTE = exports.createWebNavigationAdapter = exports.createWebStorageAdapter = exports.watchPublishedConfig = exports.DEFAULT_ORB_APPEARANCE = exports.buildKeyboardChip = exports.createOrbVisual = exports.mountOrb = exports.sendOrbSeen = exports.fetchClientManifest = exports.DEFAULT_WEB_API_URL = exports.createFiodosAgent = exports.AgentController = exports.SDK_PACKAGE = exports.SDK_VERSION = void 0;
12
+ exports.classifyPendingReply = exports.decideAction = exports.resolveUiMessages = exports.DEFAULT_AGENT_CHAINING = exports.DEFAULT_AGENT_TIMINGS = exports.createSpeechSession = exports.normalizeDynamicsId = exports.dynamicsFormSnapshot = exports.connectDynamicsFormContext = exports.createScreenContextStore = exports.createBridge = exports.isAgentApiError = exports.AgentApiError = exports.createFiodosTelemetry = exports.buildManifestPayload = exports.createFiodosBackendClient = exports.createWebCredentialAutofillAdapter = exports.createWebVoiceAdapter = exports.BACK_ROUTE = exports.createWebNavigationAdapter = exports.createWebStorageAdapter = exports.watchPublishedConfig = exports.DEFAULT_ORB_APPEARANCE = exports.buildKeyboardChip = exports.createOrbVisual = exports.mountOrb = exports.sendOrbSeen = exports.fetchClientManifest = exports.DEFAULT_WEB_API_URL = exports.createFiodosAgent = exports.AgentController = exports.SDK_PACKAGE = exports.SDK_VERSION = void 0;
13
13
  // ── Version (reported by the orb heartbeat) ───────────────────────────────────
14
14
  var version_1 = require("./version");
15
15
  Object.defineProperty(exports, "SDK_VERSION", { enumerable: true, get: function () { return version_1.SDK_VERSION; } });
@@ -42,6 +42,8 @@ Object.defineProperty(exports, "createWebNavigationAdapter", { enumerable: true,
42
42
  Object.defineProperty(exports, "BACK_ROUTE", { enumerable: true, get: function () { return webNavigationAdapter_1.BACK_ROUTE; } });
43
43
  var webVoiceAdapter_1 = require("./adapters/webVoiceAdapter");
44
44
  Object.defineProperty(exports, "createWebVoiceAdapter", { enumerable: true, get: function () { return webVoiceAdapter_1.createWebVoiceAdapter; } });
45
+ var webCredentialAutofillAdapter_1 = require("./adapters/webCredentialAutofillAdapter");
46
+ Object.defineProperty(exports, "createWebCredentialAutofillAdapter", { enumerable: true, get: function () { return webCredentialAutofillAdapter_1.createWebCredentialAutofillAdapter; } });
45
47
  // ── Backend + telemetry (same wire contract as every platform) ────────────────
46
48
  var backendClient_1 = require("./api/backendClient");
47
49
  Object.defineProperty(exports, "createFiodosBackendClient", { enumerable: true, get: function () { return backendClient_1.createFiodosBackendClient; } });
@@ -57,6 +59,11 @@ Object.defineProperty(exports, "createBridge", { enumerable: true, get: function
57
59
  // ── Screen context ────────────────────────────────────────────────────────────
58
60
  var screenContextStore_1 = require("./context/screenContextStore");
59
61
  Object.defineProperty(exports, "createScreenContextStore", { enumerable: true, get: function () { return screenContextStore_1.createScreenContextStore; } });
62
+ // ── Closed-host context adapters (embed mode) ─────────────────────────────────
63
+ var dynamics_1 = require("./embed/dynamics");
64
+ Object.defineProperty(exports, "connectDynamicsFormContext", { enumerable: true, get: function () { return dynamics_1.connectDynamicsFormContext; } });
65
+ Object.defineProperty(exports, "dynamicsFormSnapshot", { enumerable: true, get: function () { return dynamics_1.dynamicsFormSnapshot; } });
66
+ Object.defineProperty(exports, "normalizeDynamicsId", { enumerable: true, get: function () { return dynamics_1.normalizeDynamicsId; } });
60
67
  // ── Speech session ──────────────────────────────────────────────────────────────
61
68
  var speechSession_1 = require("./speech/speechSession");
62
69
  Object.defineProperty(exports, "createSpeechSession", { enumerable: true, get: function () { return speechSession_1.createSpeechSession; } });