@ruvos/handler-sdk 0.1.25 → 0.1.27
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/ccda.d.ts +3 -0
- package/dist/ccda.d.ts.map +1 -0
- package/dist/ccda.js +87 -0
- package/dist/ccda.js.map +1 -0
- package/dist/cli/create-handler.d.ts +3 -0
- package/dist/cli/create-handler.d.ts.map +1 -0
- package/dist/cli/create-handler.js +215 -0
- package/dist/cli/create-handler.js.map +1 -0
- package/dist/create-handler.d.ts +7 -0
- package/dist/create-handler.d.ts.map +1 -0
- package/dist/create-handler.js +96 -0
- package/dist/create-handler.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -0
- package/dist/logger.d.ts +12 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +32 -0
- package/dist/logger.js.map +1 -0
- package/dist/types.d.ts +80 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/validate.d.ts +6 -0
- package/dist/validate.d.ts.map +1 -0
- package/dist/validate.js +53 -0
- package/dist/validate.js.map +1 -0
- package/package.json +1 -1
package/dist/ccda.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ccda.d.ts","sourceRoot":"","sources":["../src/ccda.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AA8F/C,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,YAAY,EAAE,CAYrD"}
|
package/dist/ccda.js
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
function extractTagContent(xml, tag) {
|
|
2
|
+
const results = [];
|
|
3
|
+
const regex = new RegExp(`<${tag}[^>]*>([\\s\\S]*?)<\\/${tag}>`, "gi");
|
|
4
|
+
let match;
|
|
5
|
+
while ((match = regex.exec(xml)) !== null) {
|
|
6
|
+
results.push(match[1]);
|
|
7
|
+
}
|
|
8
|
+
return results;
|
|
9
|
+
}
|
|
10
|
+
function extractAttribute(xml, tag, attr) {
|
|
11
|
+
const regex = new RegExp(`<${tag}[^>]*?\\s${attr}="([^"]*)"`, "i");
|
|
12
|
+
const match = regex.exec(xml);
|
|
13
|
+
return match?.[1];
|
|
14
|
+
}
|
|
15
|
+
function extractPatient(xml) {
|
|
16
|
+
const patientRole = extractTagContent(xml, "patientRole")[0];
|
|
17
|
+
if (!patientRole)
|
|
18
|
+
return null;
|
|
19
|
+
const names = extractTagContent(patientRole, "name");
|
|
20
|
+
const given = names.length > 0 ? extractTagContent(names[0], "given") : [];
|
|
21
|
+
const family = names.length > 0 ? extractTagContent(names[0], "family") : [];
|
|
22
|
+
const gender = extractAttribute(patientRole, "administrativeGenderCode", "code");
|
|
23
|
+
const birthTime = extractAttribute(patientRole, "birthTime", "value");
|
|
24
|
+
const addrs = extractTagContent(patientRole, "addr");
|
|
25
|
+
let address;
|
|
26
|
+
if (addrs.length > 0) {
|
|
27
|
+
const streetLines = extractTagContent(addrs[0], "streetAddressLine");
|
|
28
|
+
const city = extractTagContent(addrs[0], "city")[0];
|
|
29
|
+
const state = extractTagContent(addrs[0], "state")[0];
|
|
30
|
+
const postalCode = extractTagContent(addrs[0], "postalCode")[0];
|
|
31
|
+
address = {
|
|
32
|
+
...(streetLines.length > 0 ? { line: streetLines } : {}),
|
|
33
|
+
...(city ? { city } : {}),
|
|
34
|
+
...(state ? { state } : {}),
|
|
35
|
+
...(postalCode ? { postalCode } : {}),
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
const genderMap = { M: "male", F: "female", UN: "unknown" };
|
|
39
|
+
return {
|
|
40
|
+
resourceType: "Patient",
|
|
41
|
+
name: [{ given, family: family[0] ?? "" }],
|
|
42
|
+
...(gender ? { gender: genderMap[gender] ?? "unknown" } : {}),
|
|
43
|
+
...(birthTime ? { birthDate: `${birthTime.slice(0, 4)}-${birthTime.slice(4, 6)}-${birthTime.slice(6, 8)}` } : {}),
|
|
44
|
+
...(address ? { address: [address] } : {}),
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
function extractSections(xml) {
|
|
48
|
+
const components = extractTagContent(xml, "component");
|
|
49
|
+
const sections = [];
|
|
50
|
+
for (const comp of components) {
|
|
51
|
+
const sectionBlocks = extractTagContent(comp, "section");
|
|
52
|
+
for (const sec of sectionBlocks) {
|
|
53
|
+
const code = extractAttribute(sec, "code", "code") ?? "unknown";
|
|
54
|
+
const titles = extractTagContent(sec, "title");
|
|
55
|
+
const title = titles[0] ?? "Untitled Section";
|
|
56
|
+
const texts = extractTagContent(sec, "text");
|
|
57
|
+
sections.push({ code, title, text: texts[0] });
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return sections;
|
|
61
|
+
}
|
|
62
|
+
function sectionToComposition(sections) {
|
|
63
|
+
return {
|
|
64
|
+
resourceType: "Composition",
|
|
65
|
+
status: "final",
|
|
66
|
+
type: {
|
|
67
|
+
coding: [{ system: "http://loinc.org", code: "34133-9", display: "Summary of episode note" }],
|
|
68
|
+
},
|
|
69
|
+
section: sections.map((s) => ({
|
|
70
|
+
title: s.title,
|
|
71
|
+
code: { coding: [{ code: s.code }] },
|
|
72
|
+
text: s.text ? { status: "generated", div: `<div xmlns="http://www.w3.org/1999/xhtml">${s.text}</div>` } : undefined,
|
|
73
|
+
})),
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
export function parseCcda(xml) {
|
|
77
|
+
const resources = [];
|
|
78
|
+
const patient = extractPatient(xml);
|
|
79
|
+
if (patient)
|
|
80
|
+
resources.push(patient);
|
|
81
|
+
const sections = extractSections(xml);
|
|
82
|
+
if (sections.length > 0) {
|
|
83
|
+
resources.push(sectionToComposition(sections));
|
|
84
|
+
}
|
|
85
|
+
return resources;
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=ccda.js.map
|
package/dist/ccda.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ccda.js","sourceRoot":"","sources":["../src/ccda.ts"],"names":[],"mappings":"AAQA,SAAS,iBAAiB,CAAC,GAAW,EAAE,GAAW;IACjD,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,MAAM,KAAK,GAAG,IAAI,MAAM,CAAC,IAAI,GAAG,yBAAyB,GAAG,GAAG,EAAE,IAAI,CAAC,CAAC;IACvE,IAAI,KAA6B,CAAC;IAClC,OAAO,CAAC,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC;QAC1C,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAE,CAAC,CAAC;IAC1B,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,SAAS,gBAAgB,CAAC,GAAW,EAAE,GAAW,EAAE,IAAY;IAC9D,MAAM,KAAK,GAAG,IAAI,MAAM,CAAC,IAAI,GAAG,YAAY,IAAI,YAAY,EAAE,GAAG,CAAC,CAAC;IACnE,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAC9B,OAAO,KAAK,EAAE,CAAC,CAAC,CAAC,CAAC;AACpB,CAAC;AAED,SAAS,cAAc,CAAC,GAAW;IACjC,MAAM,WAAW,GAAG,iBAAiB,CAAC,GAAG,EAAE,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC;IAC7D,IAAI,CAAC,WAAW;QAAE,OAAO,IAAI,CAAC;IAE9B,MAAM,KAAK,GAAG,iBAAiB,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;IACrD,MAAM,KAAK,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,iBAAiB,CAAC,KAAK,CAAC,CAAC,CAAE,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAC5E,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,iBAAiB,CAAC,KAAK,CAAC,CAAC,CAAE,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAE9E,MAAM,MAAM,GAAG,gBAAgB,CAAC,WAAW,EAAE,0BAA0B,EAAE,MAAM,CAAC,CAAC;IACjF,MAAM,SAAS,GAAG,gBAAgB,CAAC,WAAW,EAAE,WAAW,EAAE,OAAO,CAAC,CAAC;IAEtE,MAAM,KAAK,GAAG,iBAAiB,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;IACrD,IAAI,OAA4C,CAAC;IACjD,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACrB,MAAM,WAAW,GAAG,iBAAiB,CAAC,KAAK,CAAC,CAAC,CAAE,EAAE,mBAAmB,CAAC,CAAC;QACtE,MAAM,IAAI,GAAG,iBAAiB,CAAC,KAAK,CAAC,CAAC,CAAE,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;QACrD,MAAM,KAAK,GAAG,iBAAiB,CAAC,KAAK,CAAC,CAAC,CAAE,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC;QACvD,MAAM,UAAU,GAAG,iBAAiB,CAAC,KAAK,CAAC,CAAC,CAAE,EAAE,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC;QACjE,OAAO,GAAG;YACR,GAAG,CAAC,WAAW,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACxD,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACzB,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAC3B,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SACtC,CAAC;IACJ,CAAC;IAED,MAAM,SAAS,GAA2B,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE,SAAS,EAAE,CAAC;IAEpF,OAAO;QACL,YAAY,EAAE,SAAS;QACvB,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC;QAC1C,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,SAAS,CAAC,MAAM,CAAC,IAAI,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC7D,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,GAAG,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,SAAS,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACjH,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAC3C,CAAC;AACJ,CAAC;AAED,SAAS,eAAe,CAAC,GAAW;IAClC,MAAM,UAAU,GAAG,iBAAiB,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC;IACvD,MAAM,QAAQ,GAAkB,EAAE,CAAC;IAEnC,KAAK,MAAM,IAAI,IAAI,UAAU,EAAE,CAAC;QAC9B,MAAM,aAAa,GAAG,iBAAiB,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;QACzD,KAAK,MAAM,GAAG,IAAI,aAAa,EAAE,CAAC;YAChC,MAAM,IAAI,GAAG,gBAAgB,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,IAAI,SAAS,CAAC;YAChE,MAAM,MAAM,GAAG,iBAAiB,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;YAC/C,MAAM,KAAK,GAAG,MAAM,CAAC,CAAC,CAAC,IAAI,kBAAkB,CAAC;YAC9C,MAAM,KAAK,GAAG,iBAAiB,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;YAC7C,QAAQ,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;QACjD,CAAC;IACH,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,SAAS,oBAAoB,CAAC,QAAuB;IACnD,OAAO;QACL,YAAY,EAAE,aAAa;QAC3B,MAAM,EAAE,OAAO;QACf,IAAI,EAAE;YACJ,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,kBAAkB,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,yBAAyB,EAAE,CAAC;SAC9F;QACD,OAAO,EAAE,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YAC5B,KAAK,EAAE,CAAC,CAAC,KAAK;YACd,IAAI,EAAE,EAAE,MAAM,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,EAAE;YACpC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,WAAW,EAAE,GAAG,EAAE,6CAA6C,CAAC,CAAC,IAAI,QAAQ,EAAE,CAAC,CAAC,CAAC,SAAS;SACrH,CAAC,CAAC;KACJ,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,GAAW;IACnC,MAAM,SAAS,GAAmB,EAAE,CAAC;IAErC,MAAM,OAAO,GAAG,cAAc,CAAC,GAAG,CAAC,CAAC;IACpC,IAAI,OAAO;QAAE,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAErC,MAAM,QAAQ,GAAG,eAAe,CAAC,GAAG,CAAC,CAAC;IACtC,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACxB,SAAS,CAAC,IAAI,CAAC,oBAAoB,CAAC,QAAQ,CAAC,CAAC,CAAC;IACjD,CAAC;IAED,OAAO,SAAS,CAAC;AACnB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create-handler.d.ts","sourceRoot":"","sources":["../../src/cli/create-handler.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { mkdirSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
const name = process.argv[2];
|
|
5
|
+
if (!name) {
|
|
6
|
+
console.error("Usage: create-handler <handler-name>");
|
|
7
|
+
console.error("Example: create-handler ccda-parser");
|
|
8
|
+
process.exit(1);
|
|
9
|
+
}
|
|
10
|
+
const slug = name.replace(/[^a-z0-9-]/gi, "-").toLowerCase();
|
|
11
|
+
const dir = join(process.cwd(), `ruvos-handler-${slug}`);
|
|
12
|
+
mkdirSync(join(dir, "src"), { recursive: true });
|
|
13
|
+
mkdirSync(join(dir, "infra"), { recursive: true });
|
|
14
|
+
writeFileSync(join(dir, "package.json"), JSON.stringify({
|
|
15
|
+
name: `@ruvos/handler-${slug}`,
|
|
16
|
+
version: "0.1.0",
|
|
17
|
+
private: true,
|
|
18
|
+
type: "module",
|
|
19
|
+
scripts: {
|
|
20
|
+
build: "tsc -b && node esbuild.config.mjs",
|
|
21
|
+
clean: "rm -rf dist",
|
|
22
|
+
},
|
|
23
|
+
dependencies: {
|
|
24
|
+
"@ruvos/handler-sdk": "^0.1.0",
|
|
25
|
+
},
|
|
26
|
+
devDependencies: {
|
|
27
|
+
"@types/node": "^25.5.2",
|
|
28
|
+
esbuild: "^0.25.0",
|
|
29
|
+
typescript: "^5.7.0",
|
|
30
|
+
},
|
|
31
|
+
}, null, 2) + "\n");
|
|
32
|
+
writeFileSync(join(dir, "tsconfig.json"), JSON.stringify({
|
|
33
|
+
compilerOptions: {
|
|
34
|
+
target: "ES2022",
|
|
35
|
+
lib: ["ES2022"],
|
|
36
|
+
module: "NodeNext",
|
|
37
|
+
moduleResolution: "NodeNext",
|
|
38
|
+
strict: true,
|
|
39
|
+
skipLibCheck: true,
|
|
40
|
+
declaration: true,
|
|
41
|
+
sourceMap: true,
|
|
42
|
+
outDir: "dist",
|
|
43
|
+
rootDir: "src",
|
|
44
|
+
esModuleInterop: true,
|
|
45
|
+
isolatedModules: true,
|
|
46
|
+
verbatimModuleSyntax: true,
|
|
47
|
+
},
|
|
48
|
+
include: ["src/**/*.ts"],
|
|
49
|
+
exclude: ["node_modules", "dist"],
|
|
50
|
+
}, null, 2) + "\n");
|
|
51
|
+
writeFileSync(join(dir, "esbuild.config.mjs"), `import { build } from "esbuild";
|
|
52
|
+
|
|
53
|
+
await build({
|
|
54
|
+
entryPoints: ["src/index.ts"],
|
|
55
|
+
bundle: true,
|
|
56
|
+
platform: "node",
|
|
57
|
+
target: "node22",
|
|
58
|
+
format: "esm",
|
|
59
|
+
outfile: "dist/index.mjs",
|
|
60
|
+
sourcemap: true,
|
|
61
|
+
external: [],
|
|
62
|
+
banner: { js: 'import{createRequire}from"module";const require=createRequire(import.meta.url);' },
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
console.log("Built handler");
|
|
66
|
+
`);
|
|
67
|
+
writeFileSync(join(dir, "src/index.ts"), `import { createHandler, type HandlerInput, type HandlerOutcome } from "@ruvos/handler-sdk";
|
|
68
|
+
|
|
69
|
+
async function handle(input: HandlerInput): Promise<HandlerOutcome> {
|
|
70
|
+
// Access input.payload.resources for FHIR resources,
|
|
71
|
+
// input.config for per-registration configuration,
|
|
72
|
+
// and input.stage for the pipeline stage.
|
|
73
|
+
// Return passthrough, transform, route_to, requeue, or fail.
|
|
74
|
+
|
|
75
|
+
return { action: "passthrough" };
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export const handler = createHandler({ handlerId: "${slug}" }, handle);
|
|
79
|
+
`);
|
|
80
|
+
writeFileSync(join(dir, "infra/main.tf"), `module "handler" {
|
|
81
|
+
source = "git::https://gitlab.com/ruvos/ruvos-platform/ruvos-io.git//infra/modules/tenant-handler"
|
|
82
|
+
|
|
83
|
+
environment = var.environment
|
|
84
|
+
handler_slug = "${slug}"
|
|
85
|
+
payload_bucket_arn = var.payload_bucket_arn
|
|
86
|
+
memory_size = 256
|
|
87
|
+
timeout = 60
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
output "lambda_arn" {
|
|
91
|
+
value = module.handler.function_arn
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
output "function_name" {
|
|
95
|
+
value = module.handler.function_name
|
|
96
|
+
}
|
|
97
|
+
`);
|
|
98
|
+
writeFileSync(join(dir, "infra/variables.tf"), `variable "environment" {
|
|
99
|
+
type = string
|
|
100
|
+
default = "dev"
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
variable "payload_bucket_arn" {
|
|
104
|
+
type = string
|
|
105
|
+
description = "ARN of the Ruvos payloads S3 bucket"
|
|
106
|
+
}
|
|
107
|
+
`);
|
|
108
|
+
writeFileSync(join(dir, ".gitlab-ci.yml"), `stages:
|
|
109
|
+
- build
|
|
110
|
+
- deploy
|
|
111
|
+
- register
|
|
112
|
+
|
|
113
|
+
variables:
|
|
114
|
+
AWS_DEFAULT_REGION: us-east-1
|
|
115
|
+
NODE_VERSION: "20"
|
|
116
|
+
|
|
117
|
+
.aws_oidc_setup: &aws_oidc_setup
|
|
118
|
+
id_tokens:
|
|
119
|
+
GITLAB_OIDC_TOKEN:
|
|
120
|
+
aud: https://gitlab.com
|
|
121
|
+
before_script:
|
|
122
|
+
- export AWS_ROLE_ARN="\${AWS_GITLAB_ROLE_ARN}"
|
|
123
|
+
- export AWS_ROLE_SESSION_NAME="gitlab-\${CI_PROJECT_PATH_SLUG}-\${CI_PIPELINE_ID}"
|
|
124
|
+
- export AWS_WEB_IDENTITY_TOKEN_FILE="\${CI_PROJECT_DIR}/.gitlab-oidc-jwt"
|
|
125
|
+
- printf '%s' "\${GITLAB_OIDC_TOKEN}" > "\${AWS_WEB_IDENTITY_TOKEN_FILE}"
|
|
126
|
+
|
|
127
|
+
build:
|
|
128
|
+
stage: build
|
|
129
|
+
image: node:\${NODE_VERSION}
|
|
130
|
+
script:
|
|
131
|
+
- corepack enable
|
|
132
|
+
- pnpm install --frozen-lockfile
|
|
133
|
+
- pnpm build
|
|
134
|
+
- cd dist && zip -r handler.zip index.mjs index.mjs.map
|
|
135
|
+
artifacts:
|
|
136
|
+
paths:
|
|
137
|
+
- dist/handler.zip
|
|
138
|
+
expire_in: 1 day
|
|
139
|
+
|
|
140
|
+
deploy:
|
|
141
|
+
stage: deploy
|
|
142
|
+
image:
|
|
143
|
+
name: amazon/aws-cli:latest
|
|
144
|
+
entrypoint: [""]
|
|
145
|
+
<<: *aws_oidc_setup
|
|
146
|
+
needs:
|
|
147
|
+
- job: build
|
|
148
|
+
artifacts: true
|
|
149
|
+
script:
|
|
150
|
+
- aws lambda update-function-code
|
|
151
|
+
--function-name ruvos-\${ENV:-dev}-handler-${slug}
|
|
152
|
+
--zip-file fileb://dist/handler.zip
|
|
153
|
+
rules:
|
|
154
|
+
- if: $AWS_GITLAB_ROLE_ARN && $CI_COMMIT_BRANCH == "main"
|
|
155
|
+
|
|
156
|
+
register:
|
|
157
|
+
stage: register
|
|
158
|
+
image: curlimages/curl:latest
|
|
159
|
+
needs:
|
|
160
|
+
- job: deploy
|
|
161
|
+
script:
|
|
162
|
+
- |
|
|
163
|
+
curl -sf -X PUT "\${RUVOS_API_URL}/v1/platform/handlers/${slug}" \\
|
|
164
|
+
-H "Authorization: Bearer \${RUVOS_OPERATOR_TOKEN}" \\
|
|
165
|
+
-H "Content-Type: application/json" \\
|
|
166
|
+
-d '{
|
|
167
|
+
"displayName": "Handler: ${slug}",
|
|
168
|
+
"lambdaArn": "'\${HANDLER_LAMBDA_ARN}'",
|
|
169
|
+
"supportedStages": ["post_downstream_fetch", "pre_store"],
|
|
170
|
+
"defaultTimeoutMs": 60000,
|
|
171
|
+
"version": "0.1.0"
|
|
172
|
+
}'
|
|
173
|
+
rules:
|
|
174
|
+
- if: $AWS_GITLAB_ROLE_ARN && $CI_COMMIT_BRANCH == "main"
|
|
175
|
+
`);
|
|
176
|
+
writeFileSync(join(dir, "README.md"), `# ruvos-handler-${slug}
|
|
177
|
+
|
|
178
|
+
Custom pipeline handler for the Ruvos platform.
|
|
179
|
+
|
|
180
|
+
## Development
|
|
181
|
+
|
|
182
|
+
\`\`\`bash
|
|
183
|
+
pnpm install
|
|
184
|
+
pnpm build
|
|
185
|
+
\`\`\`
|
|
186
|
+
|
|
187
|
+
## Deployment
|
|
188
|
+
|
|
189
|
+
Push to \`main\` triggers the GitLab CI pipeline which:
|
|
190
|
+
1. Builds the handler
|
|
191
|
+
2. Deploys to AWS Lambda
|
|
192
|
+
3. Registers with the Ruvos platform
|
|
193
|
+
|
|
194
|
+
## Handler Interface
|
|
195
|
+
|
|
196
|
+
The handler receives a \`HandlerInput\` and must return a \`HandlerOutcome\`:
|
|
197
|
+
|
|
198
|
+
- \`passthrough\` — continue normal pipeline
|
|
199
|
+
- \`transform\` — replace resources with your modified version
|
|
200
|
+
- \`route_to\` — forward payload to a URL, skip normal storage
|
|
201
|
+
- \`requeue\` — retry later with optional delay
|
|
202
|
+
- \`fail\` — abort the job with a reason
|
|
203
|
+
`);
|
|
204
|
+
writeFileSync(join(dir, ".gitignore"), `node_modules/
|
|
205
|
+
dist/
|
|
206
|
+
.tsbuildinfo
|
|
207
|
+
`);
|
|
208
|
+
console.log(`Created handler scaffold at: ${dir}`);
|
|
209
|
+
console.log("");
|
|
210
|
+
console.log("Next steps:");
|
|
211
|
+
console.log(` cd ruvos-handler-${slug}`);
|
|
212
|
+
console.log(" pnpm install");
|
|
213
|
+
console.log(" # Edit src/index.ts with your handler logic");
|
|
214
|
+
console.log(" pnpm build");
|
|
215
|
+
//# sourceMappingURL=create-handler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create-handler.js","sourceRoot":"","sources":["../../src/cli/create-handler.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { HandlerInput, HandlerOutcome, HandlerFunction } from "./types.js";
|
|
2
|
+
export interface CreateHandlerOptions {
|
|
3
|
+
handlerId: string;
|
|
4
|
+
timeoutMs?: number;
|
|
5
|
+
}
|
|
6
|
+
export declare function createHandler(options: CreateHandlerOptions, fn: HandlerFunction): (event: HandlerInput) => Promise<HandlerOutcome>;
|
|
7
|
+
//# sourceMappingURL=create-handler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create-handler.d.ts","sourceRoot":"","sources":["../src/create-handler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,cAAc,EAAE,eAAe,EAAgB,MAAM,YAAY,CAAC;AAI9F,MAAM,WAAW,oBAAoB;IACnC,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAyCD,wBAAgB,aAAa,CAC3B,OAAO,EAAE,oBAAoB,EAC7B,EAAE,EAAE,eAAe,GAClB,CAAC,KAAK,EAAE,YAAY,KAAK,OAAO,CAAC,cAAc,CAAC,CAkFlD"}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { validateOutcome } from "./validate.js";
|
|
2
|
+
import { createHandlerLogger } from "./logger.js";
|
|
3
|
+
let _s3ClientPromise;
|
|
4
|
+
async function getS3Client() {
|
|
5
|
+
if (!_s3ClientPromise) {
|
|
6
|
+
_s3ClientPromise = import("@aws-sdk/client-s3");
|
|
7
|
+
}
|
|
8
|
+
return _s3ClientPromise;
|
|
9
|
+
}
|
|
10
|
+
async function readResourcesFromS3(bucket, key) {
|
|
11
|
+
const { S3Client, GetObjectCommand } = await getS3Client();
|
|
12
|
+
const s3 = new S3Client({});
|
|
13
|
+
const res = await s3.send(new GetObjectCommand({ Bucket: bucket, Key: key }));
|
|
14
|
+
return JSON.parse(await res.Body.transformToString());
|
|
15
|
+
}
|
|
16
|
+
async function writeResourcesToS3(bucket, key, resources) {
|
|
17
|
+
const { S3Client, PutObjectCommand } = await getS3Client();
|
|
18
|
+
const s3 = new S3Client({});
|
|
19
|
+
await s3.send(new PutObjectCommand({
|
|
20
|
+
Bucket: bucket,
|
|
21
|
+
Key: key,
|
|
22
|
+
Body: JSON.stringify(resources),
|
|
23
|
+
ContentType: "application/json",
|
|
24
|
+
}));
|
|
25
|
+
}
|
|
26
|
+
export function createHandler(options, fn) {
|
|
27
|
+
return async (event) => {
|
|
28
|
+
const logger = createHandlerLogger({
|
|
29
|
+
handlerId: options.handlerId,
|
|
30
|
+
correlationId: event.correlationId,
|
|
31
|
+
tenantId: event.tenantId,
|
|
32
|
+
stage: event.stage,
|
|
33
|
+
});
|
|
34
|
+
const bucket = process.env["PAYLOAD_BUCKET"] ?? "";
|
|
35
|
+
const startTime = Date.now();
|
|
36
|
+
try {
|
|
37
|
+
// Hydrate resources from S3 so handler authors always see inline resources
|
|
38
|
+
if (event.payload.resourcesS3Key && !event.payload.resources) {
|
|
39
|
+
if (!bucket) {
|
|
40
|
+
return { action: "fail", reason: "PAYLOAD_BUCKET env var not set; cannot read resources from S3" };
|
|
41
|
+
}
|
|
42
|
+
const resources = await readResourcesFromS3(bucket, event.payload.resourcesS3Key);
|
|
43
|
+
event = {
|
|
44
|
+
...event,
|
|
45
|
+
payload: { ...event.payload, resources },
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
logger.info("handler_invoked", {
|
|
49
|
+
jobId: event.jobId,
|
|
50
|
+
connectorType: event.connectorType,
|
|
51
|
+
resourceCount: event.payload.resources?.length,
|
|
52
|
+
});
|
|
53
|
+
const timeoutMs = Math.max(0, Math.min(Number(options.timeoutMs) || 120_000, 300_000));
|
|
54
|
+
const result = await Promise.race([
|
|
55
|
+
fn(event),
|
|
56
|
+
new Promise((_, reject) =>
|
|
57
|
+
// nosemgrep: javascript-eval-rule-eval_nodejs
|
|
58
|
+
setTimeout(() => reject(new Error(`Handler timed out after ${timeoutMs}ms`)), timeoutMs)),
|
|
59
|
+
]);
|
|
60
|
+
const validated = validateOutcome(result);
|
|
61
|
+
const durationMs = Date.now() - startTime;
|
|
62
|
+
// Spill transform resources to S3 to stay under Lambda payload limits
|
|
63
|
+
if (validated.action === "transform" && validated.resources) {
|
|
64
|
+
if (!bucket) {
|
|
65
|
+
return { action: "fail", reason: "PAYLOAD_BUCKET env var not set; cannot write transform results to S3" };
|
|
66
|
+
}
|
|
67
|
+
const outputKey = `hooks/${event.tenantId}/${event.jobId}/${event.stage}/${options.handlerId}/output.json`;
|
|
68
|
+
const resourceCount = validated.resources.length;
|
|
69
|
+
await writeResourcesToS3(bucket, outputKey, validated.resources);
|
|
70
|
+
logger.info("handler_completed", {
|
|
71
|
+
action: "transform",
|
|
72
|
+
durationMs,
|
|
73
|
+
outputResourceCount: resourceCount,
|
|
74
|
+
outputS3Key: outputKey,
|
|
75
|
+
});
|
|
76
|
+
return {
|
|
77
|
+
action: "transform",
|
|
78
|
+
resourcesS3Key: outputKey,
|
|
79
|
+
metadata: validated.metadata,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
logger.info("handler_completed", {
|
|
83
|
+
action: validated.action,
|
|
84
|
+
durationMs,
|
|
85
|
+
});
|
|
86
|
+
return validated;
|
|
87
|
+
}
|
|
88
|
+
catch (err) {
|
|
89
|
+
const durationMs = Date.now() - startTime;
|
|
90
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
91
|
+
logger.error("handler_error", { error: reason, durationMs });
|
|
92
|
+
return { action: "fail", reason };
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
//# sourceMappingURL=create-handler.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create-handler.js","sourceRoot":"","sources":["../src/create-handler.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export type { HookStage, FHIRResource, HandlerInput, HandlerOutcome, HandlerFunction, PlatformHandler, TenantHandler, HandlerScope, HookRegistration, } from "./types.js";
|
|
2
|
+
export { validateOutcome, HandlerValidationError } from "./validate.js";
|
|
3
|
+
export { createHandler, type CreateHandlerOptions } from "./create-handler.js";
|
|
4
|
+
export { createHandlerLogger, type HandlerLogger } from "./logger.js";
|
|
5
|
+
export { parseCcda } from "./ccda.js";
|
|
6
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,SAAS,EACT,YAAY,EACZ,YAAY,EACZ,cAAc,EACd,eAAe,EACf,eAAe,EACf,aAAa,EACb,YAAY,EACZ,gBAAgB,GACjB,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,eAAe,EAAE,sBAAsB,EAAE,MAAM,eAAe,CAAC;AACxE,OAAO,EAAE,aAAa,EAAE,KAAK,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAC/E,OAAO,EAAE,mBAAmB,EAAE,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AACtE,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAYA,OAAO,EAAE,eAAe,EAAE,sBAAsB,EAAE,MAAM,eAAe,CAAC;AACxE,OAAO,EAAE,aAAa,EAA6B,MAAM,qBAAqB,CAAC;AAC/E,OAAO,EAAE,mBAAmB,EAAsB,MAAM,aAAa,CAAC;AACtE,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC"}
|
package/dist/logger.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export interface HandlerLogger {
|
|
2
|
+
info(message: string, data?: Record<string, unknown>): void;
|
|
3
|
+
warn(message: string, data?: Record<string, unknown>): void;
|
|
4
|
+
error(message: string, data?: Record<string, unknown>): void;
|
|
5
|
+
}
|
|
6
|
+
export declare function createHandlerLogger(context: {
|
|
7
|
+
handlerId: string;
|
|
8
|
+
correlationId: string;
|
|
9
|
+
tenantId: string;
|
|
10
|
+
stage: string;
|
|
11
|
+
}): HandlerLogger;
|
|
12
|
+
//# sourceMappingURL=logger.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,aAAa;IAC5B,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IAC5D,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IAC5D,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;CAC9D;AAED,wBAAgB,mBAAmB,CAAC,OAAO,EAAE;IAC3C,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;CACf,GAAG,aAAa,CA8BhB"}
|
package/dist/logger.js
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export function createHandlerLogger(context) {
|
|
2
|
+
const base = {
|
|
3
|
+
service: `handler:${context.handlerId}`,
|
|
4
|
+
correlationId: context.correlationId,
|
|
5
|
+
tenantId: context.tenantId,
|
|
6
|
+
stage: context.stage,
|
|
7
|
+
};
|
|
8
|
+
function log(level, message, data) {
|
|
9
|
+
const entry = JSON.stringify({
|
|
10
|
+
level,
|
|
11
|
+
message,
|
|
12
|
+
timestamp: new Date().toISOString(),
|
|
13
|
+
...base,
|
|
14
|
+
...data,
|
|
15
|
+
});
|
|
16
|
+
if (level === "error") {
|
|
17
|
+
console.error(entry);
|
|
18
|
+
}
|
|
19
|
+
else if (level === "warn") {
|
|
20
|
+
console.warn(entry);
|
|
21
|
+
}
|
|
22
|
+
else {
|
|
23
|
+
console.log(entry);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return {
|
|
27
|
+
info: (msg, data) => log("info", msg, data),
|
|
28
|
+
warn: (msg, data) => log("warn", msg, data),
|
|
29
|
+
error: (msg, data) => log("error", msg, data),
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=logger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.js","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAMA,MAAM,UAAU,mBAAmB,CAAC,OAKnC;IACC,MAAM,IAAI,GAAG;QACX,OAAO,EAAE,WAAW,OAAO,CAAC,SAAS,EAAE;QACvC,aAAa,EAAE,OAAO,CAAC,aAAa;QACpC,QAAQ,EAAE,OAAO,CAAC,QAAQ;QAC1B,KAAK,EAAE,OAAO,CAAC,KAAK;KACrB,CAAC;IAEF,SAAS,GAAG,CAAC,KAAa,EAAE,OAAe,EAAE,IAA8B;QACzE,MAAM,KAAK,GAAG,IAAI,CAAC,SAAS,CAAC;YAC3B,KAAK;YACL,OAAO;YACP,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;YACnC,GAAG,IAAI;YACP,GAAG,IAAI;SACR,CAAC,CAAC;QACH,IAAI,KAAK,KAAK,OAAO,EAAE,CAAC;YACtB,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QACvB,CAAC;aAAM,IAAI,KAAK,KAAK,MAAM,EAAE,CAAC;YAC5B,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACtB,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QACrB,CAAC;IACH,CAAC;IAED,OAAO;QACL,IAAI,EAAE,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,CAAC,GAAG,CAAC,MAAM,EAAE,GAAG,EAAE,IAAI,CAAC;QAC3C,IAAI,EAAE,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,CAAC,GAAG,CAAC,MAAM,EAAE,GAAG,EAAE,IAAI,CAAC;QAC3C,KAAK,EAAE,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,EAAE,IAAI,CAAC;KAC9C,CAAC;AACJ,CAAC"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
export type HookStage = "post_downstream_fetch" | "pre_store" | "post_store";
|
|
2
|
+
export interface FHIRResource {
|
|
3
|
+
resourceType: string;
|
|
4
|
+
id?: string;
|
|
5
|
+
[key: string]: unknown;
|
|
6
|
+
}
|
|
7
|
+
export interface HandlerInput {
|
|
8
|
+
stage: HookStage;
|
|
9
|
+
tenantId: string;
|
|
10
|
+
jobId: string;
|
|
11
|
+
correlationId: string;
|
|
12
|
+
connectorType: string;
|
|
13
|
+
config?: Record<string, unknown>;
|
|
14
|
+
payload: {
|
|
15
|
+
resources?: FHIRResource[];
|
|
16
|
+
resourcesS3Key?: string;
|
|
17
|
+
metadata?: Record<string, unknown>;
|
|
18
|
+
s3Key?: string;
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
export type HandlerOutcome = {
|
|
22
|
+
action: "passthrough";
|
|
23
|
+
} | {
|
|
24
|
+
action: "transform";
|
|
25
|
+
resources?: FHIRResource[];
|
|
26
|
+
resourcesS3Key?: string;
|
|
27
|
+
metadata?: Record<string, unknown>;
|
|
28
|
+
} | {
|
|
29
|
+
action: "route_to";
|
|
30
|
+
url: string;
|
|
31
|
+
method?: string;
|
|
32
|
+
headers?: Record<string, string>;
|
|
33
|
+
body: unknown;
|
|
34
|
+
} | {
|
|
35
|
+
action: "requeue";
|
|
36
|
+
delaySeconds?: number;
|
|
37
|
+
} | {
|
|
38
|
+
action: "fail";
|
|
39
|
+
reason: string;
|
|
40
|
+
};
|
|
41
|
+
export type HandlerFunction = (input: HandlerInput) => Promise<HandlerOutcome>;
|
|
42
|
+
export interface PlatformHandler {
|
|
43
|
+
id: string;
|
|
44
|
+
handlerId: string;
|
|
45
|
+
displayName: string;
|
|
46
|
+
description?: string;
|
|
47
|
+
type: "lambda";
|
|
48
|
+
lambdaArn: string;
|
|
49
|
+
supportedStages: HookStage[];
|
|
50
|
+
defaultTimeoutMs: number;
|
|
51
|
+
version: string;
|
|
52
|
+
enabled: boolean;
|
|
53
|
+
createdAt: string;
|
|
54
|
+
updatedAt: string;
|
|
55
|
+
}
|
|
56
|
+
export interface TenantHandler {
|
|
57
|
+
id: string;
|
|
58
|
+
tenantId: string;
|
|
59
|
+
handlerId: string;
|
|
60
|
+
displayName: string;
|
|
61
|
+
type: "webhook" | "lambda";
|
|
62
|
+
webhookUrl?: string;
|
|
63
|
+
signingSecretArn?: string;
|
|
64
|
+
lambdaArn?: string;
|
|
65
|
+
enabled: boolean;
|
|
66
|
+
timeoutMs: number;
|
|
67
|
+
createdAt: string;
|
|
68
|
+
updatedAt: string;
|
|
69
|
+
}
|
|
70
|
+
export type HandlerScope = "platform" | "tenant";
|
|
71
|
+
export interface HookRegistration {
|
|
72
|
+
tenantId: string;
|
|
73
|
+
stage: HookStage;
|
|
74
|
+
handlerId: string;
|
|
75
|
+
handlerScope: HandlerScope;
|
|
76
|
+
priority: number;
|
|
77
|
+
enabled: boolean;
|
|
78
|
+
config?: Record<string, unknown>;
|
|
79
|
+
}
|
|
80
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate.d.ts","sourceRoot":"","sources":["../src/validate.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAIjD,qBAAa,sBAAuB,SAAQ,KAAK;gBACnC,OAAO,EAAE,MAAM;CAI5B;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,cAAc,CAkE9D"}
|
package/dist/validate.js
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
const VALID_ACTIONS = new Set(["passthrough", "transform", "route_to", "requeue", "fail"]);
|
|
2
|
+
export class HandlerValidationError extends Error {
|
|
3
|
+
constructor(message) {
|
|
4
|
+
super(message);
|
|
5
|
+
this.name = "HandlerValidationError";
|
|
6
|
+
}
|
|
7
|
+
}
|
|
8
|
+
export function validateOutcome(value) {
|
|
9
|
+
if (!value || typeof value !== "object") {
|
|
10
|
+
throw new HandlerValidationError("Handler outcome must be a non-null object");
|
|
11
|
+
}
|
|
12
|
+
const obj = value;
|
|
13
|
+
const action = obj["action"];
|
|
14
|
+
if (typeof action !== "string" || !VALID_ACTIONS.has(action)) {
|
|
15
|
+
throw new HandlerValidationError(`Invalid handler outcome action: "${String(action)}". Must be one of: ${[...VALID_ACTIONS].join(", ")}`);
|
|
16
|
+
}
|
|
17
|
+
switch (action) {
|
|
18
|
+
case "passthrough":
|
|
19
|
+
break;
|
|
20
|
+
case "transform":
|
|
21
|
+
if (!Array.isArray(obj["resources"]) && typeof obj["resourcesS3Key"] !== "string") {
|
|
22
|
+
throw new HandlerValidationError('Handler outcome "transform" requires a "resources" array or "resourcesS3Key" string');
|
|
23
|
+
}
|
|
24
|
+
if (Array.isArray(obj["resources"])) {
|
|
25
|
+
for (const r of obj["resources"]) {
|
|
26
|
+
if (!r || typeof r !== "object" || typeof r["resourceType"] !== "string") {
|
|
27
|
+
throw new HandlerValidationError("Each resource in transform outcome must have a string resourceType");
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
break;
|
|
32
|
+
case "route_to":
|
|
33
|
+
if (typeof obj["url"] !== "string" || !obj["url"]) {
|
|
34
|
+
throw new HandlerValidationError('Handler outcome "route_to" requires a non-empty "url" string');
|
|
35
|
+
}
|
|
36
|
+
if (!String(obj["url"]).startsWith("https://")) {
|
|
37
|
+
throw new HandlerValidationError('Handler outcome "route_to" url must use HTTPS');
|
|
38
|
+
}
|
|
39
|
+
break;
|
|
40
|
+
case "requeue":
|
|
41
|
+
if (obj["delaySeconds"] !== undefined && typeof obj["delaySeconds"] !== "number") {
|
|
42
|
+
throw new HandlerValidationError('Handler outcome "requeue" delaySeconds must be a number');
|
|
43
|
+
}
|
|
44
|
+
break;
|
|
45
|
+
case "fail":
|
|
46
|
+
if (typeof obj["reason"] !== "string") {
|
|
47
|
+
throw new HandlerValidationError('Handler outcome "fail" requires a "reason" string');
|
|
48
|
+
}
|
|
49
|
+
break;
|
|
50
|
+
}
|
|
51
|
+
return value;
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=validate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate.js","sourceRoot":"","sources":["../src/validate.ts"],"names":[],"mappings":"AAEA,MAAM,aAAa,GAAG,IAAI,GAAG,CAAC,CAAC,aAAa,EAAE,WAAW,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,CAAC,CAAC,CAAC;AAE3F,MAAM,OAAO,sBAAuB,SAAQ,KAAK;IAC/C,YAAY,OAAe;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,wBAAwB,CAAC;IACvC,CAAC;CACF;AAED,MAAM,UAAU,eAAe,CAAC,KAAc;IAC5C,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QACxC,MAAM,IAAI,sBAAsB,CAAC,2CAA2C,CAAC,CAAC;IAChF,CAAC;IAED,MAAM,GAAG,GAAG,KAAgC,CAAC;IAC7C,MAAM,MAAM,GAAG,GAAG,CAAC,QAAQ,CAAC,CAAC;IAE7B,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;QAC7D,MAAM,IAAI,sBAAsB,CAC9B,oCAAoC,MAAM,CAAC,MAAM,CAAC,sBAAsB,CAAC,GAAG,aAAa,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CACxG,CAAC;IACJ,CAAC;IAED,QAAQ,MAAM,EAAE,CAAC;QACf,KAAK,aAAa;YAChB,MAAM;QAER,KAAK,WAAW;YACd,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC,IAAI,OAAO,GAAG,CAAC,gBAAgB,CAAC,KAAK,QAAQ,EAAE,CAAC;gBAClF,MAAM,IAAI,sBAAsB,CAC9B,qFAAqF,CACtF,CAAC;YACJ,CAAC;YACD,IAAI,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC,EAAE,CAAC;gBACpC,KAAK,MAAM,CAAC,IAAI,GAAG,CAAC,WAAW,CAAc,EAAE,CAAC;oBAC9C,IAAI,CAAC,CAAC,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,OAAQ,CAA6B,CAAC,cAAc,CAAC,KAAK,QAAQ,EAAE,CAAC;wBACtG,MAAM,IAAI,sBAAsB,CAC9B,oEAAoE,CACrE,CAAC;oBACJ,CAAC;gBACH,CAAC;YACH,CAAC;YACD,MAAM;QAER,KAAK,UAAU;YACb,IAAI,OAAO,GAAG,CAAC,KAAK,CAAC,KAAK,QAAQ,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;gBAClD,MAAM,IAAI,sBAAsB,CAC9B,8DAA8D,CAC/D,CAAC;YACJ,CAAC;YACD,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;gBAC/C,MAAM,IAAI,sBAAsB,CAC9B,+CAA+C,CAChD,CAAC;YACJ,CAAC;YACD,MAAM;QAER,KAAK,SAAS;YACZ,IAAI,GAAG,CAAC,cAAc,CAAC,KAAK,SAAS,IAAI,OAAO,GAAG,CAAC,cAAc,CAAC,KAAK,QAAQ,EAAE,CAAC;gBACjF,MAAM,IAAI,sBAAsB,CAC9B,yDAAyD,CAC1D,CAAC;YACJ,CAAC;YACD,MAAM;QAER,KAAK,MAAM;YACT,IAAI,OAAO,GAAG,CAAC,QAAQ,CAAC,KAAK,QAAQ,EAAE,CAAC;gBACtC,MAAM,IAAI,sBAAsB,CAC9B,mDAAmD,CACpD,CAAC;YACJ,CAAC;YACD,MAAM;IACV,CAAC;IAED,OAAO,KAAuB,CAAC;AACjC,CAAC"}
|