@narrative.io/jsonforms-provider-protocols 1.0.3

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 (69) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +200 -0
  3. package/dist/_virtual/_plugin-vue_export-helper.js +11 -0
  4. package/dist/_virtual/_plugin-vue_export-helper.js.map +1 -0
  5. package/dist/core/cache.d.ts +8 -0
  6. package/dist/core/cache.d.ts.map +1 -0
  7. package/dist/core/cache.js +27 -0
  8. package/dist/core/cache.js.map +1 -0
  9. package/dist/core/jsonpath.d.ts +2 -0
  10. package/dist/core/jsonpath.d.ts.map +1 -0
  11. package/dist/core/jsonpath.js +40 -0
  12. package/dist/core/jsonpath.js.map +1 -0
  13. package/dist/core/registry.d.ts +8 -0
  14. package/dist/core/registry.d.ts.map +1 -0
  15. package/dist/core/registry.js +17 -0
  16. package/dist/core/registry.js.map +1 -0
  17. package/dist/core/templating.d.ts +3 -0
  18. package/dist/core/templating.d.ts.map +1 -0
  19. package/dist/core/templating.js +32 -0
  20. package/dist/core/templating.js.map +1 -0
  21. package/dist/core/types.d.ts +44 -0
  22. package/dist/core/types.d.ts.map +1 -0
  23. package/dist/index.d.ts +18 -0
  24. package/dist/index.d.ts.map +1 -0
  25. package/dist/index.js +30 -0
  26. package/dist/index.js.map +1 -0
  27. package/dist/jsonforms-provider-protocols.css +4 -0
  28. package/dist/protocols/rest_api.d.ts +22 -0
  29. package/dist/protocols/rest_api.d.ts.map +1 -0
  30. package/dist/protocols/rest_api.js +92 -0
  31. package/dist/protocols/rest_api.js.map +1 -0
  32. package/dist/vue/components/ProviderAutocomplete.vue.d.ts +9 -0
  33. package/dist/vue/components/ProviderAutocomplete.vue.d.ts.map +1 -0
  34. package/dist/vue/components/ProviderAutocomplete.vue.js +68 -0
  35. package/dist/vue/components/ProviderAutocomplete.vue.js.map +1 -0
  36. package/dist/vue/components/ProviderAutocomplete.vue2.js +5 -0
  37. package/dist/vue/components/ProviderAutocomplete.vue2.js.map +1 -0
  38. package/dist/vue/components/ProviderSelect.vue.d.ts +9 -0
  39. package/dist/vue/components/ProviderSelect.vue.d.ts.map +1 -0
  40. package/dist/vue/components/ProviderSelect.vue.js +8 -0
  41. package/dist/vue/components/ProviderSelect.vue.js.map +1 -0
  42. package/dist/vue/components/ProviderSelect.vue2.js +74 -0
  43. package/dist/vue/components/ProviderSelect.vue2.js.map +1 -0
  44. package/dist/vue/composables/useProvider.d.ts +14 -0
  45. package/dist/vue/composables/useProvider.d.ts.map +1 -0
  46. package/dist/vue/composables/useProvider.js +76 -0
  47. package/dist/vue/composables/useProvider.js.map +1 -0
  48. package/dist/vue/index.d.ts +19 -0
  49. package/dist/vue/index.d.ts.map +1 -0
  50. package/dist/vue/index.js +36 -0
  51. package/dist/vue/index.js.map +1 -0
  52. package/dist/vue/testers.d.ts +2 -0
  53. package/dist/vue/testers.d.ts.map +1 -0
  54. package/dist/vue/testers.js +12 -0
  55. package/dist/vue/testers.js.map +1 -0
  56. package/package.json +100 -0
  57. package/src/core/cache.ts +23 -0
  58. package/src/core/jsonpath.ts +41 -0
  59. package/src/core/registry.ts +15 -0
  60. package/src/core/templating.ts +27 -0
  61. package/src/core/types.ts +46 -0
  62. package/src/index.ts +36 -0
  63. package/src/protocols/rest_api.ts +136 -0
  64. package/src/vue/components/ProviderAutocomplete.vue +54 -0
  65. package/src/vue/components/ProviderSelect.vue +70 -0
  66. package/src/vue/composables/useProvider.ts +111 -0
  67. package/src/vue/index.ts +44 -0
  68. package/src/vue/testers.ts +19 -0
  69. package/src/vue-shim.d.ts +9 -0
