@oneblink/sdk-core 10.1.0-beta.8 → 10.1.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.
@@ -115,4 +115,21 @@ declare const fixElementName: (elementName: string) => string;
115
115
  * @returns
116
116
  */
117
117
  declare function injectFormElementsIntoForm(form: FormTypes.Form, forms: FormTypes.Form[], injectAuthenticatedForms?: boolean): FormTypes.FormElement[];
118
- export { forEachFormElement, forEachFormElementWithOptions, findFormElement, parseDynamicFormElementOptions, flattenFormElements, determineIsInfoPage, fixElementName, injectFormElementsIntoForm, };
118
+ /**
119
+ * Async counterpart to {@link injectFormElementsIntoForm}. Injects embedded
120
+ * `form` / `infoPage` elements for each form in `forms`. Uses `getForm` to load
121
+ * embedded forms and caches results by id for the duration of the call so each
122
+ * form is retrieved at most once across the whole batch.
123
+ *
124
+ * Mutates each form object in `forms` in place (the array entries are updated
125
+ * by reference); nothing is returned. Forms returned from `getForm` are not
126
+ * mutated — their element trees are cloned before embedding.
127
+ *
128
+ * @param forms The forms to inject elements into (mutated in place)
129
+ * @param getForm Retrieves a single form by id when it is referenced by a
130
+ * `form` / `infoPage` element
131
+ * @param injectAuthenticatedForms Indicates whether forms requiring
132
+ * authentication should be injected, defaults to true
133
+ */
134
+ declare function injectFormElementsIntoForms(forms: FormTypes.Form[], getForm: (formId: number) => Promise<FormTypes.Form | undefined>, injectAuthenticatedForms?: boolean): Promise<void>;
135
+ export { forEachFormElement, forEachFormElementWithOptions, findFormElement, parseDynamicFormElementOptions, flattenFormElements, determineIsInfoPage, fixElementName, injectFormElementsIntoForm, injectFormElementsIntoForms, };
@@ -252,37 +252,24 @@ function injectFormElements(elements, forms, parentIds, injectAuthenticatedForms
252
252
  element.elements = childElements;
253
253
  }
254
254
  if (element.type === 'form' || element.type === 'infoPage') {
255
- const formToInject = forms.find((form) => element.formId === form.id);
256
- if (!formToInject) {
257
- const newElement = {
258
- ...element,
259
- type: 'html',
260
- name: 'Form_not_found',
261
- label: 'Form not found.',
262
- defaultValue: 'Unable to display the embedded form for this element, as the form was not found. Please contact your Administrator.',
263
- };
264
- newElements.push(newElement);
265
- return newElements;
255
+ const resolved = resolveEmbeddedFormElement(element, forms.find((form) => element.formId === form.id), parentIds, injectAuthenticatedForms);
256
+ switch (resolved.type) {
257
+ case 'replace': {
258
+ newElements.push(resolved.element);
259
+ break;
260
+ }
261
+ case 'skip': {
262
+ break;
263
+ }
264
+ case 'continue': {
265
+ // Clone so inject mutations (e.g. containers rewritten when cycle
266
+ // edges are omitted) do not alter the source form, which may be
267
+ // shared via the forms list.
268
+ element.elements = injectFormElements(structuredClone(resolved.form.elements), forms, [...parentIds, resolved.form.id], injectAuthenticatedForms);
269
+ newElements.push(element);
270
+ break;
271
+ }
266
272
  }
267
- if (formToInject.isAuthenticated && !injectAuthenticatedForms) {
268
- console.log(`No form elements injected for element id: ${element.id}, as request was unauthenticated and target form (form id: ${formToInject.id}) requires authentication.`);
269
- const newElement = {
270
- ...element,
271
- type: 'html',
272
- name: 'Form_requires_authenticated',
273
- label: 'Form Requires Authentication.',
274
- defaultValue: 'Unable to display the embedded form for this element, as the form requires authentication. Please login and refresh to view this embedded form.',
275
- };
276
- newElements.push(newElement);
277
- return newElements;
278
- }
279
- const injectingParentForm = parentIds && parentIds.find((id) => element.formId === id);
280
- if (injectingParentForm) {
281
- console.log(`Infinite loop was detected while attempting to inject form id: ${injectingParentForm}. Ignoring elements...`);
282
- return newElements;
283
- }
284
- element.elements = injectFormElements(formToInject.elements, forms, [...parentIds, formToInject.id], injectAuthenticatedForms);
285
- newElements.push(element);
286
273
  }
287
274
  else {
288
275
  newElements.push(element);
@@ -290,5 +277,104 @@ function injectFormElements(elements, forms, parentIds, injectAuthenticatedForms
290
277
  return newElements;
291
278
  }, []);
292
279
  }
