@narrative.io/jsonforms-provider-protocols 1.2.0-beta.0 → 1.2.0-beta.1
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/core/transforms.d.ts +29 -0
- package/dist/core/transforms.d.ts.map +1 -0
- package/dist/core/transforms.js +58 -0
- package/dist/core/transforms.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/protocols/rest_api.d.ts +3 -2
- package/dist/protocols/rest_api.d.ts.map +1 -1
- package/dist/protocols/rest_api.js +14 -13
- package/dist/protocols/rest_api.js.map +1 -1
- package/package.json +1 -1
- package/src/core/transforms.ts +107 -0
- package/src/index.ts +1 -0
- package/src/protocols/rest_api.ts +28 -18
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Transform pipeline system for manipulating API response data
|
|
3
|
+
* Transforms are applied sequentially in the order they appear in the pipeline
|
|
4
|
+
*/
|
|
5
|
+
export interface Transform {
|
|
6
|
+
name: string;
|
|
7
|
+
[key: string]: unknown;
|
|
8
|
+
}
|
|
9
|
+
export interface FlattenTransform extends Transform {
|
|
10
|
+
name: "flatten";
|
|
11
|
+
key: string;
|
|
12
|
+
labelFormat?: string;
|
|
13
|
+
}
|
|
14
|
+
export type TransformStep = FlattenTransform;
|
|
15
|
+
export type TransformPipeline = TransformStep[];
|
|
16
|
+
/**
|
|
17
|
+
* Registry of transform functions
|
|
18
|
+
*/
|
|
19
|
+
type TransformFunction = (items: unknown[], config: Transform) => unknown[];
|
|
20
|
+
/**
|
|
21
|
+
* Register a transform function
|
|
22
|
+
*/
|
|
23
|
+
export declare function registerTransform(name: string, fn: TransformFunction): void;
|
|
24
|
+
/**
|
|
25
|
+
* Apply a pipeline of transforms to data
|
|
26
|
+
*/
|
|
27
|
+
export declare function applyTransformPipeline(items: unknown[], pipeline: TransformPipeline): unknown[];
|
|
28
|
+
export {};
|
|
29
|
+
//# sourceMappingURL=transforms.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transforms.d.ts","sourceRoot":"","sources":["../../src/core/transforms.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,gBAAiB,SAAQ,SAAS;IACjD,IAAI,EAAE,SAAS,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;IACZ,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,MAAM,aAAa,GAAG,gBAAgB,CAAC;AAE7C,MAAM,MAAM,iBAAiB,GAAG,aAAa,EAAE,CAAC;AAEhD;;GAEG;AACH,KAAK,iBAAiB,GAAG,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE,MAAM,EAAE,SAAS,KAAK,OAAO,EAAE,CAAC;AAI5E;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,iBAAiB,GAAG,IAAI,CAE3E;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,OAAO,EAAE,EAChB,QAAQ,EAAE,iBAAiB,GAC1B,OAAO,EAAE,CAYX"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
const transformRegistry = {};
|
|
2
|
+
function registerTransform(name, fn) {
|
|
3
|
+
transformRegistry[name] = fn;
|
|
4
|
+
}
|
|
5
|
+
function applyTransformPipeline(items, pipeline) {
|
|
6
|
+
let result = items;
|
|
7
|
+
for (const transform of pipeline) {
|
|
8
|
+
const fn = transformRegistry[transform.name];
|
|
9
|
+
if (!fn) {
|
|
10
|
+
throw new Error(`Unknown transform: ${transform.name}`);
|
|
11
|
+
}
|
|
12
|
+
result = fn(result, transform);
|
|
13
|
+
}
|
|
14
|
+
return result;
|
|
15
|
+
}
|
|
16
|
+
function flattenTransform(items, config) {
|
|
17
|
+
const flattenConfig = config;
|
|
18
|
+
const { key, labelFormat } = flattenConfig;
|
|
19
|
+
const flattened = [];
|
|
20
|
+
for (const item of items) {
|
|
21
|
+
if (typeof item !== "object" || item === null) continue;
|
|
22
|
+
const itemObj = item;
|
|
23
|
+
const children = itemObj[key];
|
|
24
|
+
if (Array.isArray(children)) {
|
|
25
|
+
for (const child of children) {
|
|
26
|
+
if (typeof child !== "object" || child === null) continue;
|
|
27
|
+
const childObj = child;
|
|
28
|
+
if (labelFormat) {
|
|
29
|
+
const formattedChild = { ...childObj };
|
|
30
|
+
let formattedLabel = labelFormat;
|
|
31
|
+
formattedLabel = formattedLabel.replace(
|
|
32
|
+
/\{parent\.(\w+)\}/g,
|
|
33
|
+
(_, prop) => String(itemObj[prop] ?? "")
|
|
34
|
+
);
|
|
35
|
+
formattedLabel = formattedLabel.replace(
|
|
36
|
+
/\{(\w+)\}/g,
|
|
37
|
+
(_, prop) => String(childObj[prop] ?? "")
|
|
38
|
+
);
|
|
39
|
+
formattedChild._formattedLabel = formattedLabel;
|
|
40
|
+
formattedChild._parent = itemObj;
|
|
41
|
+
flattened.push(formattedChild);
|
|
42
|
+
} else {
|
|
43
|
+
flattened.push({
|
|
44
|
+
...childObj,
|
|
45
|
+
_parent: itemObj
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return flattened;
|
|
52
|
+
}
|
|
53
|
+
registerTransform("flatten", flattenTransform);
|
|
54
|
+
export {
|
|
55
|
+
applyTransformPipeline,
|
|
56
|
+
registerTransform
|
|
57
|
+
};
|
|
58
|
+
//# sourceMappingURL=transforms.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"transforms.js","sources":["../../src/core/transforms.ts"],"sourcesContent":["/**\n * Transform pipeline system for manipulating API response data\n * Transforms are applied sequentially in the order they appear in the pipeline\n */\n\nexport interface Transform {\n name: string;\n [key: string]: unknown;\n}\n\nexport interface FlattenTransform extends Transform {\n name: \"flatten\";\n key: string; // The key containing the nested array to flatten\n labelFormat?: string; // Optional format string like \"{parent.name} → {name}\"\n}\n\nexport type TransformStep = FlattenTransform;\n\nexport type TransformPipeline = TransformStep[];\n\n/**\n * Registry of transform functions\n */\ntype TransformFunction = (items: unknown[], config: Transform) => unknown[];\n\nconst transformRegistry: Record<string, TransformFunction> = {};\n\n/**\n * Register a transform function\n */\nexport function registerTransform(name: string, fn: TransformFunction): void {\n transformRegistry[name] = fn;\n}\n\n/**\n * Apply a pipeline of transforms to data\n */\nexport function applyTransformPipeline(\n items: unknown[],\n pipeline: TransformPipeline,\n): unknown[] {\n let result = items;\n\n for (const transform of pipeline) {\n const fn = transformRegistry[transform.name];\n if (!fn) {\n throw new Error(`Unknown transform: ${transform.name}`);\n }\n result = fn(result, transform);\n }\n\n return result;\n}\n\n/**\n * Flatten transform - flattens nested arrays into a single level\n */\nfunction flattenTransform(items: unknown[], config: Transform): unknown[] {\n const flattenConfig = config as FlattenTransform;\n const { key, labelFormat } = flattenConfig;\n const flattened: unknown[] = [];\n\n for (const item of items) {\n if (typeof item !== \"object\" || item === null) continue;\n\n const itemObj = item as Record<string, unknown>;\n const children = itemObj[key];\n\n if (Array.isArray(children)) {\n for (const child of children) {\n if (typeof child !== \"object\" || child === null) continue;\n\n const childObj = child as Record<string, unknown>;\n\n // If labelFormat is provided, use it to format the label\n if (labelFormat) {\n const formattedChild = { ...childObj };\n\n // Replace placeholders like {parent.name} and {name}\n let formattedLabel = labelFormat;\n formattedLabel = formattedLabel.replace(\n /\\{parent\\.(\\w+)\\}/g,\n (_, prop) => String(itemObj[prop] ?? \"\"),\n );\n formattedLabel = formattedLabel.replace(/\\{(\\w+)\\}/g, (_, prop) =>\n String(childObj[prop] ?? \"\"),\n );\n\n formattedChild._formattedLabel = formattedLabel;\n formattedChild._parent = itemObj;\n flattened.push(formattedChild);\n } else {\n // Just add parent reference\n flattened.push({\n ...childObj,\n _parent: itemObj,\n });\n }\n }\n }\n }\n\n return flattened;\n}\n\n// Register built-in transforms\nregisterTransform(\"flatten\", flattenTransform);\n"],"names":[],"mappings":"AAyBA,MAAM,oBAAuD,CAAA;AAKtD,SAAS,kBAAkB,MAAc,IAA6B;AAC3E,oBAAkB,IAAI,IAAI;AAC5B;AAKO,SAAS,uBACd,OACA,UACW;AACX,MAAI,SAAS;AAEb,aAAW,aAAa,UAAU;AAChC,UAAM,KAAK,kBAAkB,UAAU,IAAI;AAC3C,QAAI,CAAC,IAAI;AACP,YAAM,IAAI,MAAM,sBAAsB,UAAU,IAAI,EAAE;AAAA,IACxD;AACA,aAAS,GAAG,QAAQ,SAAS;AAAA,EAC/B;AAEA,SAAO;AACT;AAKA,SAAS,iBAAiB,OAAkB,QAA8B;AACxE,QAAM,gBAAgB;AACtB,QAAM,EAAE,KAAK,YAAA,IAAgB;AAC7B,QAAM,YAAuB,CAAA;AAE7B,aAAW,QAAQ,OAAO;AACxB,QAAI,OAAO,SAAS,YAAY,SAAS,KAAM;AAE/C,UAAM,UAAU;AAChB,UAAM,WAAW,QAAQ,GAAG;AAE5B,QAAI,MAAM,QAAQ,QAAQ,GAAG;AAC3B,iBAAW,SAAS,UAAU;AAC5B,YAAI,OAAO,UAAU,YAAY,UAAU,KAAM;AAEjD,cAAM,WAAW;AAGjB,YAAI,aAAa;AACf,gBAAM,iBAAiB,EAAE,GAAG,SAAA;AAG5B,cAAI,iBAAiB;AACrB,2BAAiB,eAAe;AAAA,YAC9B;AAAA,YACA,CAAC,GAAG,SAAS,OAAO,QAAQ,IAAI,KAAK,EAAE;AAAA,UAAA;AAEzC,2BAAiB,eAAe;AAAA,YAAQ;AAAA,YAAc,CAAC,GAAG,SACxD,OAAO,SAAS,IAAI,KAAK,EAAE;AAAA,UAAA;AAG7B,yBAAe,kBAAkB;AACjC,yBAAe,UAAU;AACzB,oBAAU,KAAK,cAAc;AAAA,QAC/B,OAAO;AAEL,oBAAU,KAAK;AAAA,YACb,GAAG;AAAA,YACH,SAAS;AAAA,UAAA,CACV;AAAA,QACH;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;AAGA,kBAAkB,WAAW,gBAAgB;"}
|
package/dist/index.d.ts
CHANGED
|
@@ -4,6 +4,7 @@ export { cache } from "./core/cache";
|
|
|
4
4
|
export * from "./core/jsonpath";
|
|
5
5
|
export { registry } from "./core/registry";
|
|
6
6
|
export * from "./core/templating";
|
|
7
|
+
export * from "./core/transforms";
|
|
7
8
|
export * from "./core/types";
|
|
8
9
|
export { RestApiProtocol } from "./protocols/rest_api";
|
|
9
10
|
export { providerRenderers, primevueRenderers, ProviderAutocomplete, ProviderSelect, ProviderMultiSelect, useProvider, JfText, JfTextArea, JfNumber, JfEnum, JfEnumArray, JfBoolean, } from "./vue";
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,KAAK,CAAC;AAG/B,OAAO,KAAK,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAEzD,OAAO,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AACrC,cAAc,iBAAiB,CAAC;AAChC,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAC3C,cAAc,mBAAmB,CAAC;AAElC,cAAc,cAAc,CAAC;AAG7B,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAGvD,OAAO,EACL,iBAAiB,EACjB,iBAAiB,EACjB,oBAAoB,EACpB,cAAc,EACd,mBAAmB,EACnB,WAAW,EACX,MAAM,EACN,UAAU,EACV,QAAQ,EACR,MAAM,EACN,WAAW,EACX,SAAS,GACV,MAAM,OAAO,CAAC;AAEf,MAAM,WAAW,cAAc;IAC7B,SAAS,CAAC,EAAE,QAAQ,EAAE,CAAC;IACvB,IAAI,CAAC,EAAE,UAAU,CAAC;CACnB;;iBAGc,GAAG,SAAS,cAAc;;AADzC,wBAYE"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,KAAK,CAAC;AAG/B,OAAO,KAAK,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAEzD,OAAO,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC;AACrC,cAAc,iBAAiB,CAAC;AAChC,OAAO,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAC3C,cAAc,mBAAmB,CAAC;AAClC,cAAc,mBAAmB,CAAC;AAElC,cAAc,cAAc,CAAC;AAG7B,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAGvD,OAAO,EACL,iBAAiB,EACjB,iBAAiB,EACjB,oBAAoB,EACpB,cAAc,EACd,mBAAmB,EACnB,WAAW,EACX,MAAM,EACN,UAAU,EACV,QAAQ,EACR,MAAM,EACN,WAAW,EACX,SAAS,GACV,MAAM,OAAO,CAAC;AAEf,MAAM,WAAW,cAAc;IAC7B,SAAS,CAAC,EAAE,QAAQ,EAAE,CAAC;IACvB,IAAI,CAAC,EAAE,UAAU,CAAC;CACnB;;iBAGc,GAAG,SAAS,cAAc;;AADzC,wBAYE"}
|
package/dist/index.js
CHANGED
|
@@ -2,6 +2,7 @@ import { cache } from "./core/cache.js";
|
|
|
2
2
|
import { registry } from "./core/registry.js";
|
|
3
3
|
import { jp } from "./core/jsonpath.js";
|
|
4
4
|
import { renderObj, renderTpl } from "./core/templating.js";
|
|
5
|
+
import { applyTransformPipeline, registerTransform } from "./core/transforms.js";
|
|
5
6
|
import { RestApiProtocol } from "./protocols/rest_api.js";
|
|
6
7
|
import { providerRenderers } from "./vue/index.js";
|
|
7
8
|
import { default as default2 } from "./vue/components/ProviderAutocomplete.vue.js";
|
|
@@ -39,11 +40,13 @@ export {
|
|
|
39
40
|
default10 as ProviderMultiSelect,
|
|
40
41
|
default9 as ProviderSelect,
|
|
41
42
|
RestApiProtocol,
|
|
43
|
+
applyTransformPipeline,
|
|
42
44
|
cache,
|
|
43
45
|
index as default,
|
|
44
46
|
jp,
|
|
45
47
|
primevueRenderers,
|
|
46
48
|
providerRenderers,
|
|
49
|
+
registerTransform,
|
|
47
50
|
registry,
|
|
48
51
|
renderObj,
|
|
49
52
|
renderTpl,
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../src/index.ts"],"sourcesContent":["import type { App } from \"vue\";\nimport { cache as globalCache } from \"./core/cache\";\nimport { registry as globalRegistry } from \"./core/registry\";\nimport type { Protocol, AuthConfig } from \"./core/types\";\n\nexport { cache } from \"./core/cache\";\nexport * from \"./core/jsonpath\";\nexport { registry } from \"./core/registry\";\nexport * from \"./core/templating\";\n// Core exports\nexport * from \"./core/types\";\n\n// Protocol exports\nexport { RestApiProtocol } from \"./protocols/rest_api\";\n\n// Vue exports - using named imports to avoid potential bundling issues\nexport {\n providerRenderers,\n primevueRenderers,\n ProviderAutocomplete,\n ProviderSelect,\n ProviderMultiSelect,\n useProvider,\n JfText,\n JfTextArea,\n JfNumber,\n JfEnum,\n JfEnumArray,\n JfBoolean,\n} from \"./vue\";\n\nexport interface ProviderConfig {\n protocols?: Protocol[];\n auth?: AuthConfig;\n}\n\nexport default {\n install(app: App, opts?: ProviderConfig) {\n const reg = globalRegistry;\n if (opts?.protocols) {\n for (const p of opts.protocols) {\n reg.register(p);\n }\n }\n app.provide(\"providerRegistry\", reg);\n app.provide(\"providerCache\", globalCache);\n app.provide(\"providerAuth\", opts?.auth ?? {});\n },\n};\n"],"names":["globalRegistry","globalCache"],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../src/index.ts"],"sourcesContent":["import type { App } from \"vue\";\nimport { cache as globalCache } from \"./core/cache\";\nimport { registry as globalRegistry } from \"./core/registry\";\nimport type { Protocol, AuthConfig } from \"./core/types\";\n\nexport { cache } from \"./core/cache\";\nexport * from \"./core/jsonpath\";\nexport { registry } from \"./core/registry\";\nexport * from \"./core/templating\";\nexport * from \"./core/transforms\";\n// Core exports\nexport * from \"./core/types\";\n\n// Protocol exports\nexport { RestApiProtocol } from \"./protocols/rest_api\";\n\n// Vue exports - using named imports to avoid potential bundling issues\nexport {\n providerRenderers,\n primevueRenderers,\n ProviderAutocomplete,\n ProviderSelect,\n ProviderMultiSelect,\n useProvider,\n JfText,\n JfTextArea,\n JfNumber,\n JfEnum,\n JfEnumArray,\n JfBoolean,\n} from \"./vue\";\n\nexport interface ProviderConfig {\n protocols?: Protocol[];\n auth?: AuthConfig;\n}\n\nexport default {\n install(app: App, opts?: ProviderConfig) {\n const reg = globalRegistry;\n if (opts?.protocols) {\n for (const p of opts.protocols) {\n reg.register(p);\n }\n }\n app.provide(\"providerRegistry\", reg);\n app.provide(\"providerCache\", globalCache);\n app.provide(\"providerAuth\", opts?.auth ?? {});\n },\n};\n"],"names":["globalRegistry","globalCache"],"mappings":";;;;;;;;;;;;;;;;;;AAqCA,MAAA,QAAe;AAAA,EACb,QAAQ,KAAU,MAAuB;AACvC,UAAM,MAAMA;AACZ,QAAI,MAAM,WAAW;AACnB,iBAAW,KAAK,KAAK,WAAW;AAC9B,YAAI,SAAS,CAAC;AAAA,MAChB;AAAA,IACF;AACA,QAAI,QAAQ,oBAAoB,GAAG;AACnC,QAAI,QAAQ,iBAAiBC,KAAW;AACxC,QAAI,QAAQ,gBAAgB,MAAM,QAAQ,CAAA,CAAE;AAAA,EAC9C;AACF;"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { type TransformPipeline } from "../core/transforms";
|
|
2
|
+
import type { Protocol, AuthConfig } from "../core/types";
|
|
2
3
|
export type RestApiCfg = {
|
|
3
4
|
url: string;
|
|
4
5
|
method?: "GET" | "POST";
|
|
@@ -11,7 +12,7 @@ export type RestApiCfg = {
|
|
|
11
12
|
value: string;
|
|
12
13
|
meta?: Record<string, string>;
|
|
13
14
|
};
|
|
14
|
-
|
|
15
|
+
transforms?: TransformPipeline;
|
|
15
16
|
paginate?: {
|
|
16
17
|
cursorPath: string;
|
|
17
18
|
param: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rest_api.d.ts","sourceRoot":"","sources":["../../src/protocols/rest_api.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EACV,QAAQ,
|
|
1
|
+
{"version":3,"file":"rest_api.d.ts","sourceRoot":"","sources":["../../src/protocols/rest_api.ts"],"names":[],"mappings":"AAEA,OAAO,EAEL,KAAK,iBAAiB,EACvB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EACV,QAAQ,EAGR,UAAU,EACX,MAAM,eAAe,CAAC;AAEvB,MAAM,MAAM,UAAU,GAAG;IACvB,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,CAAC,EAAE,KAAK,GAAG,MAAM,CAAC;IACxB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChC,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;KAAE,CAAC;IACrE,UAAU,CAAC,EAAE,iBAAiB,CAAC;IAC/B,QAAQ,CAAC,EAAE;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC;IACpE,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB,CAAC;AA0DF,eAAO,MAAM,eAAe,QAAO,QAAQ,CAAC,UAAU,CAoFpD,CAAC"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { jp } from "../core/jsonpath.js";
|
|
2
2
|
import { renderTpl, renderObj } from "../core/templating.js";
|
|
3
|
+
import { applyTransformPipeline } from "../core/transforms.js";
|
|
3
4
|
function buildAuthHeaders(auth, globalAuth) {
|
|
4
5
|
const headers = {};
|
|
5
6
|
if (!auth) return headers;
|
|
@@ -80,19 +81,19 @@ const RestApiProtocol = () => ({
|
|
|
80
81
|
return { items: [], ttl: 0 };
|
|
81
82
|
}
|
|
82
83
|
const json = await res.json();
|
|
83
|
-
|
|
84
|
-
if (cfg.
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
)
|
|
94
|
-
|
|
95
|
-
}
|
|
84
|
+
let items = jp(json, cfg.items);
|
|
85
|
+
if (cfg.transforms && cfg.transforms.length > 0) {
|
|
86
|
+
items = applyTransformPipeline(items, cfg.transforms);
|
|
87
|
+
}
|
|
88
|
+
for (const it of items) {
|
|
89
|
+
const itemObj = it;
|
|
90
|
+
const formattedLabel = itemObj._formattedLabel;
|
|
91
|
+
const label = formattedLabel ? String(formattedLabel) : jp(it, cfg.map.label)[0];
|
|
92
|
+
const value = jp(it, cfg.map.value)[0];
|
|
93
|
+
const meta = cfg.map.meta ? Object.fromEntries(
|
|
94
|
+
Object.entries(cfg.map.meta).map(([k, p]) => [k, jp(it, p)[0]])
|
|
95
|
+
) : void 0;
|
|
96
|
+
out.push({ label: String(label ?? ""), value, meta });
|
|
96
97
|
}
|
|
97
98
|
cursor = cfg.paginate ? jp(json, cfg.paginate.cursorPath)[0] : null;
|
|
98
99
|
page += 1;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rest_api.js","sources":["../../src/protocols/rest_api.ts"],"sourcesContent":["import { jp } from \"../core/jsonpath\";\nimport { renderObj, renderTpl } from \"../core/templating\";\nimport type {\n Protocol,\n ProviderItem,\n ProviderOutput,\n AuthConfig,\n} from \"../core/types\";\n\nexport type RestApiCfg = {\n url: string;\n method?: \"GET\" | \"POST\";\n headers?: Record<string, string>;\n query?: Record<string, unknown>;\n body?: unknown;\n items: string; // JSONPath to array; e.g. \"$.items[*]\"\n map: { label: string; value: string; meta?: Record<string, string> }; // relative to each item\n
|
|
1
|
+
{"version":3,"file":"rest_api.js","sources":["../../src/protocols/rest_api.ts"],"sourcesContent":["import { jp } from \"../core/jsonpath\";\nimport { renderObj, renderTpl } from \"../core/templating\";\nimport {\n applyTransformPipeline,\n type TransformPipeline,\n} from \"../core/transforms\";\nimport type {\n Protocol,\n ProviderItem,\n ProviderOutput,\n AuthConfig,\n} from \"../core/types\";\n\nexport type RestApiCfg = {\n url: string;\n method?: \"GET\" | \"POST\";\n headers?: Record<string, string>;\n query?: Record<string, unknown>;\n body?: unknown;\n items: string; // JSONPath to array; e.g. \"$.items[*]\"\n map: { label: string; value: string; meta?: Record<string, string> }; // relative to each item\n transforms?: TransformPipeline; // Optional transform pipeline applied before mapping\n paginate?: { cursorPath: string; param: string; maxPages?: number };\n auth?: AuthConfig;\n showError?: boolean; // Whether to show error messages, defaults to true\n};\n\nfunction buildAuthHeaders(\n auth?: AuthConfig,\n globalAuth?: Record<string, unknown>,\n): Record<string, string> {\n const headers: Record<string, string> = {};\n\n if (!auth) return headers;\n\n // Handle \"use\" reference to global auth\n if (auth.use && globalAuth?.[auth.use]) {\n const globalValue = globalAuth[auth.use];\n const value =\n typeof globalValue === \"function\" ? globalValue() : globalValue;\n\n if (auth.use === \"apiKey\") {\n headers[\"X-API-Key\"] = String(value);\n } else if (auth.use === \"bearer\") {\n headers[\"Authorization\"] = `Bearer ${value}`;\n } else if (auth.use === \"token\") {\n headers[\"Authorization\"] = `Token ${value}`;\n }\n return headers;\n }\n\n // Handle direct auth values\n if (auth.apiKey) {\n const value =\n typeof auth.apiKey === \"function\" ? auth.apiKey() : auth.apiKey;\n headers[\"X-API-Key\"] = String(value);\n }\n\n if (auth.bearer) {\n const value =\n typeof auth.bearer === \"function\" ? auth.bearer() : auth.bearer;\n headers[\"Authorization\"] = `Bearer ${value}`;\n }\n\n if (auth.token) {\n const value = typeof auth.token === \"function\" ? auth.token() : auth.token;\n headers[\"Authorization\"] = `Token ${value}`;\n }\n\n // Handle custom auth fields\n for (const [key, value] of Object.entries(auth)) {\n if (\n ![\"use\", \"apiKey\", \"bearer\", \"token\"].includes(key) &&\n value !== undefined\n ) {\n const authValue = typeof value === \"function\" ? value() : value;\n headers[key] = String(authValue);\n }\n }\n\n return headers;\n}\n\nexport const RestApiProtocol = (): Protocol<RestApiCfg> => ({\n protocol: \"rest_api\",\n async resolve(cfg, ctx): Promise<ProviderOutput> {\n const ac = ctx.signal;\n const out: ProviderItem[] = [];\n let cursor: unknown = null;\n let page = 0;\n do {\n const renderedUrl = renderTpl(cfg.url, { data: ctx.data, ui: ctx.ui });\n\n // Check if URL has unresolved template variables (empty string after template rendering)\n if (cfg.url.includes(\"{{\") && renderedUrl.endsWith(\"/\")) {\n return { items: [], ttl: 0 };\n }\n\n const url = new URL(renderedUrl);\n const q = renderObj(cfg.query ?? {}, {\n data: ctx.data,\n ui: ctx.ui,\n }) as Record<string, unknown>;\n for (const [k, v] of Object.entries(q))\n if (v !== undefined && v !== \"\") url.searchParams.set(k, String(v));\n if (cursor && cfg.paginate)\n url.searchParams.set(cfg.paginate.param, String(cursor));\n\n // Build headers with auth\n const baseHeaders = renderObj(cfg.headers ?? {}, {\n data: ctx.data,\n }) as Record<string, string>;\n const authHeaders = buildAuthHeaders(cfg.auth, ctx.auth);\n const headers = { ...baseHeaders, ...authHeaders };\n\n const method = cfg.method ?? \"GET\";\n const requestInit: RequestInit = {\n method,\n headers,\n signal: ac,\n };\n\n // Only include body for non-GET requests\n if (method !== \"GET\" && cfg.body) {\n requestInit.body = JSON.stringify(\n renderObj(cfg.body, { data: ctx.data }),\n );\n }\n\n const res = await fetch(url.toString(), requestInit);\n if (!res.ok) {\n if (cfg.showError !== false) {\n throw new Error(`REST ${res.status}`);\n }\n // If showError is false, return empty items instead of throwing\n return { items: [], ttl: 0 };\n }\n const json = await res.json();\n let items = jp(json, cfg.items);\n\n // Apply transform pipeline if provided\n if (cfg.transforms && cfg.transforms.length > 0) {\n items = applyTransformPipeline(items, cfg.transforms);\n }\n\n // Map items to ProviderItem format\n for (const it of items) {\n // Check if item has _formattedLabel from transforms\n const itemObj = it as Record<string, unknown>;\n const formattedLabel = itemObj._formattedLabel;\n\n const label = formattedLabel\n ? String(formattedLabel)\n : jp(it, cfg.map.label)[0];\n const value = jp(it, cfg.map.value)[0];\n const meta = cfg.map.meta\n ? Object.fromEntries(\n Object.entries(cfg.map.meta).map(([k, p]) => [k, jp(it, p)[0]]),\n )\n : undefined;\n out.push({ label: String(label ?? \"\"), value, meta });\n }\n cursor = cfg.paginate ? jp(json, cfg.paginate.cursorPath)[0] : null;\n page += 1;\n } while (cfg.paginate && cursor && page < (cfg.paginate.maxPages ?? 5));\n return { items: out, ttl: 300 };\n },\n});\n"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Transform pipeline system for manipulating API response data
|
|
3
|
+
* Transforms are applied sequentially in the order they appear in the pipeline
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export interface Transform {
|
|
7
|
+
name: string;
|
|
8
|
+
[key: string]: unknown;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface FlattenTransform extends Transform {
|
|
12
|
+
name: "flatten";
|
|
13
|
+
key: string; // The key containing the nested array to flatten
|
|
14
|
+
labelFormat?: string; // Optional format string like "{parent.name} → {name}"
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export type TransformStep = FlattenTransform;
|
|
18
|
+
|
|
19
|
+
export type TransformPipeline = TransformStep[];
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Registry of transform functions
|
|
23
|
+
*/
|
|
24
|
+
type TransformFunction = (items: unknown[], config: Transform) => unknown[];
|
|
25
|
+
|
|
26
|
+
const transformRegistry: Record<string, TransformFunction> = {};
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Register a transform function
|
|
30
|
+
*/
|
|
31
|
+
export function registerTransform(name: string, fn: TransformFunction): void {
|
|
32
|
+
transformRegistry[name] = fn;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Apply a pipeline of transforms to data
|
|
37
|
+
*/
|
|
38
|
+
export function applyTransformPipeline(
|
|
39
|
+
items: unknown[],
|
|
40
|
+
pipeline: TransformPipeline,
|
|
41
|
+
): unknown[] {
|
|
42
|
+
let result = items;
|
|
43
|
+
|
|
44
|
+
for (const transform of pipeline) {
|
|
45
|
+
const fn = transformRegistry[transform.name];
|
|
46
|
+
if (!fn) {
|
|
47
|
+
throw new Error(`Unknown transform: ${transform.name}`);
|
|
48
|
+
}
|
|
49
|
+
result = fn(result, transform);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return result;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Flatten transform - flattens nested arrays into a single level
|
|
57
|
+
*/
|
|
58
|
+
function flattenTransform(items: unknown[], config: Transform): unknown[] {
|
|
59
|
+
const flattenConfig = config as FlattenTransform;
|
|
60
|
+
const { key, labelFormat } = flattenConfig;
|
|
61
|
+
const flattened: unknown[] = [];
|
|
62
|
+
|
|
63
|
+
for (const item of items) {
|
|
64
|
+
if (typeof item !== "object" || item === null) continue;
|
|
65
|
+
|
|
66
|
+
const itemObj = item as Record<string, unknown>;
|
|
67
|
+
const children = itemObj[key];
|
|
68
|
+
|
|
69
|
+
if (Array.isArray(children)) {
|
|
70
|
+
for (const child of children) {
|
|
71
|
+
if (typeof child !== "object" || child === null) continue;
|
|
72
|
+
|
|
73
|
+
const childObj = child as Record<string, unknown>;
|
|
74
|
+
|
|
75
|
+
// If labelFormat is provided, use it to format the label
|
|
76
|
+
if (labelFormat) {
|
|
77
|
+
const formattedChild = { ...childObj };
|
|
78
|
+
|
|
79
|
+
// Replace placeholders like {parent.name} and {name}
|
|
80
|
+
let formattedLabel = labelFormat;
|
|
81
|
+
formattedLabel = formattedLabel.replace(
|
|
82
|
+
/\{parent\.(\w+)\}/g,
|
|
83
|
+
(_, prop) => String(itemObj[prop] ?? ""),
|
|
84
|
+
);
|
|
85
|
+
formattedLabel = formattedLabel.replace(/\{(\w+)\}/g, (_, prop) =>
|
|
86
|
+
String(childObj[prop] ?? ""),
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
formattedChild._formattedLabel = formattedLabel;
|
|
90
|
+
formattedChild._parent = itemObj;
|
|
91
|
+
flattened.push(formattedChild);
|
|
92
|
+
} else {
|
|
93
|
+
// Just add parent reference
|
|
94
|
+
flattened.push({
|
|
95
|
+
...childObj,
|
|
96
|
+
_parent: itemObj,
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return flattened;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Register built-in transforms
|
|
107
|
+
registerTransform("flatten", flattenTransform);
|
package/src/index.ts
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import { jp } from "../core/jsonpath";
|
|
2
2
|
import { renderObj, renderTpl } from "../core/templating";
|
|
3
|
+
import {
|
|
4
|
+
applyTransformPipeline,
|
|
5
|
+
type TransformPipeline,
|
|
6
|
+
} from "../core/transforms";
|
|
3
7
|
import type {
|
|
4
8
|
Protocol,
|
|
5
9
|
ProviderItem,
|
|
@@ -15,7 +19,7 @@ export type RestApiCfg = {
|
|
|
15
19
|
body?: unknown;
|
|
16
20
|
items: string; // JSONPath to array; e.g. "$.items[*]"
|
|
17
21
|
map: { label: string; value: string; meta?: Record<string, string> }; // relative to each item
|
|
18
|
-
|
|
22
|
+
transforms?: TransformPipeline; // Optional transform pipeline applied before mapping
|
|
19
23
|
paginate?: { cursorPath: string; param: string; maxPages?: number };
|
|
20
24
|
auth?: AuthConfig;
|
|
21
25
|
showError?: boolean; // Whether to show error messages, defaults to true
|
|
@@ -132,23 +136,29 @@ export const RestApiProtocol = (): Protocol<RestApiCfg> => ({
|
|
|
132
136
|
return { items: [], ttl: 0 };
|
|
133
137
|
}
|
|
134
138
|
const json = await res.json();
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
//
|
|
138
|
-
if (cfg.
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
139
|
+
let items = jp(json, cfg.items);
|
|
140
|
+
|
|
141
|
+
// Apply transform pipeline if provided
|
|
142
|
+
if (cfg.transforms && cfg.transforms.length > 0) {
|
|
143
|
+
items = applyTransformPipeline(items, cfg.transforms);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Map items to ProviderItem format
|
|
147
|
+
for (const it of items) {
|
|
148
|
+
// Check if item has _formattedLabel from transforms
|
|
149
|
+
const itemObj = it as Record<string, unknown>;
|
|
150
|
+
const formattedLabel = itemObj._formattedLabel;
|
|
151
|
+
|
|
152
|
+
const label = formattedLabel
|
|
153
|
+
? String(formattedLabel)
|
|
154
|
+
: jp(it, cfg.map.label)[0];
|
|
155
|
+
const value = jp(it, cfg.map.value)[0];
|
|
156
|
+
const meta = cfg.map.meta
|
|
157
|
+
? Object.fromEntries(
|
|
158
|
+
Object.entries(cfg.map.meta).map(([k, p]) => [k, jp(it, p)[0]]),
|
|
159
|
+
)
|
|
160
|
+
: undefined;
|
|
161
|
+
out.push({ label: String(label ?? ""), value, meta });
|
|
152
162
|
}
|
|
153
163
|
cursor = cfg.paginate ? jp(json, cfg.paginate.cursorPath)[0] : null;
|
|
154
164
|
page += 1;
|