@formio/uag-js 2.0.0-rc.6 → 2.0.0-rc.7

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 CHANGED
@@ -15,6 +15,28 @@ Formio.createForm(document.getElementById('claim'), 'https://forms.example.com/m
15
15
 
16
16
  Requests to the UAG carry the user's Form.io token (`x-jwt-token`), as formio.js sends it to Form.io; no other token is needed. Routing in the browser is not a security boundary: protect the fields an agent owns on the server, with an agent role, form permissions and `uagAuthoritative`.
17
17
 
18
+ ## AI Document
19
+
20
+ The **AI Document** component (`uagdocument`) is a UAG module: it comes with `@formio/uag-extras/modules/ai-document`, not with this package. A UAG that uses the module adds the component to `/uag-js/uag.use.js` and the portal script; an application with a bundler registers it after this package: `Formio.use(UAG(url)); Formio.use(aiDocument);`. It builds on `client`, below.
21
+
22
+ ## For components: `client`
23
+
24
+ Once `Formio.use(UAG(url))` has run, `client` (also `FormioUAG.client` and `UAG.client`) is the API that components of UAG modules build on, stable within a major version:
25
+
26
+ | Member | What it gives |
27
+ |---|---|
28
+ | `url()`, `token(form?)`, `headers(form?)` | The UAG's URL, the user's Form.io token, and the headers that identify the user to the UAG. |
29
+ | `formUrl(definition)` | A form's URL on the UAG, from its definition or a rendered form. |
30
+ | `ready`, `loaded`, `plugins(type?)`, `plugin(type, name)` | The installed plugins (`document`, `converter`, `module`), embedded by the UAG's scripts or fetched with the user's token. |
31
+ | `stream(url, body, { onEvent, signal, form })` | POST JSON, accepting server-sent events: every event goes to `onEvent`; it resolves with the `done` event's data (or the JSON answer), and rejects with the UAG's message, `status` and `code`. |
32
+ | `renderMarkdown(text)`, `escapeHtml(text)` | Markdown as the UAG sanitizes it, rendered with every text escaped. |
33
+
34
+ ```js
35
+ import { client } from '@formio/uag-js';
36
+ await client.ready;
37
+ const done = await client.stream(client.formUrl(form) + '/document/process', { path, files }, { onEvent: (name, data) => {} });
38
+ ```
39
+
18
40
  ## Chat
19
41
 
20
42
  Any form tagged `uag` can also be rendered as a chat, the way `Formio.createForm` renders it as a form. A person describes what they need; the chat asks for what is still missing and fills the form behind it, which validates and submits through the UAG once they confirm. Each turn is the form's `{form}/agent/chat` on the UAG, so there is no model code and no server of your own, and it needs no Agentic Formflow license (the UAG needs an agent engine).
package/index.d.ts CHANGED
@@ -189,11 +189,112 @@ export interface ChatForm {
189
189
  export interface UAGOptions extends Omit<AttachOptions, 'uagUrl' | 'formUrl'> {
190
190
  /** `false` attaches no Agent Input anywhere; submissions of `uag` forms still go to the UAG. */
191
191
  agentInput?: boolean;
192
+ /** AI Document options. `auto`: process files on upload unless a component turns it off (default true). */
193
+ documents?: { auto?: boolean };
194
+ /**
195
+ * Portal mode (the UAG's portal script): register only the AI Document
196
+ * component and the manifest. Saves are not routed and Agent Input is not run.
197
+ */
198
+ portal?: boolean;
199
+ /** The installed plugins, as `GET {project}/plugins` describes them. Fetched at registration when not given. */
200
+ manifest?: PluginManifestEntry[];
201
+ }
202
+
203
+ /** One installed plugin, as the UAG's manifest describes it. */
204
+ export interface PluginManifestEntry {
205
+ type: string;
206
+ name: string;
207
+ title: string;
208
+ description?: string;
209
+ version: string;
210
+ stability: 'stable' | 'experimental';
211
+ editForm?: any[];
212
+ /** Document plugins: the file types the installed converters can bring to it. */
213
+ accept?: Array<{ type: string; ext: string[]; routes: Record<string, any>; converters: string[] }>;
214
+ inputs?: Array<Array<'pdf' | 'text'>>;
215
+ editable?: boolean;
216
+ output?: 'markdown' | 'text';
217
+ defaults?: Record<string, any>;
218
+ [field: string]: any;
192
219
  }
193
220
 
221
+ /** An AI Document's stored `result`. */
222
+ export interface AIDocumentResult {
223
+ content: string;
224
+ format: 'markdown' | 'text';
225
+ status: 'ok' | 'flagged';
226
+ issues: Array<{ kind: string; message: string; output?: string; document?: string; page?: number; file?: string }>;
227
+ summary: string;
228
+ edited: boolean;
229
+ meta: { plugin: string; engine: string; model: string; files: Array<{ name: string; route: string[]; pages?: number }> };
230
+ }
231
+
232
+ /**
233
+ * Events an AI Document emits on its form: `uagDocumentStart` ({ component, path, files }),
234
+ * `uagDocumentProgress` ({ component, path, progress, message? }), `uagDocumentResult`
235
+ * ({ component, path, result }), `uagDocumentError` ({ component, path, error }).
236
+ */
237
+ export type AIDocumentEvent = 'uagDocumentStart' | 'uagDocumentProgress' | 'uagDocumentResult' | 'uagDocumentError';
238
+
239
+ /** Render Markdown as the AI Document does: headings, paragraphs, lists, tables, emphasis, code and safe links, all text escaped. */
240
+ export declare function renderMarkdown(markdown: string): string;
241
+
242
+ /** Options for `client.stream`. */
243
+ export interface ClientStreamOptions {
244
+ /** Called with every server-sent event, by name. */
245
+ onEvent?(name: string, data: any): void;
246
+ signal?: AbortSignal;
247
+ /** The rendered form, whose Form.io token identifies the user. */
248
+ form?: any;
249
+ headers?: Record<string, string>;
250
+ fetch?: typeof fetch;
251
+ }
252
+
253
+ /** An error the UAG answered with. */
254
+ export interface UAGClientError extends Error {
255
+ status?: number;
256
+ code?: string;
257
+ }
258
+
259
+ /**
260
+ * The public browser API that components of UAG modules build on, once
261
+ * `Formio.use(UAG(url))` has run. Stable within a major version.
262
+ */
263
+ export interface UAGClient {
264
+ /** The UAG's URL. */
265
+ url(): string;
266
+ /** The user's Form.io token, from the form's own formio or the SDK. */
267
+ token(form?: any): string | null;
268
+ /** The headers that identify the user to the UAG. */
269
+ headers(form?: any): Record<string, string>;
270
+ /** A form's URL on the UAG, from its definition (or a rendered form). */
271
+ formUrl(definition: any): string;
272
+ /** Resolves once the installed plugins are known. */
273
+ ready: Promise<void>;
274
+ /** Whether the installed plugins are known yet. */
275
+ readonly loaded: boolean;
276
+ /** The installed plugins, of one type or all. */
277
+ plugins(type?: string): PluginManifestEntry[];
278
+ plugin(type: string, name: string): PluginManifestEntry | null;
279
+ /** POST JSON, accepting server-sent events: resolves with the `done` event's data, or the JSON answer. */
280
+ stream(url: string, body: any, options?: ClientStreamOptions): Promise<any>;
281
+ renderMarkdown(markdown: string): string;
282
+ escapeHtml(value: unknown): string;
283
+ /** The formio.js the UAG was registered with. */
284
+ readonly Formio: any;
285
+ }
286
+
287
+ export declare const client: UAGClient;
288
+
194
289
  /** The formio.js module: `Formio.use(UAG('https://uag.example.com'))`. It also registers the `chat` display and `Formio.createChat`. */
195
290
  export declare function UAG(uagUrl: string, options?: UAGOptions): (Formio: any) => { fetch: Record<string, any>; displays?: Record<string, any> };
196
291
  export declare namespace UAG {
292
+ /** Resolves once the installed document plugins are known (embedded by the UAG's scripts, or fetched at registration). */
293
+ const ready: Promise<void>;
294
+ /** The same as `renderMarkdown`. */
295
+ function renderMarkdown(markdown: string): string;
296
+ /** The same as `client`. */
297
+ const client: UAGClient;
197
298
  /** The same as `attach`. */
198
299
  function attach(form: any, options?: AttachOptions): AgentInput;
199
300
  /** The same as `createChat`. */
package/index.js CHANGED
@@ -1424,6 +1424,227 @@
1424
1424
  return wrapper.setForm(src);
1425
1425
  }
1426
1426
 
1427
+
1428
+ // ---------------------------------------------------------------------
1429
+ // The installed plugins (the UAG's manifest), for the modules' components.
1430
+ // ---------------------------------------------------------------------
1431
+
1432
+ /** The installed plugins, as the UAG's manifest describes them. */
1433
+ var manifestState = { manifest: null, resolve: null, promise: null };
1434
+ manifestState.promise = new Promise(function (resolve) { manifestState.resolve = resolve; });
1435
+
1436
+ function setManifest(manifest) {
1437
+ manifestState.manifest = Array.isArray(manifest) ? manifest : [];
1438
+ manifestState.resolve();
1439
+ }
1440
+
1441
+ /** Fetch the manifest from the UAG, with the user's token. Resolves with [] when it cannot. */
1442
+ function fetchManifest(uagUrl, sdk) {
1443
+ var fetchImpl = (registered.options && registered.options.fetch) || (typeof fetch === 'function' ? fetch : null);
1444
+ if (!fetchImpl) return Promise.resolve([]);
1445
+ var headers = { Accept: 'application/json' };
1446
+ try {
1447
+ var token = sdk && typeof sdk.getToken === 'function' ? sdk.getToken() : null;
1448
+ if (token) headers['x-jwt-token'] = token;
1449
+ }
1450
+ catch (err) { /* no token */ }
1451
+ return Promise.resolve().then(function () {
1452
+ return fetchImpl(trimSlash(uagUrl) + '/plugins', { headers: headers });
1453
+ }).then(function (response) {
1454
+ return response && response.ok ? response.json() : [];
1455
+ }).catch(function () { return []; });
1456
+ }
1457
+
1458
+ // --- Markdown, as sanitizeMarkdown keeps it, rendered with every text escaped ---
1459
+
1460
+ function safeHref(href) {
1461
+ return /^(https?:|mailto:)/i.test(String(href || '').trim()) ? String(href).trim() : '';
1462
+ }
1463
+
1464
+ /** Inline Markdown: backslash escapes, code, links (safe targets only), strong, emphasis. Everything else is escaped text. */
1465
+ function inlineMarkdown(text) {
1466
+ var out = '';
1467
+ var rest = String(text || '');
1468
+ var pattern = /(`+)([^`]*?)\1|\[([^\]]*)\]\(\s*<?([^)\s>]*)>?(?:\s+"[^"]*")?\s*\)|<((?:https?:|mailto:)[^>\s]+)>|\*\*([^*]+)\*\*|__([^_]+)__|\*([^*\s][^*]*?)\*|_([^_\s][^_]*?)_|\\([!-\/:-@[-`{-~])/;
1469
+ var match;
1470
+ while ((match = pattern.exec(rest))) {
1471
+ out += escapeHtml(rest.slice(0, match.index));
1472
+ // A backslash escape (sanitizeMarkdown escapes what it shows as text): the character itself.
1473
+ if (match[10]) out += escapeHtml(match[10]);
1474
+ else if (match[1]) out += '<code>' + escapeHtml(match[2]) + '</code>';
1475
+ else if (match[3] !== undefined) {
1476
+ var href = safeHref(match[4]);
1477
+ out += href ? '<a href="' + escapeHtml(href) + '" target="_blank" rel="noopener noreferrer">' + inlineMarkdown(match[3]) + '</a>' : inlineMarkdown(match[3]);
1478
+ }
1479
+ else if (match[5]) out += '<a href="' + escapeHtml(match[5]) + '" target="_blank" rel="noopener noreferrer">' + escapeHtml(match[5]) + '</a>';
1480
+ else if (match[6] || match[7]) out += '<strong>' + inlineMarkdown(match[6] || match[7]) + '</strong>';
1481
+ else out += '<em>' + inlineMarkdown(match[8] || match[9]) + '</em>';
1482
+ rest = rest.slice(match.index + match[0].length);
1483
+ }
1484
+ return out + escapeHtml(rest);
1485
+ }
1486
+
1487
+ function tableRow(line) {
1488
+ return line.trim().replace(/^\|/, '').replace(/\|$/, '').split('|').map(function (cell) { return cell.trim(); });
1489
+ }
1490
+
1491
+ /**
1492
+ * Render Markdown to HTML: headings, paragraphs, lists, tables, emphasis,
1493
+ * code and http/https/mailto links, nothing else. All text is escaped.
1494
+ */
1495
+ function renderMarkdown(markdown) {
1496
+ var lines = String(markdown || '').replace(/\r\n?/g, '\n').split('\n');
1497
+ var html = [];
1498
+ var i = 0;
1499
+ while (i < lines.length) {
1500
+ var line = lines[i];
1501
+ var fence = /^\s{0,3}(`{3,}|~{3,})/.exec(line);
1502
+ if (fence) {
1503
+ var code = [];
1504
+ i++;
1505
+ while (i < lines.length && !(new RegExp('^\\s{0,3}' + fence[1][0] + '{' + fence[1].length + ',}\\s*$')).test(lines[i])) code.push(lines[i++]);
1506
+ i++;
1507
+ html.push('<pre><code>' + escapeHtml(code.join('\n')) + '</code></pre>');
1508
+ continue;
1509
+ }
1510
+ if (!line.trim()) { i++; continue; }
1511
+ var heading = /^\s{0,3}(#{1,6})\s+(.*?)\s*#*\s*$/.exec(line);
1512
+ if (heading) {
1513
+ html.push('<h' + heading[1].length + '>' + inlineMarkdown(heading[2]) + '</h' + heading[1].length + '>');
1514
+ i++;
1515
+ continue;
1516
+ }
1517
+ if (/^\s*\|/.test(line) && i + 1 < lines.length && /^\s*\|?\s*:?-{1,}:?\s*(\|\s*:?-{1,}:?\s*)*\|?\s*$/.test(lines[i + 1])) {
1518
+ var head = tableRow(line);
1519
+ i += 2;
1520
+ var rows = [];
1521
+ while (i < lines.length && /^\s*\|/.test(lines[i])) rows.push(tableRow(lines[i++]));
1522
+ html.push('<table class="table table-sm"><thead><tr>' + head.map(function (cell) { return '<th>' + inlineMarkdown(cell) + '</th>'; }).join('') + '</tr></thead><tbody>' +
1523
+ rows.map(function (row) { return '<tr>' + row.map(function (cell) { return '<td>' + inlineMarkdown(cell) + '</td>'; }).join('') + '</tr>'; }).join('') + '</tbody></table>');
1524
+ continue;
1525
+ }
1526
+ var item = /^\s*([-*+]|\d+[.)])\s+(.*)$/.exec(line);
1527
+ if (item) {
1528
+ var ordered = /\d/.test(item[1]);
1529
+ var items = [];
1530
+ while (i < lines.length) {
1531
+ var next = /^\s*([-*+]|\d+[.)])\s+(.*)$/.exec(lines[i]);
1532
+ if (!next || /\d/.test(next[1]) !== ordered) break;
1533
+ items.push('<li>' + inlineMarkdown(next[2]) + '</li>');
1534
+ i++;
1535
+ }
1536
+ html.push((ordered ? '<ol>' : '<ul>') + items.join('') + (ordered ? '</ol>' : '</ul>'));
1537
+ continue;
1538
+ }
1539
+ var paragraph = [];
1540
+ while (i < lines.length && lines[i].trim() && !/^\s{0,3}(#{1,6}\s|`{3,}|~{3,})/.test(lines[i]) && !/^\s*([-*+]|\d+[.)])\s+/.test(lines[i]) && !/^\s*\|/.test(lines[i])) {
1541
+ paragraph.push(lines[i++].trim());
1542
+ }
1543
+ if (!paragraph.length) { html.push('<p>' + inlineMarkdown(lines[i++]) + '</p>'); continue; }
1544
+ html.push('<p>' + paragraph.map(inlineMarkdown).join('<br>') + '</p>');
1545
+ }
1546
+ return html.join('');
1547
+ }
1548
+
1549
+ // ---------------------------------------------------------------------
1550
+ // client: the public browser API that components of UAG modules build on.
1551
+ // ---------------------------------------------------------------------
1552
+
1553
+ /** The headers that identify the user to the UAG: their Form.io token, and a portal team member's remote token. */
1554
+ function authHeaders(form) {
1555
+ var headers = {};
1556
+ var token = formioToken(form);
1557
+ if (token) headers['x-jwt-token'] = token;
1558
+ try {
1559
+ var sdk = registered.Formio;
1560
+ var remote = sdk && typeof sdk.getRemoteToken === 'function' ? sdk.getRemoteToken() : null;
1561
+ if (remote) headers['x-remote-token'] = remote;
1562
+ }
1563
+ catch (err) { /* no remote token */ }
1564
+ return headers;
1565
+ }
1566
+
1567
+ /** An Error with the UAG's status and code. */
1568
+ function uagError(message, status, code) {
1569
+ var err = new Error(message);
1570
+ if (status) err.status = status;
1571
+ if (code) err.code = code;
1572
+ return err;
1573
+ }
1574
+
1575
+ /**
1576
+ * POST JSON to the UAG, accepting server-sent events. Resolves with the
1577
+ * `done` event's data, or with the JSON answer; rejects with the UAG's
1578
+ * message (and `status`, `code`) for an error answer or a `failed` event.
1579
+ * Every event is passed to `options.onEvent(name, data)`.
1580
+ */
1581
+ function stream(url, body, options) {
1582
+ options = options || {};
1583
+ var fetchImpl = options.fetch || (registered.options && registered.options.fetch) || (typeof fetch === 'function' ? fetch : null);
1584
+ if (!fetchImpl) return Promise.reject(uagError('fetch is not available.'));
1585
+ var headers = Object.assign({ 'Content-Type': 'application/json', Accept: 'text/event-stream' }, authHeaders(options.form), options.headers || {});
1586
+ var done = null;
1587
+ var failure = null;
1588
+ return Promise.resolve().then(function () {
1589
+ return fetchImpl(url, { method: 'POST', headers: headers, body: JSON.stringify(body || {}), signal: options.signal });
1590
+ }).then(function (response) {
1591
+ var type = (response.headers && response.headers.get && response.headers.get('content-type')) || '';
1592
+ if (!response.ok || type.indexOf('text/event-stream') === -1) {
1593
+ return response.json().catch(function () { return {}; }).then(function (answer) {
1594
+ if (!response.ok) throw uagError((answer && answer.error) || ('The request failed (' + response.status + ').'), response.status, answer && answer.code);
1595
+ done = answer;
1596
+ });
1597
+ }
1598
+ var parser = new SSEParser(function (name, data) {
1599
+ if (typeof options.onEvent === 'function') options.onEvent(name, data);
1600
+ if (name === 'done') done = data;
1601
+ else if (name === 'failed' || name === 'error') failure = uagError((data && data.error) || 'The request failed.', data && data.status, data && data.code);
1602
+ });
1603
+ if (response.body && response.body.getReader) {
1604
+ var reader = response.body.getReader();
1605
+ var decoder = new TextDecoder();
1606
+ var pump = function () {
1607
+ return reader.read().then(function (chunk) {
1608
+ if (chunk.done) return;
1609
+ parser.push(decoder.decode(chunk.value, { stream: true }));
1610
+ return pump();
1611
+ });
1612
+ };
1613
+ return pump();
1614
+ }
1615
+ return response.text().then(function (text) { parser.push(text + '\n\n'); });
1616
+ }).then(function () {
1617
+ if (failure) throw failure;
1618
+ if (done === null) throw uagError('The request ended without an answer.');
1619
+ return done;
1620
+ });
1621
+ }
1622
+
1623
+ /**
1624
+ * The public browser API for components: the UAG's URL, the user's token,
1625
+ * a form's URL on the UAG, the installed plugins, streamed requests, and
1626
+ * Markdown rendering. Stable within a major version of @formio/uag-js.
1627
+ */
1628
+ var client = {
1629
+ url: function () { return registered.uagUrl; },
1630
+ token: function (form) { return formioToken(form); },
1631
+ headers: function (form) { return authHeaders(form); },
1632
+ formUrl: function (definition) { return uagFormUrl(registered.uagUrl, definition && definition.form && definition.form._id ? definition.form : definition); },
1633
+ ready: manifestState.promise,
1634
+ /** Whether the installed plugins are known yet (`ready` has resolved). */
1635
+ get loaded() { return manifestState.manifest !== null; },
1636
+ plugins: function (type) {
1637
+ return (manifestState.manifest || []).filter(function (plugin) { return plugin && (!type || plugin.type === type); });
1638
+ },
1639
+ plugin: function (type, name) {
1640
+ return client.plugins(type).filter(function (plugin) { return plugin.name === name; })[0] || null;
1641
+ },
1642
+ stream: stream,
1643
+ renderMarkdown: function (markdown) { return renderMarkdown(markdown); },
1644
+ escapeHtml: function (value) { return escapeHtml(value); },
1645
+ get Formio() { return registered.Formio; }
1646
+ };
1647
+
1427
1648
  /**
1428
1649
  * The formio.js module: `Formio.use(UAG('https://uag.example.com', options))`.
1429
1650
  * Options: `agentInput` (`false` turns Agent Input off), and Agent Input's
@@ -1432,8 +1653,19 @@
1432
1653
  */
