@ruvos/handler-sdk 0.1.19 → 0.1.21

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 ADDED
@@ -0,0 +1,3 @@
1
+ import type { FHIRResource } from "./types.js";
2
+ export declare function parseCcda(xml: string): FHIRResource[];
3
+ //# sourceMappingURL=ccda.d.ts.map
@@ -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
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ccda.js","sourceRoot":"","sources":["../src/ccda.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=create-handler.d.ts.map
@@ -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: "node20",
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":"AACA,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAOlD,IAAI,gBAAkH,CAAC;AAEvH,KAAK,UAAU,WAAW;IACxB,IAAI,CAAC,gBAAgB,EAAE,CAAC;QACtB,gBAAgB,GAAG,MAAM,CAAC,oBAAoB,CAI5C,CAAC;IACL,CAAC;IACD,OAAO,gBAAgB,CAAC;AAC1B,CAAC;AAED,KAAK,UAAU,mBAAmB,CAAC,MAAc,EAAE,GAAW;IAC5D,MAAM,EAAE,QAAQ,EAAE,gBAAgB,EAAE,GAAG,MAAM,WAAW,EAGvD,CAAC;IACF,MAAM,EAAE,GAAG,IAAI,QAAQ,CAAC,EAAE,CAAC,CAAC;IAC5B,MAAM,GAAG,GAAG,MAAM,EAAE,CAAC,IAAI,CAAC,IAAI,gBAAgB,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC;IAC9E,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,IAAK,CAAC,iBAAiB,EAAE,CAAmB,CAAC;AAC3E,CAAC;AAED,KAAK,UAAU,kBAAkB,CAC/B,MAAc,EAAE,GAAW,EAAE,SAAyB;IAEtD,MAAM,EAAE,QAAQ,EAAE,gBAAgB,EAAE,GAAG,MAAM,WAAW,EAGvD,CAAC;IACF,MAAM,EAAE,GAAG,IAAI,QAAQ,CAAC,EAAE,CAAC,CAAC;IAC5B,MAAM,EAAE,CAAC,IAAI,CAAC,IAAI,gBAAgB,CAAC;QACjC,MAAM,EAAE,MAAM;QACd,GAAG,EAAE,GAAG;QACR,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC;QAC/B,WAAW,EAAE,kBAAkB;KAChC,CAAC,CAAC,CAAC;AACN,CAAC;AAED,MAAM,UAAU,aAAa,CAC3B,OAA6B,EAC7B,EAAmB;IAEnB,OAAO,KAAK,EAAE,KAAmB,EAA2B,EAAE;QAC5D,MAAM,MAAM,GAAG,mBAAmB,CAAC;YACjC,SAAS,EAAE,OAAO,CAAC,SAAS;YAC5B,aAAa,EAAE,KAAK,CAAC,aAAa;YAClC,QAAQ,EAAE,KAAK,CAAC,QAAQ;YACxB,KAAK,EAAE,KAAK,CAAC,KAAK;SACnB,CAAC,CAAC;QAEH,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,IAAI,EAAE,CAAC;QAEnD,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAE7B,IAAI,CAAC;YACH,2EAA2E;YAC3E,IAAI,KAAK,CAAC,OAAO,CAAC,cAAc,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,SAAS,EAAE,CAAC;gBAC7D,IAAI,CAAC,MAAM,EAAE,CAAC;oBACZ,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,+DAA+D,EAAE,CAAC;gBACrG,CAAC;gBACD,MAAM,SAAS,GAAG,MAAM,mBAAmB,CAAC,MAAM,EAAE,KAAK,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC;gBAClF,KAAK,GAAG;oBACN,GAAG,KAAK;oBACR,OAAO,EAAE,EAAE,GAAG,KAAK,CAAC,OAAO,EAAE,SAAS,EAAE;iBACzC,CAAC;YACJ,CAAC;YAED,MAAM,CAAC,IAAI,CAAC,iBAAiB,EAAE;gBAC7B,KAAK,EAAE,KAAK,CAAC,KAAK;gBAClB,aAAa,EAAE,KAAK,CAAC,aAAa;gBAClC,aAAa,EAAE,KAAK,CAAC,OAAO,CAAC,SAAS,EAAE,MAAM;aAC/C,CAAC,CAAC;YAEH,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,IAAI,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC;YACvF,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC;gBAChC,EAAE,CAAC,KAAK,CAAC;gBACT,IAAI,OAAO,CAAQ,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE;gBAC/B,8CAA8C;gBAC9C,UAAU,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,2BAA2B,SAAS,IAAI,CAAC,CAAC,EAAE,SAAS,CAAC,CACzF;aACF,CAAC,CAAC;YAEH,MAAM,SAAS,GAAG,eAAe,CAAC,MAAM,CAAC,CAAC;YAC1C,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,CAAC;YAE1C,sEAAsE;YACtE,IAAI,SAAS,CAAC,MAAM,KAAK,WAAW,IAAI,SAAS,CAAC,SAAS,EAAE,CAAC;gBAC5D,IAAI,CAAC,MAAM,EAAE,CAAC;oBACZ,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,sEAAsE,EAAE,CAAC;gBAC5G,CAAC;gBACD,MAAM,SAAS,GAAG,SAAS,KAAK,CAAC,QAAQ,IAAI,KAAK,CAAC,KAAK,IAAI,KAAK,CAAC,KAAK,IAAI,OAAO,CAAC,SAAS,cAAc,CAAC;gBAC3G,MAAM,aAAa,GAAG,SAAS,CAAC,SAAS,CAAC,MAAM,CAAC;gBACjD,MAAM,kBAAkB,CAAC,MAAM,EAAE,SAAS,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;gBAEjE,MAAM,CAAC,IAAI,CAAC,mBAAmB,EAAE;oBAC/B,MAAM,EAAE,WAAW;oBACnB,UAAU;oBACV,mBAAmB,EAAE,aAAa;oBAClC,WAAW,EAAE,SAAS;iBACvB,CAAC,CAAC;gBAEH,OAAO;oBACL,MAAM,EAAE,WAAW;oBACnB,cAAc,EAAE,SAAS;oBACzB,QAAQ,EAAE,SAAS,CAAC,QAAQ;iBAC7B,CAAC;YACJ,CAAC;YAED,MAAM,CAAC,IAAI,CAAC,mBAAmB,EAAE;gBAC/B,MAAM,EAAE,SAAS,CAAC,MAAM;gBACxB,UAAU;aACX,CAAC,CAAC;YAEH,OAAO,SAAS,CAAC;QACnB,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,CAAC;YAC1C,MAAM,MAAM,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YAEhE,MAAM,CAAC,KAAK,CAAC,eAAe,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC,CAAC;YAE7D,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;QACpC,CAAC;IACH,CAAC,CAAC;AACJ,CAAC"}
@@ -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,5 @@
1
+ export { validateOutcome, HandlerValidationError } from "./validate.js";
2
+ export { createHandler } from "./create-handler.js";
3
+ export { createHandlerLogger } from "./logger.js";
4
+ export { parseCcda } from "./ccda.js";
5
+ //# sourceMappingURL=index.js.map
@@ -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"}
@@ -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"}
@@ -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,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,6 @@
1
+ import type { HandlerOutcome } from "./types.js";
2
+ export declare class HandlerValidationError extends Error {
3
+ constructor(message: string);
4
+ }
5
+ export declare function validateOutcome(value: unknown): HandlerOutcome;
6
+ //# sourceMappingURL=validate.d.ts.map
@@ -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"}
@@ -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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ruvos/handler-sdk",
3
- "version": "0.1.19",
3
+ "version": "0.1.21",
4
4
  "type": "module",
5
5
  "sideEffects": false,
6
6
  "exports": {
@@ -36,6 +36,9 @@
36
36
  "build": "tsc -b",
37
37
  "clean": "rm -rf dist .tsbuildinfo"
38
38
  },
39
+ "dependencies": {
40
+ "@aws-sdk/client-s3": "^3.700.0"
41
+ },
39
42
  "devDependencies": {
40
43
  "@types/node": "25.5.2"
41
44
  },