293
- export { forEachFormElement, forEachFormElementWithOptions, findFormElement, parseDynamicFormElementOptions, flattenFormElements, determineIsInfoPage, fixElementName, injectFormElementsIntoForm, };
280
+ function resolveEmbeddedFormElement(element, formToInject, parentIds, injectAuthenticatedForms) {
281
+ if (!formToInject) {
282
+ return {
283
+ type: 'replace',
284
+ element: {
285
+ ...element,
286
+ type: 'html',
287
+ name: 'Form_not_found',
288
+ label: 'Form not found.',
289
+ defaultValue: 'Unable to display the embedded form for this element, as the form was not found. Please contact your Administrator.',
290
+ },
291
+ };
292
+ }
293
+ if (formToInject.isAuthenticated && !injectAuthenticatedForms) {
294
+ console.log(`No form elements injected for element id: ${element.id}, as request was unauthenticated and target form (form id: ${formToInject.id}) requires authentication.`);
295
+ return {
296
+ type: 'replace',
297
+ element: {
298
+ ...element,
299
+ type: 'html',
300
+ name: 'Form_requires_authenticated',
301
+ label: 'Form Requires Authentication.',
302
+ defaultValue: 'Unable to display the embedded form for this element, as the form requires authentication. Please login and refresh to view this embedded form.',
303
+ },
304
+ };
305
+ }
306
+ const injectingParentForm = parentIds.find((id) => element.formId === id);
307
+ if (injectingParentForm) {
308
+ console.log(`Infinite loop was detected while attempting to inject form id: ${injectingParentForm}. Ignoring elements...`);
309
+ return { type: 'skip' };
310
+ }
311
+ return { type: 'continue', form: formToInject };
312
+ }
313
+ /**
314
+ * Async counterpart to {@link injectFormElementsIntoForm}. Injects embedded
315
+ * `form` / `infoPage` elements for each form in `forms`. Uses `getForm` to load
316
+ * embedded forms and caches results by id for the duration of the call so each
317
+ * form is retrieved at most once across the whole batch.
318
+ *
319
+ * Mutates each form object in `forms` in place (the array entries are updated
320
+ * by reference); nothing is returned. Forms returned from `getForm` are not
321
+ * mutated — their element trees are cloned before embedding.
322
+ *
323
+ * @param forms The forms to inject elements into (mutated in place)
324
+ * @param getForm Retrieves a single form by id when it is referenced by a
325
+ * `form` / `infoPage` element
326
+ * @param injectAuthenticatedForms Indicates whether forms requiring
327
+ * authentication should be injected, defaults to true
328
+ */
329
+ async function injectFormElementsIntoForms(forms, getForm, injectAuthenticatedForms = true) {
330
+ const formCache = new Map();
331
+ const getFormCached = async (formId) => {
332
+ if (formCache.has(formId)) {
333
+ return formCache.get(formId);
334
+ }
335
+ const retrieved = (await getForm(formId)) ?? undefined;
336
+ formCache.set(formId, retrieved);
337
+ return retrieved;
338
+ };
339
+ for (const form of forms) {
340
+ form.elements = await injectFormElementsAsync(form.elements, getFormCached, [form.id], injectAuthenticatedForms);
341
+ }
342
+ }
343
+ async function injectFormElementsAsync(elements, getForm, parentIds, injectAuthenticatedForms) {
344
+ const newElements = [];
345
+ for (const element of elements) {
346
+ if ((element.type === 'page' ||
347
+ element.type === 'repeatableSet' ||
348
+ element.type === 'section') &&
349
+ Array.isArray(element.elements)) {
350
+ element.elements = await injectFormElementsAsync(element.elements, getForm, parentIds, injectAuthenticatedForms);
351
+ }
352
+ if (element.type === 'form' || element.type === 'infoPage') {
353
+ const form = await getForm(element.formId);
354
+ const resolved = resolveEmbeddedFormElement(element, form, parentIds, injectAuthenticatedForms);
355
+ switch (resolved.type) {
356
+ case 'replace': {
357
+ newElements.push(resolved.element);
358
+ break;
359
+ }
360
+ case 'skip': {
361
+ break;
362
+ }
363
+ case 'continue': {
364
+ // Clone so inject mutations (e.g. containers rewritten when cycle
365
+ // edges are omitted) do not alter the source form, which may be
366
+ // cached or a later batch root.
367
+ element.elements = await injectFormElementsAsync(structuredClone(resolved.form.elements), getForm, [...parentIds, resolved.form.id], injectAuthenticatedForms);
368
+ newElements.push(element);
369
+ break;
370
+ }
371
+ }
372
+ }
373
+ else {
374
+ newElements.push(element);
375
+ }
376
+ }
377
+ return newElements;
378
+ }
379
+ export { forEachFormElement, forEachFormElementWithOptions, findFormElement, parseDynamicFormElementOptions, flattenFormElements, determineIsInfoPage, fixElementName, injectFormElementsIntoForm, injectFormElementsIntoForms, };
294
380
  //# sourceMappingURL=formElementsService.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"formElementsService.js","sourceRoot":"","sources":["../src/formElementsService.ts"],"names":[],"mappings":"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","sourcesContent":["import { FormTypes } from '@oneblink/types'\nimport { typeCastService } from './index.js'\nexport { matchElementsTagRegex } from './form-elements-regex.js'\n\n/**\n * Iterate over all form elements, also iterating over nested form element (e.g.\n * page elements).\n *\n * #### Example\n *\n * ```js\n * formElementsService.forEachFormElement(form.elements, (formElement) => {\n * // do something with formElement\n * })\n * ```\n *\n * @param elements The form elements to iterate over\n * @param forEach Function to execute on each form element\n */\nfunction forEachFormElement(\n elements: FormTypes.FormElement[],\n forEach: (\n element: FormTypes.FormElement,\n elements: FormTypes.FormElement[],\n ) => void,\n): void {\n findFormElement(elements, (formElement, parentElements) => {\n forEach(formElement, parentElements)\n return false\n })\n}\n\n/**\n * Iterate over all form elements that have options (e.g. `'select'` type\n * elements), also iterating over nested form element (e.g. page elements).\n *\n * #### Example\n *\n * ```js\n * formElementsService.forEachFormElementWithOptions(\n * form.elements,\n * (formElementWithOptions) => {\n * // do something with formElementWithOptions\n * },\n * )\n * ```\n *\n * @param elements The form elements to iterate over\n * @param forEach Function to execute on each form element with options\n */\nfunction forEachFormElementWithOptions(\n elements: FormTypes.FormElement[],\n forEach: (\n elementWithOptions: FormTypes.FormElementWithOptions,\n elements: FormTypes.FormElement[],\n ) => void,\n): void {\n findFormElement(elements, (formElement, parentElements) => {\n const optionsFormElement =\n typeCastService.formElements.toOptionsElement(formElement)\n if (optionsFormElement) {\n forEach(optionsFormElement, parentElements)\n }\n return false\n })\n}\n\n/**\n * Iterate over all form elements and return an element that matches a\n * predicate, also iterating over nested form element (e.g. page elements). Will\n * return `undefined` if no matching element is found.\n *\n * #### Example\n *\n * ```js\n * const formElement = formElementsService.findFormElement(\n * form.elements,\n * (formElement) => {\n * return formElement.id === '123-abc'\n * },\n * )\n * ```\n *\n * @param elements The form elements to iterate over\n * @param predicate Predicate function to execute on each form element\n * @param parentElements\n * @returns\n */\nfunction findFormElement(\n elements: FormTypes.FormElement[],\n predicate: (\n element: FormTypes.FormElement,\n elements: FormTypes.FormElement[],\n ) => boolean,\n parentElements: FormTypes.FormElement[] = [],\n): FormTypes.FormElement | undefined {\n for (const element of elements) {\n if (predicate(element, parentElements)) {\n return element\n }\n\n if (\n (element.type === 'repeatableSet' ||\n element.type === 'page' ||\n element.type === 'form' ||\n element.type === 'infoPage' ||\n element.type === 'section') &&\n Array.isArray(element.elements)\n ) {\n const nestedElement = findFormElement(element.elements, predicate, [\n ...parentElements,\n element,\n ])\n\n if (nestedElement) {\n return nestedElement\n }\n }\n }\n}\n\n/**\n * Parse unknown data as valid dynamic options for a forms element. This will\n * always return an Array of valid dynamic options.\n *\n * #### Example\n *\n * ```js\n * const options = formElementsService.parseDynamicFormElementOptions(data)\n * // \"options\" are valid for a form element\n * ```\n *\n * @param data\n * @returns\n */\nfunction parseDynamicFormElementOptions(\n data: unknown,\n): FormTypes.DynamicChoiceElementOption[] {\n if (!Array.isArray(data)) {\n return []\n }\n return data.reduce<FormTypes.DynamicChoiceElementOption[]>(\n (options, record, index) => {\n if (typeof record === 'string') {\n options.push({\n value: record,\n label: record,\n })\n } else if (typeof record === 'object') {\n const option = record as Record<string, unknown>\n const value =\n typeof option.value === 'string' && option.value\n ? option.value\n : index.toString()\n const label =\n typeof option.label === 'string' && option.label\n ? option.label\n : value\n const colour =\n typeof option.colour === 'string' && option.colour\n ? option.colour\n : undefined\n const displayAlways =\n typeof option.displayAlways === 'boolean'\n ? option.displayAlways\n : undefined\n const imageUrl =\n typeof option.imageUrl === 'string' && option.imageUrl\n ? option.imageUrl\n : undefined\n const attributes = Array.isArray(option.attributes)\n ? option.attributes.reduce<\n FormTypes.DynamicChoiceElementOptionAttribute[]\n >((memo, attribute: unknown) => {\n if (\n typeof attribute === 'object' &&\n attribute &&\n 'value' in attribute &&\n typeof attribute.value === 'string' &&\n 'label' in attribute &&\n typeof attribute.label === 'string'\n ) {\n memo.push({\n value: attribute.value,\n label: attribute.label,\n })\n }\n return memo\n }, [])\n : undefined\n options.push({\n value,\n label,\n colour,\n attributes,\n displayAlways,\n imageUrl,\n options: Array.isArray(option.options)\n ? parseDynamicFormElementOptions(option.options)\n : undefined,\n })\n }\n return options\n },\n [],\n )\n}\n\n/**\n * Takes the nested definition structure and returns all form elements as 1d\n * array.\n *\n * #### Example\n *\n * ```js\n * const flattenedElements = formElementsService.flattenFormElements(\n * form.elements,\n * )\n * ```\n *\n * @param elements\n * @returns\n */\nfunction flattenFormElements(\n elements: FormTypes.FormElement[],\n): FormTypes.FormElement[] {\n return elements.reduce<FormTypes.FormElement[]>(\n (flattenedElements, element) => {\n flattenedElements.push(element)\n switch (element.type) {\n case 'section':\n case 'page': {\n flattenedElements.push(...flattenFormElements(element.elements))\n }\n }\n return flattenedElements\n },\n [],\n )\n}\n\nconst infoPageElements: FormTypes.FormElementType[] = [\n 'heading',\n 'html',\n 'image',\n 'section',\n 'page',\n 'infoPage',\n 'form',\n]\n/**\n * Determine a form is considered an info page. This means the form does not\n * allow any user input.\n *\n * @param form\n * @returns\n */\nfunction determineIsInfoPage(form: FormTypes.Form): boolean {\n const foundInputElement = findFormElement(form.elements, (e) => {\n return !infoPageElements.includes(e.type)\n })\n return !foundInputElement\n}\n\n/**\n * Remove invalid characters from a form element name.\n *\n * @param elementName\n * @returns\n */\nconst fixElementName = (elementName: string) => {\n // removes characters that aren't letters, numbers, underscores or dashes\n // replaces empty spaces with _\n return elementName\n .replace(/[^-\\w\\s]/g, '')\n .replace(/\\s/g, '_')\n .trim()\n}\n\n/**\n * Injects the elements of any elements with type `FORM` or `INFOPAGE` into the\n * form\n *\n * @param form The form to inject elements into\n * @param forms The forms that will be used to inject elements from if\n * referenced\n * @param injectAuthenticatedForms Indicates whether forms requiring\n * authentication should be injected, defaults to true\n * @returns\n */\nfunction injectFormElementsIntoForm(\n form: FormTypes.Form,\n forms: FormTypes.Form[],\n injectAuthenticatedForms = true,\n): FormTypes.FormElement[] {\n const elementsWithInjectedForms = injectFormElements(\n form.elements,\n forms,\n [form.id],\n injectAuthenticatedForms,\n )\n form.elements = elementsWithInjectedForms\n return form.elements\n}\n\nfunction injectFormElements(\n elements: FormTypes.FormElement[],\n forms: FormTypes.Form[],\n parentIds: number[],\n injectAuthenticatedForms: boolean,\n): FormTypes.FormElement[] {\n return elements.reduce<FormTypes.FormElement[]>((newElements, element) => {\n if (\n (element.type === 'page' ||\n element.type === 'repeatableSet' ||\n element.type === 'section') &&\n Array.isArray(element.elements)\n ) {\n const childElements = injectFormElements(\n element.elements,\n forms,\n parentIds,\n injectAuthenticatedForms,\n )\n element.elements = childElements\n }\n\n if (element.type === 'form' || element.type === 'infoPage') {\n const formToInject = forms.find((form) => element.formId === form.id)\n\n if (!formToInject) {\n const newElement: FormTypes.HtmlElement = {\n ...element,\n type: 'html',\n name: 'Form_not_found',\n label: 'Form not found.',\n defaultValue:\n 'Unable to display the embedded form for this element, as the form was not found. Please contact your Administrator.',\n }\n newElements.push(newElement)\n return newElements\n }\n\n if (formToInject.isAuthenticated && !injectAuthenticatedForms) {\n console.log(\n `No form elements injected for element id: ${element.id}, as request was unauthenticated and target form (form id: ${formToInject.id}) requires authentication.`,\n )\n\n const newElement: FormTypes.HtmlElement = {\n ...element,\n type: 'html',\n name: 'Form_requires_authenticated',\n label: 'Form Requires Authentication.',\n defaultValue:\n 'Unable to display the embedded form for this element, as the form requires authentication. Please login and refresh to view this embedded form.',\n }\n newElements.push(newElement)\n return newElements\n }\n\n const injectingParentForm =\n parentIds && parentIds.find((id) => element.formId === id)\n\n if (injectingParentForm) {\n console.log(\n `Infinite loop was detected while attempting to inject form id: ${injectingParentForm}. Ignoring elements...`,\n )\n return newElements\n }\n\n element.elements = injectFormElements(\n formToInject.elements,\n forms,\n [...parentIds, formToInject.id],\n injectAuthenticatedForms,\n )\n newElements.push(element)\n } else {\n newElements.push(element)\n }\n\n return newElements\n }, [])\n}\n\nexport {\n forEachFormElement,\n forEachFormElementWithOptions,\n findFormElement,\n parseDynamicFormElementOptions,\n flattenFormElements,\n determineIsInfoPage,\n fixElementName,\n injectFormElementsIntoForm,\n}\n"]}
