@fhirfly-io/shl 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.
Files changed (51) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +118 -0
  3. package/dist/chunk-7WIM2QP5.js +133 -0
  4. package/dist/chunk-7WIM2QP5.js.map +1 -0
  5. package/dist/chunk-CNVYKA4D.cjs +135 -0
  6. package/dist/chunk-CNVYKA4D.cjs.map +1 -0
  7. package/dist/chunk-KI44MYPE.cjs +170 -0
  8. package/dist/chunk-KI44MYPE.cjs.map +1 -0
  9. package/dist/chunk-PZ5AY32C.js +9 -0
  10. package/dist/chunk-PZ5AY32C.js.map +1 -0
  11. package/dist/chunk-Q7SFCCGT.cjs +11 -0
  12. package/dist/chunk-Q7SFCCGT.cjs.map +1 -0
  13. package/dist/chunk-XTLU6O32.js +163 -0
  14. package/dist/chunk-XTLU6O32.js.map +1 -0
  15. package/dist/express.cjs +37 -0
  16. package/dist/express.cjs.map +1 -0
  17. package/dist/express.d.cts +39 -0
  18. package/dist/express.d.ts +39 -0
  19. package/dist/express.js +35 -0
  20. package/dist/express.js.map +1 -0
  21. package/dist/fastify.cjs +49 -0
  22. package/dist/fastify.cjs.map +1 -0
  23. package/dist/fastify.d.cts +43 -0
  24. package/dist/fastify.d.ts +43 -0
  25. package/dist/fastify.js +47 -0
  26. package/dist/fastify.js.map +1 -0
  27. package/dist/index.cjs +1615 -0
  28. package/dist/index.cjs.map +1 -0
  29. package/dist/index.d.cts +765 -0
  30. package/dist/index.d.ts +765 -0
  31. package/dist/index.js +1593 -0
  32. package/dist/index.js.map +1 -0
  33. package/dist/lambda.cjs +64 -0
  34. package/dist/lambda.cjs.map +1 -0
  35. package/dist/lambda.d.cts +53 -0
  36. package/dist/lambda.d.ts +53 -0
  37. package/dist/lambda.js +62 -0
  38. package/dist/lambda.js.map +1 -0
  39. package/dist/server.cjs +166 -0
  40. package/dist/server.cjs.map +1 -0
  41. package/dist/server.d.cts +85 -0
  42. package/dist/server.d.ts +85 -0
  43. package/dist/server.js +159 -0
  44. package/dist/server.js.map +1 -0
  45. package/dist/storage-CHi9vLD_.d.cts +82 -0
  46. package/dist/storage-iI_tyHcX.d.ts +82 -0
  47. package/dist/types--f4ITgu9.d.cts +73 -0
  48. package/dist/types-DKtPO4DP.d.ts +73 -0
  49. package/dist/types-yk0mDByJ.d.cts +96 -0
  50. package/dist/types-yk0mDByJ.d.ts +96 -0
  51. package/package.json +137 -0
