bsmnt 0.6.4 → 0.7.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/application/add-integration/index.d.ts.map +1 -1
- package/dist/application/add-integration/index.js +30 -4
- package/dist/application/add-integration/index.js.map +1 -1
- package/dist/application/shared/node-version.d.ts +8 -0
- package/dist/application/shared/node-version.d.ts.map +1 -0
- package/dist/application/shared/node-version.js +31 -0
- package/dist/application/shared/node-version.js.map +1 -0
- package/dist/application/shared/strip-ansi.d.ts +3 -0
- package/dist/application/shared/strip-ansi.d.ts.map +1 -0
- package/dist/application/shared/strip-ansi.js +7 -0
- package/dist/application/shared/strip-ansi.js.map +1 -0
- package/dist/core/create/execute-plan.d.ts +3 -0
- package/dist/core/create/execute-plan.d.ts.map +1 -1
- package/dist/core/create/execute-plan.js +3 -0
- package/dist/core/create/execute-plan.js.map +1 -1
- package/dist/domain/cms.d.ts +2 -0
- package/dist/domain/cms.d.ts.map +1 -1
- package/dist/domain/cms.js +1 -0
- package/dist/domain/cms.js.map +1 -1
- package/dist/domain/forms.d.ts +2 -2
- package/dist/domain/forms.js +1 -1
- package/dist/domain/forms.js.map +1 -1
- package/dist/domain/integration.d.ts +7 -3
- package/dist/domain/integration.d.ts.map +1 -1
- package/dist/domain/integration.js.map +1 -1
- package/dist/domain/skills.d.ts +26 -8
- package/dist/domain/skills.d.ts.map +1 -1
- package/dist/domain/skills.js +30 -9
- package/dist/domain/skills.js.map +1 -1
- package/dist/index.js +132 -16
- package/dist/index.js.map +1 -1
- package/dist/infrastructure/create/executor.d.ts.map +1 -1
- package/dist/infrastructure/create/executor.js +2 -1
- package/dist/infrastructure/create/executor.js.map +1 -1
- package/dist/infrastructure/create/init-git-repo.d.ts +11 -7
- package/dist/infrastructure/create/init-git-repo.d.ts.map +1 -1
- package/dist/infrastructure/create/init-git-repo.js +21 -18
- package/dist/infrastructure/create/init-git-repo.js.map +1 -1
- package/dist/infrastructure/create/sanity-auth.d.ts +11 -0
- package/dist/infrastructure/create/sanity-auth.d.ts.map +1 -0
- package/dist/infrastructure/create/sanity-auth.js +72 -0
- package/dist/infrastructure/create/sanity-auth.js.map +1 -0
- package/dist/infrastructure/create/setup-agent.d.ts +4 -3
- package/dist/infrastructure/create/setup-agent.d.ts.map +1 -1
- package/dist/infrastructure/create/setup-agent.js +51 -30
- package/dist/infrastructure/create/setup-agent.js.map +1 -1
- package/dist/infrastructure/create/setup-dotenvx.js +1 -1
- package/dist/infrastructure/create/setup-dotenvx.js.map +1 -1
- package/dist/infrastructure/create/setup-remote.d.ts +14 -0
- package/dist/infrastructure/create/setup-remote.d.ts.map +1 -0
- package/dist/infrastructure/create/setup-remote.js +139 -0
- package/dist/infrastructure/create/setup-remote.js.map +1 -0
- package/dist/infrastructure/create/setup-sanity.d.ts +3 -2
- package/dist/infrastructure/create/setup-sanity.d.ts.map +1 -1
- package/dist/infrastructure/create/setup-sanity.js +34 -49
- package/dist/infrastructure/create/setup-sanity.js.map +1 -1
- package/dist/modules/features/env/dotenvx/config.d.ts.map +1 -1
- package/dist/modules/features/env/dotenvx/config.js +6 -0
- package/dist/modules/features/env/dotenvx/config.js.map +1 -1
- package/dist/paths.d.ts +0 -2
- package/dist/paths.d.ts.map +1 -1
- package/dist/paths.js +0 -2
- package/dist/paths.js.map +1 -1
- package/package.json +3 -3
- package/src/agent-skills/hubspot-pagebuilder-block/SKILL.md +160 -0
- package/src/agent-skills/rss-feed/SKILL.md +158 -0
- package/src/modules/features/cms/sanity/files/lib/integrations/sanity/sanity.config.ts +3 -0
- package/src/modules/features/cms/sanity-pagebuilder/files/lib/integrations/sanity/sanity.config.ts +3 -0
- package/src/modules/features/cms/sanity-pagebuilder/files/lib/scripts/sanity-typegen.ts +16 -25
- package/src/modules/features/env/dotenvx/files/push-keys.mjs +7 -7
- package/src/modules/features/env/dotenvx/files/setup-remote.ts +182 -0
- package/src/modules/features/forms/hubspot/files/_sanity-adapter/form-block.ts +60 -0
- package/src/modules/features/forms/hubspot/files/_sanity-adapter/form-field.ts +30 -0
- package/src/modules/features/forms/hubspot/files/_sanity-adapter/form-select-input.tsx +169 -0
- package/src/modules/features/forms/hubspot/files/lib/integrations/hubspot/README.md +114 -0
- package/src/modules/features/forms/hubspot/files/lib/integrations/hubspot/embed.tsx +165 -0
- package/src/modules/features/forms/hubspot/files/lib/integrations/hubspot/form-selection.ts +40 -0
- package/src/modules/features/forms/hubspot/files/lib/integrations/hubspot/form.tsx +29 -0
- package/src/modules/features/forms/hubspot/files/lib/integrations/hubspot/forms.ts +142 -0
- package/src/modules/features/forms/hubspot/files/lib/integrations/hubspot/hubspot-form.css +634 -0
- package/src/modules/features/forms/hubspot/files/lib/integrations/hubspot/skeleton.tsx +24 -0
- package/src/templates/next-default/README.md +1 -0
- package/src/templates/next-default/knip.json +9 -1
- package/src/templates/next-experiments/README.md +1 -0
- package/src/templates/next-experiments/knip.json +9 -1
- package/src/templates/next-pagebuilder/README.md +1 -0
- package/src/templates/next-pagebuilder/knip.json +7 -2
- package/src/templates/next-pagebuilder/lib/integrations/sanity/sanity.types.ts +1197 -176
- package/src/templates/next-webgl/README.md +1 -0
- package/src/templates/next-webgl/knip.json +9 -1
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import { useCallback, useEffect, useId, useRef, useState } from "react"
|
|
4
|
+
import { HubSpotFormSkeleton } from "./skeleton"
|
|
5
|
+
import "./hubspot-form.css"
|
|
6
|
+
|
|
7
|
+
const HUBSPOT_FORMS_SCRIPT_SRC = "https://js.hsforms.net/forms/embed/v2.js"
|
|
8
|
+
// A form that hasn't rendered within this window is treated as failed.
|
|
9
|
+
const FORM_READY_TIMEOUT_MS = 12_000
|
|
10
|
+
|
|
11
|
+
let hubspotFormsScriptPromise: Promise<void> | null = null
|
|
12
|
+
|
|
13
|
+
type HubSpotFormCreateOptions = {
|
|
14
|
+
portalId: string
|
|
15
|
+
formId: string
|
|
16
|
+
region: string
|
|
17
|
+
target: string
|
|
18
|
+
cssRequired?: string
|
|
19
|
+
css?: string
|
|
20
|
+
onFormReady?: (form: HTMLFormElement) => void
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
declare global {
|
|
24
|
+
interface Window {
|
|
25
|
+
hbspt?: {
|
|
26
|
+
forms?: {
|
|
27
|
+
create: (options: HubSpotFormCreateOptions) => void
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const loadHubSpotFormsScript = () => {
|
|
34
|
+
if (typeof window === "undefined") return Promise.resolve()
|
|
35
|
+
if (window.hbspt?.forms) return Promise.resolve()
|
|
36
|
+
if (hubspotFormsScriptPromise) return hubspotFormsScriptPromise
|
|
37
|
+
|
|
38
|
+
hubspotFormsScriptPromise = new Promise((resolve, reject) => {
|
|
39
|
+
// Drop the dead tag on error so a later mount refetches instead of awaiting
|
|
40
|
+
// a `load` that already fired.
|
|
41
|
+
const fail = (script: HTMLScriptElement) => {
|
|
42
|
+
hubspotFormsScriptPromise = null
|
|
43
|
+
script.remove()
|
|
44
|
+
reject(new Error("Failed to load the HubSpot forms script."))
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// An existing tag means a load is in flight (a finished one set window.hbspt,
|
|
48
|
+
// caught above) — attach to its events.
|
|
49
|
+
const existing = document.querySelector<HTMLScriptElement>(
|
|
50
|
+
`script[src="${HUBSPOT_FORMS_SCRIPT_SRC}"]`
|
|
51
|
+
)
|
|
52
|
+
if (existing) {
|
|
53
|
+
existing.addEventListener("load", () => resolve(), { once: true })
|
|
54
|
+
existing.addEventListener("error", () => fail(existing), { once: true })
|
|
55
|
+
return
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const script = document.createElement("script")
|
|
59
|
+
script.src = HUBSPOT_FORMS_SCRIPT_SRC
|
|
60
|
+
script.async = true
|
|
61
|
+
script.charset = "utf-8"
|
|
62
|
+
script.onload = () => resolve()
|
|
63
|
+
script.onerror = () => fail(script)
|
|
64
|
+
document.head.appendChild(script)
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
return hubspotFormsScriptPromise
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
type HubSpotFormEmbedProps = {
|
|
71
|
+
portalId: string
|
|
72
|
+
formId: string
|
|
73
|
+
region: string
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export function HubSpotFormEmbed({
|
|
77
|
+
portalId,
|
|
78
|
+
formId,
|
|
79
|
+
region,
|
|
80
|
+
}: HubSpotFormEmbedProps) {
|
|
81
|
+
// Sanitize useId() (it can contain `:`) for use as a DOM id / selector.
|
|
82
|
+
const containerId = `hubspot-form-${useId().replace(/[^a-zA-Z0-9_-]/g, "")}`
|
|
83
|
+
const containerRef = useRef<HTMLDivElement | null>(null)
|
|
84
|
+
const readyTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null)
|
|
85
|
+
const unmountedRef = useRef(false)
|
|
86
|
+
const [hasError, setHasError] = useState(false)
|
|
87
|
+
const [isReady, setIsReady] = useState(false)
|
|
88
|
+
|
|
89
|
+
const createForm = useCallback(() => {
|
|
90
|
+
const target = containerRef.current
|
|
91
|
+
const formsApi = window.hbspt?.forms
|
|
92
|
+
if (!(target && formsApi)) {
|
|
93
|
+
setHasError(true)
|
|
94
|
+
return
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
setIsReady(false)
|
|
98
|
+
target.innerHTML = ""
|
|
99
|
+
|
|
100
|
+
// create() is fire-and-forget: a bad portal/region or archived form never
|
|
101
|
+
// calls onFormReady, so fall back to the error state on timeout.
|
|
102
|
+
if (readyTimeoutRef.current) clearTimeout(readyTimeoutRef.current)
|
|
103
|
+
readyTimeoutRef.current = setTimeout(() => {
|
|
104
|
+
if (unmountedRef.current) return
|
|
105
|
+
target.innerHTML = ""
|
|
106
|
+
setHasError(true)
|
|
107
|
+
}, FORM_READY_TIMEOUT_MS)
|
|
108
|
+
|
|
109
|
+
// cssRequired/css are emptied so HubSpot doesn't inject its own stylesheet —
|
|
110
|
+
// the form is styled by hubspot-form.css instead. Everything else (submit,
|
|
111
|
+
// validation, the post-submit thank-you message) is HubSpot's default.
|
|
112
|
+
formsApi.create({
|
|
113
|
+
portalId,
|
|
114
|
+
formId,
|
|
115
|
+
region,
|
|
116
|
+
target: `#${containerId}`,
|
|
117
|
+
cssRequired: "",
|
|
118
|
+
css: "",
|
|
119
|
+
onFormReady: () => {
|
|
120
|
+
if (unmountedRef.current) return
|
|
121
|
+
if (readyTimeoutRef.current) clearTimeout(readyTimeoutRef.current)
|
|
122
|
+
setIsReady(true)
|
|
123
|
+
setHasError(false)
|
|
124
|
+
},
|
|
125
|
+
})
|
|
126
|
+
}, [containerId, formId, portalId, region])
|
|
127
|
+
|
|
128
|
+
useEffect(() => {
|
|
129
|
+
unmountedRef.current = false
|
|
130
|
+
setHasError(false)
|
|
131
|
+
|
|
132
|
+
void loadHubSpotFormsScript()
|
|
133
|
+
.then(() => {
|
|
134
|
+
if (unmountedRef.current) return
|
|
135
|
+
createForm()
|
|
136
|
+
})
|
|
137
|
+
.catch((error: unknown) => {
|
|
138
|
+
if (unmountedRef.current) return
|
|
139
|
+
console.error("Failed to mount HubSpot form", error)
|
|
140
|
+
setHasError(true)
|
|
141
|
+
})
|
|
142
|
+
|
|
143
|
+
return () => {
|
|
144
|
+
unmountedRef.current = true
|
|
145
|
+
if (readyTimeoutRef.current) clearTimeout(readyTimeoutRef.current)
|
|
146
|
+
if (containerRef.current) containerRef.current.innerHTML = ""
|
|
147
|
+
}
|
|
148
|
+
}, [createForm])
|
|
149
|
+
|
|
150
|
+
const showSkeleton = !(isReady || hasError)
|
|
151
|
+
|
|
152
|
+
return (
|
|
153
|
+
<div className="hubspot-form-wrapper">
|
|
154
|
+
<div className="hubspot-form-embed" id={containerId} ref={containerRef} />
|
|
155
|
+
|
|
156
|
+
{showSkeleton ? <HubSpotFormSkeleton /> : null}
|
|
157
|
+
|
|
158
|
+
{hasError ? (
|
|
159
|
+
<p className="hubspot-form-error">
|
|
160
|
+
We couldn't load this form right now.
|
|
161
|
+
</p>
|
|
162
|
+
) : null}
|
|
163
|
+
</div>
|
|
164
|
+
)
|
|
165
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
export type HubSpotFormSelection = {
|
|
2
|
+
name: string
|
|
3
|
+
formId: string
|
|
4
|
+
portalId: string
|
|
5
|
+
region: string
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function decodeHubSpotFormSelection(
|
|
9
|
+
value?: string | null
|
|
10
|
+
): HubSpotFormSelection | null {
|
|
11
|
+
if (!value) return null
|
|
12
|
+
|
|
13
|
+
try {
|
|
14
|
+
const parsed: unknown = JSON.parse(value)
|
|
15
|
+
|
|
16
|
+
if (typeof parsed !== "object" || parsed === null) return null
|
|
17
|
+
|
|
18
|
+
const { name, formId, portalId, region } = parsed as Record<string, unknown>
|
|
19
|
+
|
|
20
|
+
if (
|
|
21
|
+
typeof name !== "string" ||
|
|
22
|
+
typeof formId !== "string" ||
|
|
23
|
+
typeof portalId !== "string" ||
|
|
24
|
+
typeof region !== "string"
|
|
25
|
+
) {
|
|
26
|
+
return null
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Fields that drive hbspt.forms.create must be non-empty (name is cosmetic).
|
|
30
|
+
if (!(formId.trim() && portalId.trim() && region.trim())) return null
|
|
31
|
+
|
|
32
|
+
return { name, formId, portalId, region }
|
|
33
|
+
} catch {
|
|
34
|
+
return null
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function getHubSpotFormSelectionLabel(selection: HubSpotFormSelection) {
|
|
39
|
+
return `${selection.name} (${selection.formId})`
|
|
40
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { HubSpotFormEmbed } from "./embed"
|
|
2
|
+
import "./hubspot-form.css"
|
|
3
|
+
|
|
4
|
+
type HubSpotFormProps = {
|
|
5
|
+
portalId: string
|
|
6
|
+
formId: string
|
|
7
|
+
region: string
|
|
8
|
+
className?: string | undefined
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Renders a HubSpot form, fully styled and decoupled from any CMS. Pass the
|
|
13
|
+
* portal/form/region directly, or decode them from a stored selection with
|
|
14
|
+
* `decodeHubSpotFormSelection`.
|
|
15
|
+
*/
|
|
16
|
+
export function HubSpotForm({
|
|
17
|
+
portalId,
|
|
18
|
+
formId,
|
|
19
|
+
region,
|
|
20
|
+
className,
|
|
21
|
+
}: HubSpotFormProps) {
|
|
22
|
+
const classes = ["hubspot-form-card", className].filter(Boolean).join(" ")
|
|
23
|
+
|
|
24
|
+
return (
|
|
25
|
+
<div className={classes}>
|
|
26
|
+
<HubSpotFormEmbed formId={formId} portalId={portalId} region={region} />
|
|
27
|
+
</div>
|
|
28
|
+
)
|
|
29
|
+
}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
"use server"
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
getHubSpotFormSelectionLabel,
|
|
5
|
+
type HubSpotFormSelection,
|
|
6
|
+
} from "./form-selection"
|
|
7
|
+
|
|
8
|
+
type HubSpotAccountDetailsResponse = {
|
|
9
|
+
portalId?: number | string
|
|
10
|
+
dataHostingLocation?: string
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
type HubSpotApiForm = {
|
|
14
|
+
id?: string
|
|
15
|
+
name?: string
|
|
16
|
+
archived?: boolean
|
|
17
|
+
displayOptions?: {
|
|
18
|
+
renderRawHtml?: boolean
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
type HubSpotFormsResponse = {
|
|
23
|
+
results?: HubSpotApiForm[]
|
|
24
|
+
paging?: { next?: { after?: string } }
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export type HubSpotFormOption = {
|
|
28
|
+
label: string
|
|
29
|
+
value: string
|
|
30
|
+
/** False when HubSpot will iframe-embed (Free portals); used only as a hint. */
|
|
31
|
+
renderRawHtml: boolean | null
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const HUBSPOT_API_BASE_URL = "https://api.hubapi.com"
|
|
35
|
+
|
|
36
|
+
async function fetchHubSpotJson<T>(path: string, accessToken: string) {
|
|
37
|
+
const response = await fetch(`${HUBSPOT_API_BASE_URL}${path}`, {
|
|
38
|
+
headers: {
|
|
39
|
+
Authorization: `Bearer ${accessToken}`,
|
|
40
|
+
Accept: "application/json",
|
|
41
|
+
},
|
|
42
|
+
cache: "no-store",
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
if (!response.ok) {
|
|
46
|
+
// Keep the full body for the server log, but throw a clean status so the
|
|
47
|
+
// caller-facing message never leaks HubSpot's raw response.
|
|
48
|
+
const body = await response.text()
|
|
49
|
+
console.error(
|
|
50
|
+
`HubSpot request failed (${response.status}) ${path}: ${body}`
|
|
51
|
+
)
|
|
52
|
+
throw new Error(`HubSpot request failed (${response.status}).`)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return (await response.json()) as T
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
async function fetchHubSpotForms(accessToken: string) {
|
|
59
|
+
const forms: HubSpotApiForm[] = []
|
|
60
|
+
let after: string | undefined
|
|
61
|
+
|
|
62
|
+
do {
|
|
63
|
+
const searchParams = new URLSearchParams({
|
|
64
|
+
formTypes: "hubspot",
|
|
65
|
+
limit: "100",
|
|
66
|
+
})
|
|
67
|
+
if (after) searchParams.set("after", after)
|
|
68
|
+
|
|
69
|
+
const response = await fetchHubSpotJson<HubSpotFormsResponse>(
|
|
70
|
+
`/marketing/v3/forms?${searchParams.toString()}`,
|
|
71
|
+
accessToken
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
forms.push(...(response.results ?? []))
|
|
75
|
+
after = response.paging?.next?.after
|
|
76
|
+
} while (after)
|
|
77
|
+
|
|
78
|
+
return forms
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Lists every published HubSpot form in the connected portal as a flat list of
|
|
83
|
+
* options. Each option's `value` is the JSON-encoded selection consumed by
|
|
84
|
+
* `decodeHubSpotFormSelection` and ultimately by `<HubSpotForm />`.
|
|
85
|
+
*
|
|
86
|
+
* Requires a private-app token in `HUBSPOT_ACCESS_TOKEN` with the `forms` and
|
|
87
|
+
* `account-info.security.read` scopes.
|
|
88
|
+
*/
|
|
89
|
+
export async function getHubSpotFormOptions(): Promise<{
|
|
90
|
+
forms: HubSpotFormOption[]
|
|
91
|
+
error: string | null
|
|
92
|
+
}> {
|
|
93
|
+
const accessToken = process.env.HUBSPOT_ACCESS_TOKEN
|
|
94
|
+
|
|
95
|
+
if (!accessToken) {
|
|
96
|
+
return { forms: [], error: "Missing HUBSPOT_ACCESS_TOKEN." }
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
try {
|
|
100
|
+
const [accountDetails, forms] = await Promise.all([
|
|
101
|
+
fetchHubSpotJson<HubSpotAccountDetailsResponse>(
|
|
102
|
+
"/account-info/v3/details",
|
|
103
|
+
accessToken
|
|
104
|
+
),
|
|
105
|
+
fetchHubSpotForms(accessToken),
|
|
106
|
+
])
|
|
107
|
+
|
|
108
|
+
const portalId = accountDetails.portalId
|
|
109
|
+
if (!portalId) {
|
|
110
|
+
return {
|
|
111
|
+
forms: [],
|
|
112
|
+
error: "HubSpot account details did not include a portal ID.",
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const region = accountDetails.dataHostingLocation || "na1"
|
|
117
|
+
|
|
118
|
+
const options = forms
|
|
119
|
+
.filter((form) => form.id && form.name && !form.archived)
|
|
120
|
+
.map<HubSpotFormOption>((form) => {
|
|
121
|
+
const selection: HubSpotFormSelection = {
|
|
122
|
+
name: form.name as string,
|
|
123
|
+
formId: form.id as string,
|
|
124
|
+
portalId: String(portalId),
|
|
125
|
+
region,
|
|
126
|
+
}
|
|
127
|
+
return {
|
|
128
|
+
label: getHubSpotFormSelectionLabel(selection),
|
|
129
|
+
value: JSON.stringify(selection),
|
|
130
|
+
renderRawHtml: form.displayOptions?.renderRawHtml ?? null,
|
|
131
|
+
}
|
|
132
|
+
})
|
|
133
|
+
.sort((a, b) => a.label.localeCompare(b.label))
|
|
134
|
+
|
|
135
|
+
return { forms: options, error: null }
|
|
136
|
+
} catch (error) {
|
|
137
|
+
return {
|
|
138
|
+
forms: [],
|
|
139
|
+
error: error instanceof Error ? error.message : "Unable to load forms.",
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|