1
+ {"version":3,"file":"formElementsService.js","sourceRoot":"","sources":["../src/formElementsService.ts"],"names":[],"mappings":"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","sourcesContent":["import { FormTypes } from '@oneblink/types'\nimport { typeCastService } from './index.js'\nexport { matchElementsTagRegex } from './form-elements-regex.js'\n\n/**\n * Iterate over all form elements, also iterating over nested form element (e.g.\n * page elements).\n *\n * #### Example\n *\n * ```js\n * formElementsService.forEachFormElement(form.elements, (formElement) => {\n * // do something with formElement\n * })\n * ```\n *\n * @param elements The form elements to iterate over\n * @param forEach Function to execute on each form element\n */\nfunction forEachFormElement(\n elements: FormTypes.FormElement[],\n forEach: (\n element: FormTypes.FormElement,\n elements: FormTypes.FormElement[],\n ) => void,\n): void {\n findFormElement(elements, (formElement, parentElements) => {\n forEach(formElement, parentElements)\n return false\n })\n}\n\n/**\n * Iterate over all form elements that have options (e.g. `'select'` type\n * elements), also iterating over nested form element (e.g. page elements).\n *\n * #### Example\n *\n * ```js\n * formElementsService.forEachFormElementWithOptions(\n * form.elements,\n * (formElementWithOptions) => {\n * // do something with formElementWithOptions\n * },\n * )\n * ```\n *\n * @param elements The form elements to iterate over\n * @param forEach Function to execute on each form element with options\n */\nfunction forEachFormElementWithOptions(\n elements: FormTypes.FormElement[],\n forEach: (\n elementWithOptions: FormTypes.FormElementWithOptions,\n elements: FormTypes.FormElement[],\n ) => void,\n): void {\n findFormElement(elements, (formElement, parentElements) => {\n const optionsFormElement =\n typeCastService.formElements.toOptionsElement(formElement)\n if (optionsFormElement) {\n forEach(optionsFormElement, parentElements)\n }\n return false\n })\n}\n\n/**\n * Iterate over all form elements and return an element that matches a\n * predicate, also iterating over nested form element (e.g. page elements). Will\n * return `undefined` if no matching element is found.\n *\n * #### Example\n *\n * ```js\n * const formElement = formElementsService.findFormElement(\n * form.elements,\n * (formElement) => {\n * return formElement.id === '123-abc'\n * },\n * )\n * ```\n *\n * @param elements The form elements to iterate over\n * @param predicate Predicate function to execute on each form element\n * @param parentElements\n * @returns\n */\nfunction findFormElement(\n elements: FormTypes.FormElement[],\n predicate: (\n element: FormTypes.FormElement,\n elements: FormTypes.FormElement[],\n ) => boolean,\n parentElements: FormTypes.FormElement[] = [],\n): FormTypes.FormElement | undefined {\n for (const element of elements) {\n if (predicate(element, parentElements)) {\n return element\n }\n\n if (\n (element.type === 'repeatableSet' ||\n element.type === 'page' ||\n element.type === 'form' ||\n element.type === 'infoPage' ||\n element.type === 'section') &&\n Array.isArray(element.elements)\n ) {\n const nestedElement = findFormElement(element.elements, predicate, [\n ...parentElements,\n element,\n ])\n\n if (nestedElement) {\n return nestedElement\n }\n }\n }\n}\n\n/**\n * Parse unknown data as valid dynamic options for a forms element. This will\n * always return an Array of valid dynamic options.\n *\n * #### Example\n *\n * ```js\n * const options = formElementsService.parseDynamicFormElementOptions(data)\n * // \"options\" are valid for a form element\n * ```\n *\n * @param data\n * @returns\n */\nfunction parseDynamicFormElementOptions(\n data: unknown,\n): FormTypes.DynamicChoiceElementOption[] {\n if (!Array.isArray(data)) {\n return []\n }\n return data.reduce<FormTypes.DynamicChoiceElementOption[]>(\n (options, record, index) => {\n if (typeof record === 'string') {\n options.push({\n value: record,\n label: record,\n })\n } else if (typeof record === 'object') {\n const option = record as Record<string, unknown>\n const value =\n typeof option.value === 'string' && option.value\n ? option.value\n : index.toString()\n const label =\n typeof option.label === 'string' && option.label\n ? option.label\n : value\n const colour =\n typeof option.colour === 'string' && option.colour\n ? option.colour\n : undefined\n const displayAlways =\n typeof option.displayAlways === 'boolean'\n ? option.displayAlways\n : undefined\n const imageUrl =\n typeof option.imageUrl === 'string' && option.imageUrl\n ? option.imageUrl\n : undefined\n const attributes = Array.isArray(option.attributes)\n ? option.attributes.reduce<\n FormTypes.DynamicChoiceElementOptionAttribute[]\n >((memo, attribute: unknown) => {\n if (\n typeof attribute === 'object' &&\n attribute &&\n 'value' in attribute &&\n typeof attribute.value === 'string' &&\n 'label' in attribute &&\n typeof attribute.label === 'string'\n ) {\n memo.push({\n value: attribute.value,\n label: attribute.label,\n })\n }\n return memo\n }, [])\n : undefined\n options.push({\n value,\n label,\n colour,\n attributes,\n displayAlways,\n imageUrl,\n options: Array.isArray(option.options)\n ? parseDynamicFormElementOptions(option.options)\n : undefined,\n })\n }\n return options\n },\n [],\n )\n}\n\n/**\n * Takes the nested definition structure and returns all form elements as 1d\n * array.\n *\n * #### Example\n *\n * ```js\n * const flattenedElements = formElementsService.flattenFormElements(\n * form.elements,\n * )\n * ```\n *\n * @param elements\n * @returns\n */\nfunction flattenFormElements(\n elements: FormTypes.FormElement[],\n): FormTypes.FormElement[] {\n return elements.reduce<FormTypes.FormElement[]>(\n (flattenedElements, element) => {\n flattenedElements.push(element)\n switch (element.type) {\n case 'section':\n case 'page': {\n flattenedElements.push(...flattenFormElements(element.elements))\n }\n }\n return flattenedElements\n },\n [],\n )\n}\n\nconst infoPageElements: FormTypes.FormElementType[] = [\n 'heading',\n 'html',\n 'image',\n 'section',\n 'page',\n 'infoPage',\n 'form',\n]\n/**\n * Determine a form is considered an info page. This means the form does not\n * allow any user input.\n *\n * @param form\n * @returns\n */\nfunction determineIsInfoPage(form: FormTypes.Form): boolean {\n const foundInputElement = findFormElement(form.elements, (e) => {\n return !infoPageElements.includes(e.type)\n })\n return !foundInputElement\n}\n\n/**\n * Remove invalid characters from a form element name.\n *\n * @param elementName\n * @returns\n */\nconst fixElementName = (elementName: string) => {\n // removes characters that aren't letters, numbers, underscores or dashes\n // replaces empty spaces with _\n return elementName\n .replace(/[^-\\w\\s]/g, '')\n .replace(/\\s/g, '_')\n .trim()\n}\n\n/**\n * Injects the elements of any elements with type `FORM` or `INFOPAGE` into the\n * form\n *\n * @param form The form to inject elements into\n * @param forms The forms that will be used to inject elements from if\n * referenced\n * @param injectAuthenticatedForms Indicates whether forms requiring\n * authentication should be injected, defaults to true\n * @returns\n */\nfunction injectFormElementsIntoForm(\n form: FormTypes.Form,\n forms: FormTypes.Form[],\n injectAuthenticatedForms = true,\n): FormTypes.FormElement[] {\n const elementsWithInjectedForms = injectFormElements(\n form.elements,\n forms,\n [form.id],\n injectAuthenticatedForms,\n )\n form.elements = elementsWithInjectedForms\n return form.elements\n}\n\nfunction injectFormElements(\n elements: FormTypes.FormElement[],\n forms: FormTypes.Form[],\n parentIds: number[],\n injectAuthenticatedForms: boolean,\n): FormTypes.FormElement[] {\n return elements.reduce<FormTypes.FormElement[]>((newElements, element) => {\n if (\n (element.type === 'page' ||\n element.type === 'repeatableSet' ||\n element.type === 'section') &&\n Array.isArray(element.elements)\n ) {\n const childElements = injectFormElements(\n element.elements,\n forms,\n parentIds,\n injectAuthenticatedForms,\n )\n element.elements = childElements\n }\n\n if (element.type === 'form' || element.type === 'infoPage') {\n const resolved = resolveEmbeddedFormElement(\n element,\n forms.find((form) => element.formId === form.id),\n parentIds,\n injectAuthenticatedForms,\n )\n\n switch (resolved.type) {\n case 'replace': {\n newElements.push(resolved.element)\n break\n }\n case 'skip': {\n break\n }\n case 'continue': {\n // Clone so inject mutations (e.g. containers rewritten when cycle\n // edges are omitted) do not alter the source form, which may be\n // shared via the forms list.\n element.elements = injectFormElements(\n structuredClone(resolved.form.elements),\n forms,\n [...parentIds, resolved.form.id],\n injectAuthenticatedForms,\n )\n newElements.push(element)\n break\n }\n }\n } else {\n newElements.push(element)\n }\n\n return newElements\n }, [])\n}\n\ntype ResolveEmbeddedFormResult =\n | { type: 'replace'; element: FormTypes.HtmlElement }\n | { type: 'skip' }\n | { type: 'continue'; form: FormTypes.Form }\n\nfunction resolveEmbeddedFormElement(\n element: FormTypes.FormFormElement,\n formToInject: FormTypes.Form | undefined,\n parentIds: number[],\n injectAuthenticatedForms: boolean,\n): ResolveEmbeddedFormResult {\n if (!formToInject) {\n return {\n type: 'replace',\n element: {\n ...element,\n type: 'html',\n name: 'Form_not_found',\n label: 'Form not found.',\n defaultValue:\n 'Unable to display the embedded form for this element, as the form was not found. Please contact your Administrator.',\n },\n }\n }\n\n if (formToInject.isAuthenticated && !injectAuthenticatedForms) {\n console.log(\n `No form elements injected for element id: ${element.id}, as request was unauthenticated and target form (form id: ${formToInject.id}) requires authentication.`,\n )\n\n return {\n type: 'replace',\n element: {\n ...element,\n type: 'html',\n name: 'Form_requires_authenticated',\n label: 'Form Requires Authentication.',\n defaultValue:\n 'Unable to display the embedded form for this element, as the form requires authentication. Please login and refresh to view this embedded form.',\n },\n }\n }\n\n const injectingParentForm = parentIds.find((id) => element.formId === id)\n\n if (injectingParentForm) {\n console.log(\n `Infinite loop was detected while attempting to inject form id: ${injectingParentForm}. Ignoring elements...`,\n )\n return { type: 'skip' }\n }\n\n return { type: 'continue', form: formToInject }\n}\n\n/**\n * Async counterpart to {@link injectFormElementsIntoForm}. Injects embedded\n * `form` / `infoPage` elements for each form in `forms`. Uses `getForm` to load\n * embedded forms and caches results by id for the duration of the call so each\n * form is retrieved at most once across the whole batch.\n *\n * Mutates each form object in `forms` in place (the array entries are updated\n * by reference); nothing is returned. Forms returned from `getForm` are not\n * mutated — their element trees are cloned before embedding.\n *\n * @param forms The forms to inject elements into (mutated in place)\n * @param getForm Retrieves a single form by id when it is referenced by a\n * `form` / `infoPage` element\n * @param injectAuthenticatedForms Indicates whether forms requiring\n * authentication should be injected, defaults to true\n */\nasync function injectFormElementsIntoForms(\n forms: FormTypes.Form[],\n getForm: (formId: number) => Promise<FormTypes.Form | undefined>,\n injectAuthenticatedForms = true,\n): Promise<void> {\n const formCache = new Map<number, FormTypes.Form | undefined>()\n\n const getFormCached = async (formId: number) => {\n if (formCache.has(formId)) {\n return formCache.get(formId)\n }\n const retrieved = (await getForm(formId)) ?? undefined\n formCache.set(formId, retrieved)\n return retrieved\n }\n\n for (const form of forms) {\n form.elements = await injectFormElementsAsync(\n form.elements,\n getFormCached,\n [form.id],\n injectAuthenticatedForms,\n )\n }\n}\n\nasync function injectFormElementsAsync(\n elements: FormTypes.FormElement[],\n getForm: (formId: number) => Promise<FormTypes.Form | undefined>,\n parentIds: number[],\n injectAuthenticatedForms: boolean,\n): Promise<FormTypes.FormElement[]> {\n const newElements: FormTypes.FormElement[] = []\n\n for (const element of elements) {\n if (\n (element.type === 'page' ||\n element.type === 'repeatableSet' ||\n element.type === 'section') &&\n Array.isArray(element.elements)\n ) {\n element.elements = await injectFormElementsAsync(\n element.elements,\n getForm,\n parentIds,\n injectAuthenticatedForms,\n )\n }\n\n if (element.type === 'form' || element.type === 'infoPage') {\n const form = await getForm(element.formId)\n const resolved = resolveEmbeddedFormElement(\n element,\n form,\n parentIds,\n injectAuthenticatedForms,\n )\n\n switch (resolved.type) {\n case 'replace': {\n newElements.push(resolved.element)\n break\n }\n case 'skip': {\n break\n }\n case 'continue': {\n // Clone so inject mutations (e.g. containers rewritten when cycle\n // edges are omitted) do not alter the source form, which may be\n // cached or a later batch root.\n element.elements = await injectFormElementsAsync(\n structuredClone(resolved.form.elements),\n getForm,\n [...parentIds, resolved.form.id],\n injectAuthenticatedForms,\n )\n newElements.push(element)\n break\n }\n }\n } else {\n newElements.push(element)\n }\n }\n\n return newElements\n}\n\nexport {\n forEachFormElement,\n forEachFormElementWithOptions,\n findFormElement,\n parseDynamicFormElementOptions,\n flattenFormElements,\n determineIsInfoPage,\n fixElementName,\n injectFormElementsIntoForm,\n injectFormElementsIntoForms,\n}\n"]}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@oneblink/sdk-core",
3
3
  "description": "OneBlink SDK for JavaScript (works in Browsers and NodeJS)",
4
- "version": "10.1.0-beta.8",
4
+ "version": "10.1.0",
5
5
  "author": "OneBlink <developers@oneblink.io> (https://oneblink.io)",
6
6
  "bugs": {
7
7
  "url": "https://github.com/oneblink/sdk-core-js/issues"