@@ -0,0 +1,27 @@
1
+ export function renderTpl(str: string, ctx: unknown): string {
2
+ return str.replace(/\{\{\s*([^}]+)\s*\}\}/g, (_, path) => {
3
+ const trimmedPath = path.trim();
4
+ return String(
5
+ trimmedPath.split(".").reduce((a: unknown, k: string) => {
6
+ if (a && typeof a === "object" && k in a) {
7
+ return (a as Record<string, unknown>)[k];
8
+ }
9
+ return undefined;
10
+ }, ctx) ?? "",
11
+ );
12
+ });
13
+ }
14
+ export function renderObj<T = unknown>(obj: unknown, ctx: unknown): T {
15
+ if (!obj || typeof obj !== "object") return obj as T;
16
+ const out: Record<string, unknown> | unknown[] = Array.isArray(obj) ? [] : {};
17
+ for (const [k, v] of Object.entries(obj)) {
18
+ if (typeof v === "string") {
19
+ (out as Record<string, unknown>)[k] = renderTpl(v, ctx);
20
+ } else if (v && typeof v === "object") {
21
+ (out as Record<string, unknown>)[k] = renderObj(v, ctx);
22
+ } else {
23
+ (out as Record<string, unknown>)[k] = v;
24
+ }
25
+ }
26
+ return out as T;
27
+ }
@@ -0,0 +1,46 @@
1
+ export type ProviderItem = {
2
+ label: string;
3
+ value: unknown;
4
+ meta?: Record<string, unknown>;
5
+ };
6
+ export type ProviderOutput = {
7
+ items: ProviderItem[];
8
+ ttl?: number;
9
+ etag?: string;
10
+ };
11
+
12
+ export type ProviderContext = {
13
+ data: unknown;
14
+ path: string;
15
+ signal: AbortSignal;
16
+ ui?: { query?: string };
17
+ auth?: Record<string, unknown>; // Global auth registry, not AuthConfig
18
+ };
19
+
20
+ export interface Protocol<Cfg = unknown> {
21
+ protocol: string; // 'rest_api' | 'table' | 'cookie' | ...
22
+ resolve(cfg: Cfg, ctx: ProviderContext): Promise<ProviderOutput>;
23
+ }
24
+
25
+ export interface ProviderBinding {
26
+ ref: string;
27
+ protocol: string;
28
+ config?: unknown;
29
+ auth?: AuthConfig;
30
+ cacheTTL?: number;
31
+ load?: "mount" | "onFocus" | "query";
32
+ }
33
+
34
+ export type Mapping = {
35
+ label: string;
36
+ value: string;
37
+ meta?: Record<string, string>;
38
+ };
39
+
40
+ export interface AuthConfig {
41
+ use?: string; // Reference global auth key
42
+ apiKey?: string | (() => string); // API key auth
43
+ bearer?: string | (() => string); // Bearer token auth
44
+ token?: string | (() => string); // Generic token auth
45
+ [key: string]: unknown; // Custom auth fields
46
+ }
package/src/index.ts ADDED
@@ -0,0 +1,36 @@
1
+ import type { App } from "vue";
2
+ import { cache as globalCache } from "./core/cache";
3
+ import { registry as globalRegistry } from "./core/registry";
4
+ import type { Protocol, AuthConfig } from "./core/types";
5
+
6
+ export { cache } from "./core/cache";
7
+ export * from "./core/jsonpath";
8
+ export { registry } from "./core/registry";
9
+ export * from "./core/templating";
10
+ // Core exports
11
+ export * from "./core/types";
12
+
13
+ // Protocol exports
14
+ export { RestApiProtocol } from "./protocols/rest_api";
15
+
16
+ // Vue exports
17
+ export * as vue from "./vue";
18
+
19
+ export interface ProviderConfig {
20
+ protocols?: Protocol[];
21
+ auth?: AuthConfig;
22
+ }
23
+
24
+ export default {
25
+ install(app: App, opts?: ProviderConfig) {
26
+ const reg = globalRegistry;
27
+ if (opts?.protocols) {
28
+ for (const p of opts.protocols) {
29
+ reg.register(p);
30
+ }
31
+ }
32
+ app.provide("providerRegistry", reg);
33
+ app.provide("providerCache", globalCache);
34
+ app.provide("providerAuth", opts?.auth ?? {});
35
+ },
36
+ };
@@ -0,0 +1,136 @@
1
+ import { jp } from "../core/jsonpath";
2
+ import { renderObj, renderTpl } from "../core/templating";
3
+ import type {
4
+ Protocol,
5
+ ProviderItem,
6
+ ProviderOutput,
7
+ AuthConfig,
8
+ } from "../core/types";
9
+
10
+ export type RestApiCfg = {
11
+ url: string;
12
+ method?: "GET" | "POST";
13
+ headers?: Record<string, string>;
14
+ query?: Record<string, unknown>;
15
+ body?: unknown;
16
+ items: string; // JSONPath to array; e.g. "$.items[*]"
17
+ map: { label: string; value: string; meta?: Record<string, string> }; // relative to each item
18
+ paginate?: { cursorPath: string; param: string; maxPages?: number };
19
+ auth?: AuthConfig;
20
+ };
21
+
22
+ function buildAuthHeaders(
23
+ auth?: AuthConfig,
24
+ globalAuth?: Record<string, unknown>,
25
+ ): Record<string, string> {
26
+ const headers: Record<string, string> = {};
27
+
28
+ if (!auth) return headers;
29
+
30
+ // Handle "use" reference to global auth
31
+ if (auth.use && globalAuth?.[auth.use]) {
32
+ const globalValue = globalAuth[auth.use];
33
+ const value =
34
+ typeof globalValue === "function" ? globalValue() : globalValue;
35
+
36
+ if (auth.use === "apiKey") {
37
+ headers["X-API-Key"] = String(value);
38
+ } else if (auth.use === "bearer") {
39
+ headers["Authorization"] = `Bearer ${value}`;
40
+ } else if (auth.use === "token") {
41
+ headers["Authorization"] = `Token ${value}`;
42
+ }
43
+ return headers;
44
+ }
45
+
46
+ // Handle direct auth values
47
+ if (auth.apiKey) {
48
+ const value =
49
+ typeof auth.apiKey === "function" ? auth.apiKey() : auth.apiKey;
50
+ headers["X-API-Key"] = String(value);
51
+ }
52
+
53
+ if (auth.bearer) {
54
+ const value =
55
+ typeof auth.bearer === "function" ? auth.bearer() : auth.bearer;
56
+ headers["Authorization"] = `Bearer ${value}`;
57
+ }
58
+
59
+ if (auth.token) {
60
+ const value = typeof auth.token === "function" ? auth.token() : auth.token;
61
+ headers["Authorization"] = `Token ${value}`;
62
+ }
63
+
64
+ // Handle custom auth fields
65
+ for (const [key, value] of Object.entries(auth)) {
66
+ if (
67
+ !["use", "apiKey", "bearer", "token"].includes(key) &&
68
+ value !== undefined
69
+ ) {
70
+ const authValue = typeof value === "function" ? value() : value;
71
+ headers[key] = String(authValue);
72
+ }
73
+ }
74
+
75
+ return headers;
76
+ }
77
+
78
+ export const RestApiProtocol = (): Protocol<RestApiCfg> => ({
79
+ protocol: "rest_api",
80
+ async resolve(cfg, ctx): Promise<ProviderOutput> {
81
+ const ac = ctx.signal;
82
+ const out: ProviderItem[] = [];
83
+ let cursor: unknown = null;
84
+ let page = 0;
85
+ do {
86
+ const url = new URL(renderTpl(cfg.url, { data: ctx.data, ui: ctx.ui }));
87
+ const q = renderObj(cfg.query ?? {}, {
88
+ data: ctx.data,
89
+ ui: ctx.ui,
90
+ }) as Record<string, unknown>;
91
+ for (const [k, v] of Object.entries(q))
92
+ if (v !== undefined && v !== "") url.searchParams.set(k, String(v));
93
+ if (cursor && cfg.paginate)
94
+ url.searchParams.set(cfg.paginate.param, String(cursor));
95
+
96
+ // Build headers with auth
97
+ const baseHeaders = renderObj(cfg.headers ?? {}, {
98
+ data: ctx.data,
99
+ }) as Record<string, string>;
100
+ const authHeaders = buildAuthHeaders(cfg.auth, ctx.auth);
101
+ const headers = { ...baseHeaders, ...authHeaders };
102
+
103
+ const method = cfg.method ?? "GET";
104
+ const requestInit: RequestInit = {
105
+ method,
106
+ headers,
107
+ signal: ac,
108
+ };
109
+
110
+ // Only include body for non-GET requests
111
+ if (method !== "GET" && cfg.body) {
112
+ requestInit.body = JSON.stringify(
113
+ renderObj(cfg.body, { data: ctx.data }),
114
+ );
115
+ }
116
+
117
+ const res = await fetch(url.toString(), requestInit);
118
+ if (!res.ok) throw new Error(`REST ${res.status}`);
119
+ const json = await res.json();
120
+ const items = jp(json, cfg.items);
121
+ for (const it of items) {
122
+ const label = jp(it, cfg.map.label)[0];
123
+ const value = jp(it, cfg.map.value)[0];
124
+ const meta = cfg.map.meta
125
+ ? Object.fromEntries(
126
+ Object.entries(cfg.map.meta).map(([k, p]) => [k, jp(it, p)[0]]),
127
+ )
128
+ : undefined;
129
+ out.push({ label: String(label ?? ""), value, meta });
130
+ }
131
+ cursor = cfg.paginate ? jp(json, cfg.paginate.cursorPath)[0] : null;
132
+ page += 1;
133
+ } while (cfg.paginate && cursor && page < (cfg.paginate.maxPages ?? 5));
134
+ return { items: out, ttl: 300 };
135
+ },
136
+ });
@@ -0,0 +1,54 @@
1
+ <script setup lang="ts">
2
+ import type { ControlElement, JsonSchema } from "@jsonforms/core";
3
+ import { useJsonFormsControl } from "@jsonforms/vue";
4
+ import { computed, ref, watch } from "vue";
5
+ import { useProvider } from "../composables/useProvider";
6
+
7
+ const props = defineProps<{
8
+ uischema: ControlElement;
9
+ schema: JsonSchema;
10
+ path: string;
11
+ }>();
12
+ const { control, handleChange } = useJsonFormsControl(props);
13
+
14
+ const binding = computed(() => {
15
+ const provider = control.value.uischema?.options?.provider;
16
+ // Ensure load property is set to 'query' by default for autocomplete
17
+ if (provider && typeof provider === "object" && !provider.load) {
18
+ return { ...provider, load: "query" };
19
+ }
20
+ return provider;
21
+ });
22
+ const query = ref("");
23
+ const { items, loading, error, reload } = useProvider(binding, {
24
+ data: control.value.data,
25
+ path: control.value.path,
26
+ uiQuery: query.value,
27
+ });
28
+
29
+ watch(query, () => {
30
+ if (binding.value?.load === "query") reload();
31
+ });
32
+
33
+ const value = computed({
34
+ get: () => control.value.data,
35
+ set: (v) => handleChange(control.value.path, v),
36
+ });
37
+ </script>
38
+
39
+ <template>
40
+ <div class="provider-autocomplete">
41
+ <label>{{ control.schema.title }}</label>
42
+ <input
43
+ v-model="query"
44
+ :placeholder="loading ? 'Loading…' : 'Type to search…'"
45
+ type="text"
46
+ />
47
+ <ul v-if="items.length">
48
+ <li v-for="it in items" :key="String(it.value)" @click="value = it.value">
49
+ {{ it.label }}
50
+ </li>
51
+ </ul>
52
+ <small v-if="error" role="alert">Failed: {{ error }}</small>
53
+ </div>
54
+ </template>
@@ -0,0 +1,70 @@
1
+ <script setup lang="ts">
2
+ import type { ControlElement, JsonSchema } from "@jsonforms/core";
3
+ import { useJsonFormsControl } from "@jsonforms/vue";
4
+ import { computed, watch } from "vue";
5
+ import { useProvider } from "../composables/useProvider";
6
+
7
+ const props = defineProps<{
8
+ uischema: ControlElement;
9
+ schema: JsonSchema;
10
+ path: string;
11
+ }>();
12
+ const { control, handleChange } = useJsonFormsControl(props);
13
+
14
+ const binding = computed(() => {
15
+ const provider = control.value.uischema?.options?.provider;
16
+ // Ensure load property is set to 'mount' by default
17
+ if (provider && typeof provider === "object" && !provider.load) {
18
+ return { ...provider, load: "mount" };
19
+ }
20
+ return provider;
21
+ });
22
+
23
+ const deps = computed(
24
+ () =>
25
+ ((
26
+ (control.value.schema as Record<string, unknown>)?.[
27
+ "x-provider"
28
+ ] as Record<string, unknown>
29
+ )?.dependsOn as string[]) ?? [],
30
+ );
31
+ const depValues = computed(() => deps.value.map(() => null)); // you can resolve actual values via control.value.data & pointers
32
+
33
+ const { items, loading, error, reload } = useProvider(binding, {
34
+ data: control.value.data,
35
+ path: control.value.path,
36
+ dependsOnValues: depValues.value,
37
+ });
38
+
39
+ watch(
40
+ () => control.value.data,
41
+ () => {
42
+ // if dependsOn changed → reload
43
+ reload();
44
+ },
45
+ );
46
+
47
+ const value = computed({
48
+ get: () => control.value.data,
49
+ set: (v) => handleChange(control.value.path, v),
50
+ });
51
+ </script>
52
+
53
+ <template>
54
+ <div class="provider-select">
55
+ <label>{{ control.schema.title }}</label>
56
+ <select v-model="value">
57
+ <option value="" disabled>{{ loading ? "Loading…" : "Select…" }}</option>
58
+ <option v-for="it in items" :key="String(it.value)" :value="it.value">
59
+ {{ it.label }}
60
+ </option>
61
+ </select>
62
+ <small v-if="error" role="alert">Failed to load: {{ error }}</small>
63
+ </div>
64
+ </template>
65
+
66
+ <style scoped>
67
+ .provider-select select {
68
+ min-width: 16rem;
69
+ }
70
+ </style>
@@ -0,0 +1,111 @@
1
+ import {
2
+ type ComputedRef,
3
+ computed,
4
+ inject,
5
+ onBeforeUnmount,
6
+ type Ref,
7
+ ref,
8
+ unref,
9
+ watch,
10
+ } from "vue";
11
+ import { cache as globalCache } from "../../core/cache";
12
+ import { registry as globalRegistry } from "../../core/registry";
13
+ import type {
14
+ AuthConfig,
15
+ ProviderBinding,
16
+ ProviderItem,
17
+ ProviderOutput,
18
+ } from "../../core/types";
19
+
20
+ export function useProvider(
21
+ binding:
22
+ | ProviderBinding
23
+ | Ref<ProviderBinding>
24
+ | ComputedRef<ProviderBinding>,
25
+ ctxBits: {
26
+ data: unknown;
27
+ path: string;
28
+ dependsOnValues?: unknown[];
29
+ uiQuery?: string;
30
+ },
31
+ ) {
32
+ const registry = inject("providerRegistry", globalRegistry);
33
+ const cache = inject("providerCache", globalCache);
34
+ const auth = inject("providerAuth", {}) as Record<
35
+ string,
36
+ (() => string) | string
37
+ >;
38
+
39
+ const items = ref<ProviderItem[]>([]);
40
+ const loading = ref(false);
41
+ const error = ref<string | undefined>(undefined);
42
+ const ac = new AbortController();
43
+
44
+ const cacheKey = computed(() =>
45
+ JSON.stringify({
46
+ b: unref(binding),
47
+ d: ctxBits.dependsOnValues ?? [],
48
+ q: ctxBits.uiQuery ?? "",
49
+ }),
50
+ );
51
+
52
+ async function load() {
53
+ const bindingValue = unref(binding);
54
+ if (!bindingValue) return;
55
+ loading.value = true;
56
+ error.value = undefined;
57
+ const hit = cache.get(cacheKey.value) as ProviderOutput | undefined;
58
+ if (hit) {
59
+ items.value = hit.items;
60
+ loading.value = false;
61
+ return;
62
+ }
63
+ try {
64
+ const driver = registry.get(bindingValue.protocol);
65
+ if (!driver) {
66
+ throw new Error(
67
+ `No provider registered for protocol: ${bindingValue.protocol}`,
68
+ );
69
+ }
70
+ const out = await driver.resolve(bindingValue.config ?? {}, {
71
+ data: ctxBits.data,
72
+ path: ctxBits.path,
73
+ ui: { query: ctxBits.uiQuery },
74
+ signal: ac.signal,
75
+ auth: resolveAuth(bindingValue.auth, auth),
76
+ });
77
+ items.value = out.items;
78
+ cache.set(cacheKey.value, out, bindingValue.cacheTTL ?? out.ttl ?? 0);
79
+ } catch (e) {
80
+ error.value = (e as Error)?.message ?? String(e);
81
+ items.value = [];
82
+ } finally {
83
+ loading.value = false;
84
+ }
85
+ }
86
+
87
+ onBeforeUnmount(() => ac.abort());
88
+ watch(
89
+ [cacheKey],
90
+ () => {
91
+ const bindingValue = unref(binding);
92
+ if (bindingValue?.load === "mount" || bindingValue?.load === "query")
93
+ load();
94
+ },
95
+ { immediate: unref(binding)?.load === "mount" },
96
+ );
97
+
98
+ return { items, loading, error, reload: load };
99
+ }
100
+
101
+ function resolveAuth(
102
+ spec: AuthConfig | undefined,
103
+ authBag: Record<string, (() => string) | string>,
104
+ ): AuthConfig {
105
+ if (!spec) return {};
106
+ if (spec.use && typeof authBag[spec.use] === "function") {
107
+ const authFunc = authBag[spec.use] as () => string;
108
+ return { [spec.use]: authFunc() };
109
+ }
110
+ return spec;
111
+ }
@@ -0,0 +1,44 @@
1
+ import type { UISchemaElement } from "@jsonforms/core";
2
+ import {
3
+ and,
4
+ isIntegerControl,
5
+ isNumberControl,
6
+ isStringControl,
7
+ or,
8
+ rankWith,
9
+ } from "@jsonforms/core";
10
+ import ProviderAutocomplete from "./components/ProviderAutocomplete.vue";
11
+ import ProviderSelect from "./components/ProviderSelect.vue";
12
+
13
+ // Custom tester that checks if provider option exists (as object or boolean)
14
+ const hasProvider = (uischema: UISchemaElement) => {
15
+ return uischema?.options?.provider !== undefined;
16
+ };
17
+
18
+ // Create specific testers for each component type
19
+ const providerSelectTester = rankWith(
20
+ 6,
21
+ and(
22
+ or(isStringControl, isNumberControl, isIntegerControl),
23
+ hasProvider,
24
+ (uischema) => !uischema?.options?.autocomplete,
25
+ ),
26
+ );
27
+
28
+ const providerAutocompleteTester = rankWith(
29
+ 7,
30
+ and(
31
+ or(isStringControl, isNumberControl, isIntegerControl),
32
+ hasProvider,
33
+ (uischema) => uischema?.options?.autocomplete === true,
34
+ ),
35
+ );
36
+
37
+ export const providerRenderers = [
38
+ { tester: providerSelectTester, renderer: ProviderSelect },
39
+ { tester: providerAutocompleteTester, renderer: ProviderAutocomplete },
40
+ ];
41
+
42
+ export { ProviderAutocomplete, ProviderSelect };
43
+ export { useProvider } from "./composables/useProvider";
44
+ export * from "./testers";
@@ -0,0 +1,19 @@
1
+ import {
2
+ and,
3
+ isIntegerControl,
4
+ isNumberControl,
5
+ isStringControl,
6
+ or,
7
+ rankWith,
8
+ } from "@jsonforms/core";
9
+
10
+ // Tester that checks if provider option exists (as object or boolean)
11
+ export const providerTester = rankWith(
12
+ 5,
13
+ and(
14
+ or(isStringControl, isNumberControl, isIntegerControl),
15
+ (uischema: import("@jsonforms/core").UISchemaElement) =>
16
+ uischema?.options?.provider !== undefined &&
17
+ uischema?.options?.provider !== null,
18
+ ),
19
+ );
@@ -0,0 +1,9 @@
1
+ declare module "*.vue" {
2
+ import type { DefineComponent } from "vue";
3
+ const component: DefineComponent<
4
+ Record<string, unknown>,
5
+ Record<string, unknown>,
6
+ unknown
7
+ >;
8
+ export default component;
9
+ }