@ruvos/handler-sdk 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +179 -0
- 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 +40 -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 +78 -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 +51 -0
- package/dist/validate.js.map +1 -0
- package/package.json +36 -0
package/README.md
ADDED
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
# @ruvos/handler-sdk
|
|
2
|
+
|
|
3
|
+
SDK for building custom pipeline handlers for the [Ruvos](https://ruvos.io) healthcare data exchange platform.
|
|
4
|
+
|
|
5
|
+
Handlers let you intercept and process FHIR resources at three stages of the Ruvos data pipeline:
|
|
6
|
+
- **`post_downstream_fetch`** — after data is fetched from an external source
|
|
7
|
+
- **`pre_store`** — before data is encrypted and stored
|
|
8
|
+
- **`post_store`** — after data is persisted
|
|
9
|
+
|
|
10
|
+
## Installation
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
npm install @ruvos/handler-sdk
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
## Quick Start
|
|
17
|
+
|
|
18
|
+
### Scaffold a new handler project
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
npx @ruvos/handler-sdk create-handler my-handler
|
|
22
|
+
cd ruvos-handler-my-handler
|
|
23
|
+
npm install
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
This generates a complete project with TypeScript config, esbuild bundling, Terraform infrastructure, and a GitLab CI pipeline.
|
|
27
|
+
|
|
28
|
+
### Write a handler
|
|
29
|
+
|
|
30
|
+
```typescript
|
|
31
|
+
import { createHandler, type HandlerInput, type HandlerOutcome } from "@ruvos/handler-sdk";
|
|
32
|
+
|
|
33
|
+
async function handle(input: HandlerInput): Promise<HandlerOutcome> {
|
|
34
|
+
const { stage, payload, tenantId, connectorType } = input;
|
|
35
|
+
|
|
36
|
+
// Transform: modify resources before storage
|
|
37
|
+
if (stage === "pre_store" && payload.resources) {
|
|
38
|
+
const enriched = payload.resources.map((r) => ({
|
|
39
|
+
...r,
|
|
40
|
+
meta: { ...r.meta, tag: [{ code: "reviewed", display: "Reviewed by handler" }] },
|
|
41
|
+
}));
|
|
42
|
+
return { action: "transform", resources: enriched };
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Passthrough: continue normal pipeline
|
|
46
|
+
return { action: "passthrough" };
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export const handler = createHandler({ handlerId: "my-handler" }, handle);
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Handler Outcomes
|
|
53
|
+
|
|
54
|
+
Every handler must return one of five outcomes:
|
|
55
|
+
|
|
56
|
+
| Outcome | Description |
|
|
57
|
+
|---|---|
|
|
58
|
+
| `passthrough` | Continue normal pipeline processing |
|
|
59
|
+
| `transform` | Replace resources with a modified set |
|
|
60
|
+
| `route_to` | Forward the payload to an HTTPS URL (skip normal storage) |
|
|
61
|
+
| `requeue` | Retry processing later with an optional delay |
|
|
62
|
+
| `fail` | Abort the job with a reason |
|
|
63
|
+
|
|
64
|
+
```typescript
|
|
65
|
+
// passthrough
|
|
66
|
+
return { action: "passthrough" };
|
|
67
|
+
|
|
68
|
+
// transform
|
|
69
|
+
return { action: "transform", resources: modifiedResources, metadata: { source: "enriched" } };
|
|
70
|
+
|
|
71
|
+
// route_to
|
|
72
|
+
return { action: "route_to", url: "https://my-service.example.com/ingest", body: payload };
|
|
73
|
+
|
|
74
|
+
// requeue
|
|
75
|
+
return { action: "requeue", delaySeconds: 30 };
|
|
76
|
+
|
|
77
|
+
// fail
|
|
78
|
+
return { action: "fail", reason: "Missing required patient identifier" };
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## API Reference
|
|
82
|
+
|
|
83
|
+
### `createHandler(options, fn)`
|
|
84
|
+
|
|
85
|
+
Wraps a handler function with logging, timeout, and outcome validation.
|
|
86
|
+
|
|
87
|
+
```typescript
|
|
88
|
+
import { createHandler } from "@ruvos/handler-sdk";
|
|
89
|
+
|
|
90
|
+
export const handler = createHandler(
|
|
91
|
+
{ handlerId: "my-handler", timeoutMs: 60_000 },
|
|
92
|
+
async (input) => { /* ... */ }
|
|
93
|
+
);
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
**Options:**
|
|
97
|
+
- `handlerId` — unique identifier for this handler
|
|
98
|
+
- `timeoutMs` — execution timeout (default: 120,000ms)
|
|
99
|
+
|
|
100
|
+
### `parseCcda(xml)`
|
|
101
|
+
|
|
102
|
+
Parses a C-CDA XML document into FHIR resources (Patient + Composition).
|
|
103
|
+
|
|
104
|
+
```typescript
|
|
105
|
+
import { parseCcda } from "@ruvos/handler-sdk";
|
|
106
|
+
|
|
107
|
+
const resources = parseCcda(xmlString);
|
|
108
|
+
// [{ resourceType: "Patient", ... }, { resourceType: "Composition", ... }]
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### `validateOutcome(value)`
|
|
112
|
+
|
|
113
|
+
Validates that a handler outcome has the correct structure. Called automatically by `createHandler`, but available for testing.
|
|
114
|
+
|
|
115
|
+
```typescript
|
|
116
|
+
import { validateOutcome, HandlerValidationError } from "@ruvos/handler-sdk";
|
|
117
|
+
|
|
118
|
+
try {
|
|
119
|
+
const validated = validateOutcome(result);
|
|
120
|
+
} catch (err) {
|
|
121
|
+
if (err instanceof HandlerValidationError) {
|
|
122
|
+
console.error("Invalid outcome:", err.message);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### `createHandlerLogger(context)`
|
|
128
|
+
|
|
129
|
+
Creates a structured JSON logger for use within handlers.
|
|
130
|
+
|
|
131
|
+
```typescript
|
|
132
|
+
import { createHandlerLogger } from "@ruvos/handler-sdk";
|
|
133
|
+
|
|
134
|
+
const logger = createHandlerLogger({
|
|
135
|
+
handlerId: "my-handler",
|
|
136
|
+
correlationId: input.correlationId,
|
|
137
|
+
tenantId: input.tenantId,
|
|
138
|
+
stage: input.stage,
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
logger.info("processing_resources", { count: input.payload.resources?.length });
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
## Types
|
|
145
|
+
|
|
146
|
+
```typescript
|
|
147
|
+
type HookStage = "post_downstream_fetch" | "pre_store" | "post_store";
|
|
148
|
+
|
|
149
|
+
interface HandlerInput {
|
|
150
|
+
stage: HookStage;
|
|
151
|
+
tenantId: string;
|
|
152
|
+
jobId: string;
|
|
153
|
+
correlationId: string;
|
|
154
|
+
connectorType: string;
|
|
155
|
+
config?: Record<string, unknown>;
|
|
156
|
+
payload: {
|
|
157
|
+
resources?: FHIRResource[];
|
|
158
|
+
metadata?: Record<string, unknown>;
|
|
159
|
+
s3Key?: string;
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
type HandlerOutcome =
|
|
164
|
+
| { action: "passthrough" }
|
|
165
|
+
| { action: "transform"; resources: FHIRResource[]; metadata?: Record<string, unknown> }
|
|
166
|
+
| { action: "route_to"; url: string; method?: string; headers?: Record<string, string>; body: unknown }
|
|
167
|
+
| { action: "requeue"; delaySeconds?: number }
|
|
168
|
+
| { action: "fail"; reason: string };
|
|
169
|
+
|
|
170
|
+
interface FHIRResource {
|
|
171
|
+
resourceType: string;
|
|
172
|
+
id?: string;
|
|
173
|
+
[key: string]: unknown;
|
|
174
|
+
}
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
## License
|
|
178
|
+
|
|
179
|
+
MIT
|
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":"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"}
|
|
@@ -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,EAAE,MAAM,YAAY,CAAC;AAIhF,MAAM,WAAW,oBAAoB;IACnC,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,wBAAgB,aAAa,CAC3B,OAAO,EAAE,oBAAoB,EAC7B,EAAE,EAAE,eAAe,GAClB,CAAC,KAAK,EAAE,YAAY,KAAK,OAAO,CAAC,cAAc,CAAC,CA4ClD"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { validateOutcome } from "./validate.js";
|
|
2
|
+
import { createHandlerLogger } from "./logger.js";
|
|
3
|
+
export function createHandler(options, fn) {
|
|
4
|
+
return async (event) => {
|
|
5
|
+
const logger = createHandlerLogger({
|
|
6
|
+
handlerId: options.handlerId,
|
|
7
|
+
correlationId: event.correlationId,
|
|
8
|
+
tenantId: event.tenantId,
|
|
9
|
+
stage: event.stage,
|
|
10
|
+
});
|
|
11
|
+
const startTime = Date.now();
|
|
12
|
+
logger.info("handler_invoked", {
|
|
13
|
+
jobId: event.jobId,
|
|
14
|
+
connectorType: event.connectorType,
|
|
15
|
+
resourceCount: event.payload.resources?.length,
|
|
16
|
+
});
|
|
17
|
+
try {
|
|
18
|
+
const timeoutMs = options.timeoutMs ?? 120_000;
|
|
19
|
+
const result = await Promise.race([
|
|
20
|
+
fn(event),
|
|
21
|
+
new Promise((_, reject) => setTimeout(() => reject(new Error(`Handler timed out after ${timeoutMs}ms`)), timeoutMs)),
|
|
22
|
+
]);
|
|
23
|
+
const validated = validateOutcome(result);
|
|
24
|
+
const durationMs = Date.now() - startTime;
|
|
25
|
+
logger.info("handler_completed", {
|
|
26
|
+
action: validated.action,
|
|
27
|
+
durationMs,
|
|
28
|
+
...(validated.action === "transform" ? { outputResourceCount: validated.resources.length } : {}),
|
|
29
|
+
});
|
|
30
|
+
return validated;
|
|
31
|
+
}
|
|
32
|
+
catch (err) {
|
|
33
|
+
const durationMs = Date.now() - startTime;
|
|
34
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
35
|
+
logger.error("handler_error", { error: reason, durationMs });
|
|
36
|
+
return { action: "fail", reason };
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
//# 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,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,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAC7B,MAAM,CAAC,IAAI,CAAC,iBAAiB,EAAE;YAC7B,KAAK,EAAE,KAAK,CAAC,KAAK;YAClB,aAAa,EAAE,KAAK,CAAC,aAAa;YAClC,aAAa,EAAE,KAAK,CAAC,OAAO,CAAC,SAAS,EAAE,MAAM;SAC/C,CAAC,CAAC;QAEH,IAAI,CAAC;YACH,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,OAAO,CAAC;YAC/C,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC;gBAChC,EAAE,CAAC,KAAK,CAAC;gBACT,IAAI,OAAO,CAAQ,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE,CAC/B,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,MAAM,CAAC,IAAI,CAAC,mBAAmB,EAAE;gBAC/B,MAAM,EAAE,SAAS,CAAC,MAAM;gBACxB,UAAU;gBACV,GAAG,CAAC,SAAS,CAAC,MAAM,KAAK,WAAW,CAAC,CAAC,CAAC,EAAE,mBAAmB,EAAE,SAAS,CAAC,SAAS,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aACjG,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"}
|
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,78 @@
|
|
|
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
|
+
metadata?: Record<string, unknown>;
|
|
17
|
+
s3Key?: string;
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
export type HandlerOutcome = {
|
|
21
|
+
action: "passthrough";
|
|
22
|
+
} | {
|
|
23
|
+
action: "transform";
|
|
24
|
+
resources: FHIRResource[];
|
|
25
|
+
metadata?: Record<string, unknown>;
|
|
26
|
+
} | {
|
|
27
|
+
action: "route_to";
|
|
28
|
+
url: string;
|
|
29
|
+
method?: string;
|
|
30
|
+
headers?: Record<string, string>;
|
|
31
|
+
body: unknown;
|
|
32
|
+
} | {
|
|
33
|
+
action: "requeue";
|
|
34
|
+
delaySeconds?: number;
|
|
35
|
+
} | {
|
|
36
|
+
action: "fail";
|
|
37
|
+
reason: string;
|
|
38
|
+
};
|
|
39
|
+
export type HandlerFunction = (input: HandlerInput) => Promise<HandlerOutcome>;
|
|
40
|
+
export interface PlatformHandler {
|
|
41
|
+
id: string;
|
|
42
|
+
handlerId: string;
|
|
43
|
+
displayName: string;
|
|
44
|
+
description?: string;
|
|
45
|
+
type: "lambda";
|
|
46
|
+
lambdaArn: string;
|
|
47
|
+
supportedStages: HookStage[];
|
|
48
|
+
defaultTimeoutMs: number;
|
|
49
|
+
version: string;
|
|
50
|
+
enabled: boolean;
|
|
51
|
+
createdAt: string;
|
|
52
|
+
updatedAt: string;
|
|
53
|
+
}
|
|
54
|
+
export interface TenantHandler {
|
|
55
|
+
id: string;
|
|
56
|
+
tenantId: string;
|
|
57
|
+
handlerId: string;
|
|
58
|
+
displayName: string;
|
|
59
|
+
type: "webhook" | "lambda";
|
|
60
|
+
webhookUrl?: string;
|
|
61
|
+
signingSecretArn?: string;
|
|
62
|
+
lambdaArn?: string;
|
|
63
|
+
enabled: boolean;
|
|
64
|
+
timeoutMs: number;
|
|
65
|
+
createdAt: string;
|
|
66
|
+
updatedAt: string;
|
|
67
|
+
}
|
|
68
|
+
export type HandlerScope = "platform" | "tenant";
|
|
69
|
+
export interface HookRegistration {
|
|
70
|
+
tenantId: string;
|
|
71
|
+
stage: HookStage;
|
|
72
|
+
handlerId: string;
|
|
73
|
+
handlerScope: HandlerScope;
|
|
74
|
+
priority: number;
|
|
75
|
+
enabled: boolean;
|
|
76
|
+
config?: Record<string, unknown>;
|
|
77
|
+
}
|
|
78
|
+
//# 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,CAgE9D"}
|
package/dist/validate.js
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
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"])) {
|
|
22
|
+
throw new HandlerValidationError('Handler outcome "transform" requires a "resources" array');
|
|
23
|
+
}
|
|
24
|
+
for (const r of obj["resources"]) {
|
|
25
|
+
if (!r || typeof r !== "object" || typeof r["resourceType"] !== "string") {
|
|
26
|
+
throw new HandlerValidationError("Each resource in transform outcome must have a string resourceType");
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
break;
|
|
30
|
+
case "route_to":
|
|
31
|
+
if (typeof obj["url"] !== "string" || !obj["url"]) {
|
|
32
|
+
throw new HandlerValidationError('Handler outcome "route_to" requires a non-empty "url" string');
|
|
33
|
+
}
|
|
34
|
+
if (!String(obj["url"]).startsWith("https://")) {
|
|
35
|
+
throw new HandlerValidationError('Handler outcome "route_to" url must use HTTPS');
|
|
36
|
+
}
|
|
37
|
+
break;
|
|
38
|
+
case "requeue":
|
|
39
|
+
if (obj["delaySeconds"] !== undefined && typeof obj["delaySeconds"] !== "number") {
|
|
40
|
+
throw new HandlerValidationError('Handler outcome "requeue" delaySeconds must be a number');
|
|
41
|
+
}
|
|
42
|
+
break;
|
|
43
|
+
case "fail":
|
|
44
|
+
if (typeof obj["reason"] !== "string") {
|
|
45
|
+
throw new HandlerValidationError('Handler outcome "fail" requires a "reason" string');
|
|
46
|
+
}
|
|
47
|
+
break;
|
|
48
|
+
}
|
|
49
|
+
return value;
|
|
50
|
+
}
|
|
51
|
+
//# 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,EAAE,CAAC;gBACrC,MAAM,IAAI,sBAAsB,CAC9B,0DAA0D,CAC3D,CAAC;YACJ,CAAC;YACD,KAAK,MAAM,CAAC,IAAI,GAAG,CAAC,WAAW,CAAc,EAAE,CAAC;gBAC9C,IAAI,CAAC,CAAC,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,OAAQ,CAA6B,CAAC,cAAc,CAAC,KAAK,QAAQ,EAAE,CAAC;oBACtG,MAAM,IAAI,sBAAsB,CAC9B,oEAAoE,CACrE,CAAC;gBACJ,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
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ruvos/handler-sdk",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"sideEffects": false,
|
|
6
|
+
"exports": {
|
|
7
|
+
".": {
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"import": "./dist/index.js"
|
|
10
|
+
}
|
|
11
|
+
},
|
|
12
|
+
"types": "./dist/index.d.ts",
|
|
13
|
+
"files": ["dist", "!dist/.tsbuildinfo"],
|
|
14
|
+
"description": "SDK for building custom pipeline handlers for the Ruvos healthcare data exchange platform",
|
|
15
|
+
"license": "MIT",
|
|
16
|
+
"repository": {
|
|
17
|
+
"type": "git",
|
|
18
|
+
"url": "https://gitlab.com/ruvos/ruvos-platform/ruvos-io.git",
|
|
19
|
+
"directory": "packages/handler-sdk"
|
|
20
|
+
},
|
|
21
|
+
"keywords": ["ruvos", "healthcare", "fhir", "handler", "pipeline", "sdk"],
|
|
22
|
+
"publishConfig": {
|
|
23
|
+
"access": "public",
|
|
24
|
+
"registry": "https://registry.npmjs.org/"
|
|
25
|
+
},
|
|
26
|
+
"scripts": {
|
|
27
|
+
"build": "tsc -b",
|
|
28
|
+
"clean": "rm -rf dist .tsbuildinfo"
|
|
29
|
+
},
|
|
30
|
+
"devDependencies": {
|
|
31
|
+
"@types/node": "^25.5.2"
|
|
32
|
+
},
|
|
33
|
+
"bin": {
|
|
34
|
+
"create-handler": "./dist/cli/create-handler.js"
|
|
35
|
+
}
|
|
36
|
+
}
|