@@ -0,0 +1,82 @@
1
+ import { b as SHLStorage } from './types-yk0mDByJ.js';
2
+
3
+ /**
4
+ * Configuration for local filesystem SHL storage.
5
+ */
6
+ interface LocalStorageConfig {
7
+ /** Directory path for storing SHL files */
8
+ directory: string;
9
+ /** Base URL for serving the files (trailing slashes are stripped) */
10
+ baseUrl: string;
11
+ }
12
+ /**
13
+ * Local filesystem storage for SMART Health Links.
14
+ * Useful for development and testing.
15
+ *
16
+ * Files are written to `{directory}/{key}`. The user's server
17
+ * maps `{baseUrl}/{shlId}` to reads from this directory.
18
+ *
19
+ * @example
20
+ * ```ts
21
+ * const storage = new SHL.LocalStorage({
22
+ * directory: "./shl-data",
23
+ * baseUrl: "http://localhost:3000/shl",
24
+ * });
25
+ * ```
26
+ */
27
+ declare class LocalStorage implements SHLStorage {
28
+ private readonly _config;
29
+ constructor(config: LocalStorageConfig);
30
+ /** Returns the storage configuration. */
31
+ get config(): LocalStorageConfig;
32
+ /** Base URL with trailing slashes stripped. */
33
+ get baseUrl(): string;
34
+ store(key: string, content: string | Uint8Array): Promise<void>;
35
+ delete(prefix: string): Promise<void>;
36
+ }
37
+ /**
38
+ * Configuration for S3-based SHL storage.
39
+ */
40
+ interface S3StorageConfig {
41
+ /** S3 bucket name */
42
+ bucket: string;
43
+ /** AWS region */
44
+ region: string;
45
+ /** Optional key prefix */
46
+ prefix?: string;
47
+ /** Base URL for serving the files */
48
+ baseUrl: string;
49
+ }
50
+ /**
51
+ * S3-backed storage for SMART Health Links.
52
+ *
53
+ * Requires `@aws-sdk/client-s3` as a peer dependency — install it separately:
54
+ * ```
55
+ * npm install @aws-sdk/client-s3
56
+ * ```
57
+ *
58
+ * @example
59
+ * ```ts
60
+ * const storage = new SHL.S3Storage({
61
+ * bucket: "my-shl-bucket",
62
+ * region: "us-east-1",
63
+ * baseUrl: "https://shl.example.com",
64
+ * });
65
+ * ```
66
+ */
67
+ declare class S3Storage implements SHLStorage {
68
+ private readonly _config;
69
+ private _client?;
70
+ constructor(config: S3StorageConfig);
71
+ /** Returns the storage configuration. */
72
+ get config(): S3StorageConfig;
73
+ /** Base URL with trailing slashes stripped. */
74
+ get baseUrl(): string;
75
+ store(key: string, content: string | Uint8Array): Promise<void>;
76
+ delete(prefix: string): Promise<void>;
77
+ private _getClient;
78
+ private _s3Key;
79
+ private _contentType;
80
+ }
81
+
82
+ export { LocalStorage as L, S3Storage as S, type LocalStorageConfig as a, type S3StorageConfig as b };
@@ -0,0 +1,73 @@
1
+ import { b as SHLStorage, e as SHLMetadata } from './types-yk0mDByJ.cjs';
2
+
3
+ /**
4
+ * Framework-agnostic incoming request.
5
+ */
6
+ interface HandlerRequest {
7
+ /** HTTP method (uppercase) */
8
+ method: string;
9
+ /** Path relative to mount point, e.g., "/{shlId}" or "/{shlId}/content" */
10
+ path: string;
11
+ /** Parsed JSON body (for POST requests) */
12
+ body?: unknown;
13
+ /** Request headers (lowercase keys) */
14
+ headers: Record<string, string | undefined>;
15
+ }
16
+ /**
17
+ * Framework-agnostic outgoing response.
18
+ */
19
+ interface HandlerResponse {
20
+ /** HTTP status code */
21
+ status: number;
22
+ /** Response headers */
23
+ headers: Record<string, string>;
24
+ /** Response body (string for JSON, Uint8Array for binary) */
25
+ body: string | Uint8Array;
26
+ }
27
+ /**
28
+ * Extended storage interface for server-side operations.
29
+ *
30
+ * Adds `read` and `updateMetadata` to the base `SHLStorage` interface.
31
+ * Server storage needs to read files and atomically update metadata
32
+ * (e.g., increment access counts).
33
+ */
34
+ interface SHLServerStorage extends SHLStorage {
35
+ /** Read a file by key. Returns null if not found. */
36
+ read(key: string): Promise<string | Uint8Array | null>;
37
+ /**
38
+ * Atomically read-modify-write metadata for an SHL.
39
+ *
40
+ * The `updater` function receives the current metadata and returns
41
+ * the updated metadata (or `null` to signal no update should occur).
42
+ *
43
+ * @param shlId - The SHL identifier
44
+ * @param updater - Function that transforms metadata
45
+ * @returns The updated metadata, or null if the SHL was not found or updater returned null
46
+ */
47
+ updateMetadata(shlId: string, updater: (current: SHLMetadata) => SHLMetadata | null): Promise<SHLMetadata | null>;
48
+ }
49
+ /**
50
+ * Configuration for the SHL server handler.
51
+ */
52
+ interface SHLHandlerConfig {
53
+ /** Server storage backend (must implement SHLServerStorage) */
54
+ storage: SHLServerStorage;
55
+ /**
56
+ * Optional callback invoked on each successful manifest access.
57
+ * Useful for logging, analytics, or custom access control.
58
+ */
59
+ onAccess?: (event: AccessEvent) => void | Promise<void>;
60
+ }
61
+ /**
62
+ * Event emitted on each successful manifest access.
63
+ */
64
+ interface AccessEvent {
65
+ /** The SHL identifier */
66
+ shlId: string;
67
+ /** Current access count (after increment) */
68
+ accessCount: number;
69
+ /** Timestamp of the access */
70
+ timestamp: Date;
71
+ }
72
+
73
+ export type { AccessEvent as A, HandlerRequest as H, SHLHandlerConfig as S, HandlerResponse as a, SHLServerStorage as b };
@@ -0,0 +1,73 @@
1
+ import { b as SHLStorage, e as SHLMetadata } from './types-yk0mDByJ.js';
2
+
3
+ /**
4
+ * Framework-agnostic incoming request.
5
+ */
6
+ interface HandlerRequest {
7
+ /** HTTP method (uppercase) */
8
+ method: string;
9
+ /** Path relative to mount point, e.g., "/{shlId}" or "/{shlId}/content" */
10
+ path: string;
11
+ /** Parsed JSON body (for POST requests) */
12
+ body?: unknown;
13
+ /** Request headers (lowercase keys) */
14
+ headers: Record<string, string | undefined>;
15
+ }
16
+ /**
17
+ * Framework-agnostic outgoing response.
18
+ */
19
+ interface HandlerResponse {
20
+ /** HTTP status code */
21
+ status: number;
22
+ /** Response headers */
23
+ headers: Record<string, string>;
24
+ /** Response body (string for JSON, Uint8Array for binary) */
25
+ body: string | Uint8Array;
26
+ }
27
+ /**
28
+ * Extended storage interface for server-side operations.
29
+ *
30
+ * Adds `read` and `updateMetadata` to the base `SHLStorage` interface.
31
+ * Server storage needs to read files and atomically update metadata
32
+ * (e.g., increment access counts).
33
+ */
34
+ interface SHLServerStorage extends SHLStorage {
35
+ /** Read a file by key. Returns null if not found. */
36
+ read(key: string): Promise<string | Uint8Array | null>;
37
+ /**
38
+ * Atomically read-modify-write metadata for an SHL.
39
+ *
40
+ * The `updater` function receives the current metadata and returns
41
+ * the updated metadata (or `null` to signal no update should occur).
42
+ *
43
+ * @param shlId - The SHL identifier
44
+ * @param updater - Function that transforms metadata
45
+ * @returns The updated metadata, or null if the SHL was not found or updater returned null
46
+ */
47
+ updateMetadata(shlId: string, updater: (current: SHLMetadata) => SHLMetadata | null): Promise<SHLMetadata | null>;
48
+ }
49
+ /**
50
+ * Configuration for the SHL server handler.
51
+ */
52
+ interface SHLHandlerConfig {
53
+ /** Server storage backend (must implement SHLServerStorage) */
54
+ storage: SHLServerStorage;
55
+ /**
56
+ * Optional callback invoked on each successful manifest access.
57
+ * Useful for logging, analytics, or custom access control.
58
+ */
59
+ onAccess?: (event: AccessEvent) => void | Promise<void>;
60
+ }
61
+ /**
62
+ * Event emitted on each successful manifest access.
63
+ */
64
+ interface AccessEvent {
65
+ /** The SHL identifier */
66
+ shlId: string;
67
+ /** Current access count (after increment) */
68
+ accessCount: number;
69
+ /** Timestamp of the access */
70
+ timestamp: Date;
71
+ }
72
+
73
+ export type { AccessEvent as A, HandlerRequest as H, SHLHandlerConfig as S, HandlerResponse as a, SHLServerStorage as b };
@@ -0,0 +1,96 @@
1
+ /**
2
+ * Options for creating a SMART Health Link.
3
+ */
4
+ interface SHLOptions {
5
+ /** The FHIR Bundle to share (as a JSON object) */
6
+ bundle: Record<string, unknown>;
7
+ /** Additional files to include (e.g., PDF documents, SMART Health Cards) */
8
+ attachments?: SHLAttachment[];
9
+ /** Optional passcode to protect the link */
10
+ passcode?: string;
11
+ /** Expiration date for the link */
12
+ expiresAt?: Date;
13
+ /** Maximum number of times the link can be accessed */
14
+ maxAccesses?: number;
15
+ /** Label for the SHL (shown in viewer apps, max 80 chars) */
16
+ label?: string;
17
+ /** Storage backend to use */
18
+ storage: SHLStorage;
19
+ /** Save unencrypted bundle alongside encrypted JWE (development only — do not use in production) */
20
+ debug?: boolean;
21
+ }
22
+ /**
23
+ * An additional file to include in the SHL manifest.
24
+ */
25
+ interface SHLAttachment {
26
+ /** MIME content type (e.g., "application/pdf") */
27
+ contentType: string;
28
+ /** File content — string for text, Uint8Array for binary */
29
+ content: string | Uint8Array;
30
+ }
31
+ /**
32
+ * Result of creating a SMART Health Link.
33
+ */
34
+ interface SHLResult {
35
+ /** The full SHL URL (shlink:/ protocol) */
36
+ url: string;
37
+ /** QR code as a PNG data URI (data:image/png;base64,...) */
38
+ qrCode: string;
39
+ /** The passcode (if one was set) */
40
+ passcode?: string;
41
+ /** Unique identifier for the SHL */
42
+ id: string;
43
+ /** When the link expires */
44
+ expiresAt?: Date;
45
+ /** Path to the unencrypted bundle (only set when debug mode is enabled) */
46
+ debugBundlePath?: string;
47
+ }
48
+ /**
49
+ * SHL manifest file entry.
50
+ */
51
+ interface ManifestEntry {
52
+ /** MIME type of the content (e.g., "application/fhir+json", "application/pdf") */
53
+ contentType: string;
54
+ /** URL to retrieve the content */
55
+ location?: string;
56
+ /** Embedded content (base64url-encoded if encrypted) */
57
+ embedded?: string;
58
+ }
59
+ /**
60
+ * SHL manifest file.
61
+ */
62
+ interface Manifest {
63
+ /** Array of files available via this SHL */
64
+ files: ManifestEntry[];
65
+ }
66
+ /**
67
+ * Metadata stored alongside an SHL for access control.
68
+ */
69
+ interface SHLMetadata {
70
+ /** Passcode required to access the link */
71
+ passcode?: string;
72
+ /** Maximum number of times the link can be accessed */
73
+ maxAccesses?: number;
74
+ /** Number of times the link has been accessed */
75
+ accessCount?: number;
76
+ /** ISO 8601 expiration date */
77
+ expiresAt?: string;
78
+ /** ISO 8601 creation date */
79
+ createdAt: string;
80
+ }
81
+ /**
82
+ * Interface for SHL storage backends.
83
+ *
84
+ * Storage backends write files to a location (filesystem, S3, etc.).
85
+ * The user configures their own server to serve these files via `baseUrl`.
86
+ */
87
+ interface SHLStorage {
88
+ /** Base URL of the user's SHL server. Used to compute manifest and content URLs. */
89
+ readonly baseUrl: string;
90
+ /** Store content at the given key path. */
91
+ store(key: string, content: string | Uint8Array): Promise<void>;
92
+ /** Delete all files for an SHL by prefix. */
93
+ delete(prefix: string): Promise<void>;
94
+ }
95
+
96
+ export type { Manifest as M, SHLOptions as S, SHLResult as a, SHLStorage as b, ManifestEntry as c, SHLAttachment as d, SHLMetadata as e };
@@ -0,0 +1,96 @@
1
+ /**
2
+ * Options for creating a SMART Health Link.
3
+ */
4
+ interface SHLOptions {
5
+ /** The FHIR Bundle to share (as a JSON object) */
6
+ bundle: Record<string, unknown>;
7
+ /** Additional files to include (e.g., PDF documents, SMART Health Cards) */
8
+ attachments?: SHLAttachment[];
9
+ /** Optional passcode to protect the link */
10
+ passcode?: string;
11
+ /** Expiration date for the link */
12
+ expiresAt?: Date;
13
+ /** Maximum number of times the link can be accessed */
14
+ maxAccesses?: number;
15
+ /** Label for the SHL (shown in viewer apps, max 80 chars) */
16
+ label?: string;
17
+ /** Storage backend to use */
18
+ storage: SHLStorage;
19
+ /** Save unencrypted bundle alongside encrypted JWE (development only — do not use in production) */
20
+ debug?: boolean;
21
+ }
22
+ /**
23
+ * An additional file to include in the SHL manifest.
24
+ */
25
+ interface SHLAttachment {
26
+ /** MIME content type (e.g., "application/pdf") */
27
+ contentType: string;
28
+ /** File content — string for text, Uint8Array for binary */
29
+ content: string | Uint8Array;
30
+ }
31
+ /**
32
+ * Result of creating a SMART Health Link.
33
+ */
34
+ interface SHLResult {
35
+ /** The full SHL URL (shlink:/ protocol) */
36
+ url: string;
37
+ /** QR code as a PNG data URI (data:image/png;base64,...) */
38
+ qrCode: string;
39
+ /** The passcode (if one was set) */
40
+ passcode?: string;
41
+ /** Unique identifier for the SHL */
42
+ id: string;
43
+ /** When the link expires */
44
+ expiresAt?: Date;
45
+ /** Path to the unencrypted bundle (only set when debug mode is enabled) */
46
+ debugBundlePath?: string;
47
+ }
48
+ /**
49
+ * SHL manifest file entry.
50
+ */
51
+ interface ManifestEntry {
52
+ /** MIME type of the content (e.g., "application/fhir+json", "application/pdf") */
53
+ contentType: string;
54
+ /** URL to retrieve the content */
55
+ location?: string;
56
+ /** Embedded content (base64url-encoded if encrypted) */
57
+ embedded?: string;
58
+ }
59
+ /**
60
+ * SHL manifest file.
61
+ */
62
+ interface Manifest {
63
+ /** Array of files available via this SHL */
64
+ files: ManifestEntry[];
65
+ }
66
+ /**
67
+ * Metadata stored alongside an SHL for access control.
68
+ */
69
+ interface SHLMetadata {
70
+ /** Passcode required to access the link */
71
+ passcode?: string;
72
+ /** Maximum number of times the link can be accessed */
73
+ maxAccesses?: number;
74
+ /** Number of times the link has been accessed */
75
+ accessCount?: number;
76
+ /** ISO 8601 expiration date */
77
+ expiresAt?: string;
78
+ /** ISO 8601 creation date */
79
+ createdAt: string;
80
+ }
81
+ /**
82
+ * Interface for SHL storage backends.
83
+ *
84
+ * Storage backends write files to a location (filesystem, S3, etc.).
85
+ * The user configures their own server to serve these files via `baseUrl`.
86
+ */
87
+ interface SHLStorage {
88
+ /** Base URL of the user's SHL server. Used to compute manifest and content URLs. */
89
+ readonly baseUrl: string;
90
+ /** Store content at the given key path. */
91
+ store(key: string, content: string | Uint8Array): Promise<void>;
92
+ /** Delete all files for an SHL by prefix. */
93
+ delete(prefix: string): Promise<void>;
94
+ }
95
+
96
+ export type { Manifest as M, SHLOptions as S, SHLResult as a, SHLStorage as b, ManifestEntry as c, SHLAttachment as d, SHLMetadata as e };
package/package.json ADDED
@@ -0,0 +1,137 @@
1
+ {
2
+ "name": "@fhirfly-io/shl",
3
+ "version": "0.1.0",
4
+ "description": "Official FHIRfly SDK for SMART Health Links - IPS bundle creation and SHL sharing",
5
+ "author": "FHIRfly.io LLC <admin@fhirfly.io>",
6
+ "license": "MIT",
7
+ "type": "module",
8
+ "homepage": "https://fhirfly.io",
9
+ "repository": {
10
+ "type": "git",
11
+ "url": "git+https://github.com/FHIRfly-io/fhirfly-shl.git"
12
+ },
13
+ "bugs": {
14
+ "url": "https://github.com/FHIRfly-io/fhirfly-shl/issues"
15
+ },
16
+ "keywords": [
17
+ "fhirfly",
18
+ "healthcare",
19
+ "smart-health-links",
20
+ "shl",
21
+ "ips",
22
+ "fhir",
23
+ "patient-summary",
24
+ "health-records",
25
+ "qr-code",
26
+ "clinical",
27
+ "middleware",
28
+ "express",
29
+ "fastify",
30
+ "lambda"
31
+ ],
32
+ "main": "./dist/index.cjs",
33
+ "module": "./dist/index.js",
34
+ "types": "./dist/index.d.ts",
35
+ "exports": {
36
+ ".": {
37
+ "require": {
38
+ "types": "./dist/index.d.cts",
39
+ "default": "./dist/index.cjs"
40
+ },
41
+ "import": {
42
+ "types": "./dist/index.d.ts",
43
+ "default": "./dist/index.js"
44
+ }
45
+ },
46
+ "./server": {
47
+ "require": {
48
+ "types": "./dist/server.d.cts",
49
+ "default": "./dist/server.cjs"
50
+ },
51
+ "import": {
52
+ "types": "./dist/server.d.ts",
53
+ "default": "./dist/server.js"
54
+ }
55
+ },
56
+ "./express": {
57
+ "require": {
58
+ "types": "./dist/express.d.cts",
59
+ "default": "./dist/express.cjs"
60
+ },
61
+ "import": {
62
+ "types": "./dist/express.d.ts",
63
+ "default": "./dist/express.js"
64
+ }
65
+ },
66
+ "./fastify": {
67
+ "require": {
68
+ "types": "./dist/fastify.d.cts",
69
+ "default": "./dist/fastify.cjs"
70
+ },
71
+ "import": {
72
+ "types": "./dist/fastify.d.ts",
73
+ "default": "./dist/fastify.js"
74
+ }
75
+ },
76
+ "./lambda": {
77
+ "require": {
78
+ "types": "./dist/lambda.d.cts",
79
+ "default": "./dist/lambda.cjs"
80
+ },
81
+ "import": {
82
+ "types": "./dist/lambda.d.ts",
83
+ "default": "./dist/lambda.js"
84
+ }
85
+ }
86
+ },
87
+ "files": [
88
+ "dist",
89
+ "README.md",
90
+ "LICENSE"
91
+ ],
92
+ "engines": {
93
+ "node": ">=18.0.0"
94
+ },
95
+ "scripts": {
96
+ "build": "tsup",
97
+ "dev": "tsup --watch",
98
+ "typecheck": "tsc --noEmit",
99
+ "test": "vitest run",
100
+ "test:watch": "vitest",
101
+ "lint": "eslint src",
102
+ "test:validate:setup": "bash tests/fhir-validate/setup-validator.sh",
103
+ "test:validate": "npm run test:validate:setup && vitest run tests/fhir-validate/",
104
+ "prepublishOnly": "npm run build"
105
+ },
106
+ "dependencies": {
107
+ "qrcode": "^1.5.0"
108
+ },
109
+ "peerDependencies": {
110
+ "@aws-sdk/client-s3": "^3.0.0",
111
+ "express": "^4.0.0 || ^5.0.0",
112
+ "fastify": "^4.0.0 || ^5.0.0"
113
+ },
114
+ "peerDependenciesMeta": {
115
+ "@aws-sdk/client-s3": {
116
+ "optional": true
117
+ },
118
+ "express": {
119
+ "optional": true
120
+ },
121
+ "fastify": {
122
+ "optional": true
123
+ }
124
+ },
125
+ "devDependencies": {
126
+ "@eslint/js": "^9.0.0",
127
+ "@fhirfly-io/terminology": "^0.5.0",
128
+ "@types/node": "^22.0.0",
129
+ "@types/qrcode": "^1.5.0",
130
+ "eslint": "^9.0.0",
131
+ "tsup": "^8.5.0",
132
+ "tsx": "^4.21.0",
133
+ "typescript": "^5.7.0",
134
+ "typescript-eslint": "^8.0.0",
135
+ "vitest": "^4.0.0"
136
+ }
137
+ }