1433
1654
  function UAG(uagUrl, options) {
1434
1655
  if (!uagUrl) throw new Error('UAG(): give the UAG\'s URL, e.g. UAG(\'https://uag.example.com\').');
1656
+ options = options || {};
1435
1657
  return function (Formio) {
1436
- registered = { uagUrl: trimSlash(uagUrl), Formio: Formio || null, options: options || {} };
1658
+ registered = { uagUrl: trimSlash(uagUrl), Formio: Formio || null, options: options };
1659
+ // Components of UAG modules find the client on the formio.js they are given.
1660
+ if (Formio && (typeof Formio === 'object' || typeof Formio === 'function')) {
1661
+ try { Object.defineProperty(Formio, 'uagClient', { value: client, configurable: true }); }
1662
+ catch (err) { /* a frozen Formio: components use FormioUAG.client */ }
1663
+ }
1664
+ // The installed plugins (the manifest), which components of UAG modules read through `client`.
1665
+ if (options.manifest) setManifest(options.manifest);
1666
+ else if (!manifestState.manifest) fetchManifest(registered.uagUrl, Formio).then(setManifest);
1667
+ // Portal mode: only the manifest and the client, for the modules' components; saves and Agent Input are left alone.
1668
+ if (options.portal) return {};
1437
1669
  var mod = { fetch: { uag: routingPlugin(registered.uagUrl) } };
1438
1670
  var displays = (Formio && Formio.Displays && Formio.Displays.displays) || {};
1439
1671
  mod.displays = {};
@@ -1452,6 +1684,10 @@
1452
1684
  }
1453
1685
  UAG.attach = attach;
1454
1686
  UAG.createChat = createChat;
1687
+ /** Resolves once the installed document plugins are known (embedded, or fetched at registration). */
1688
+ UAG.ready = manifestState.promise;
1689
+ UAG.renderMarkdown = renderMarkdown;
1690
+ UAG.client = client;
1455
1691
 
1456
1692
  return {
1457
1693
  UAG: UAG,
@@ -1462,6 +1698,8 @@
1462
1698
  ExtractionState: ExtractionState,
1463
1699
  SSEParser: SSEParser,
1464
1700
  isEmpty: isEmpty,
1465
- CHAT_TEMPLATES: CHAT_TEMPLATES
1701
+ CHAT_TEMPLATES: CHAT_TEMPLATES,
1702
+ renderMarkdown: renderMarkdown,
1703
+ client: client
1466
1704
  };
1467
1705
  }));
package/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  // The ES module entry: the same module as index.js, which is CommonJS (and a browser global).
2
2
  import uag from './index.js';
3
3
 
4
- export const { UAG, attach, createChat, uagFormUrl, routingPlugin, ExtractionState, SSEParser, isEmpty, CHAT_TEMPLATES } = uag;
4
+ export const { UAG, attach, createChat, uagFormUrl, routingPlugin, ExtractionState, SSEParser, isEmpty, CHAT_TEMPLATES, renderMarkdown, client } = uag;
5
5
  export default uag;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@formio/uag-js",
3
- "version": "2.0.0-rc.6",
3
+ "version": "2.0.0-rc.7",
4
4
  "description": "The Form.io UAG for formio.js applications: register it once, and forms tagged \"uag\" save through the UAG and get Agent Input, without overwriting what the user entered.",
5
5
  "keywords": [
6
6
  "formio",
@@ -30,6 +30,7 @@
30
30
  "index.mjs",
31
31
  "index.d.ts",
32
32
  "uag-js.css",
33
+ "uag.use.js",
33
34
  "README.md",
34
35
  "LICENSE.txt"
35
36
  ],
package/uag.use.js ADDED
@@ -0,0 +1,25 @@
1
+ /**
2
+ * @formio/uag-js for a page without a bundler. Load it after formio.js and
3
+ * this package's index.js, from the UAG it should talk to:
4
+ *
5
+ * <script src="https://cdn.form.io/js/formio.full.min.js"></script>
6
+ * <script src="https://uag.example.com/uag-js/uag.use.js"></script>
7
+ *
8
+ * The UAG serves this file at /uag-js/uag.use.js with the bundle and its
9
+ * manifest included. On its own, it registers FormioUAG (index.js) with the
10
+ * UAG it was loaded from, read from its own src, and fetches the manifest.
11
+ */
12
+ (function () {
13
+ var script = document.currentScript;
14
+ var src = script && script.src ? new URL(script.src, location.href) : null;
15
+ var uagUrl = src && src.href.indexOf('/uag-js/') !== -1 ? src.href.slice(0, src.href.indexOf('/uag-js/')) : (src ? src.origin : location.origin);
16
+ function register() {
17
+ if (!window.Formio || !window.FormioUAG || typeof window.Formio.use !== 'function') return false;
18
+ window.Formio.use(window.FormioUAG.UAG(uagUrl));
19
+ return true;
20
+ }
21
+ if (!register()) {
22
+ var tries = 0;
23
+ var timer = setInterval(function () { if (register() || ++tries > 600) clearInterval(timer); }, 50);
24
+ }
25
+ })();