@the-cascade-protocol/sdk 1.0.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 (131) hide show
  1. package/LICENSE +190 -0
  2. package/README.md +164 -0
  3. package/dist/consent/consent-filter.d.ts +21 -0
  4. package/dist/consent/consent-filter.d.ts.map +1 -0
  5. package/dist/consent/consent-filter.js +71 -0
  6. package/dist/consent/consent-filter.js.map +1 -0
  7. package/dist/consent/index.d.ts +2 -0
  8. package/dist/consent/index.d.ts.map +1 -0
  9. package/dist/consent/index.js +2 -0
  10. package/dist/consent/index.js.map +1 -0
  11. package/dist/deserializer/index.d.ts +7 -0
  12. package/dist/deserializer/index.d.ts.map +1 -0
  13. package/dist/deserializer/index.js +7 -0
  14. package/dist/deserializer/index.js.map +1 -0
  15. package/dist/deserializer/turtle-parser.d.ts +47 -0
  16. package/dist/deserializer/turtle-parser.d.ts.map +1 -0
  17. package/dist/deserializer/turtle-parser.js +840 -0
  18. package/dist/deserializer/turtle-parser.js.map +1 -0
  19. package/dist/index.d.ts +42 -0
  20. package/dist/index.d.ts.map +1 -0
  21. package/dist/index.js +34 -0
  22. package/dist/index.js.map +1 -0
  23. package/dist/jsonld/context.d.ts +29 -0
  24. package/dist/jsonld/context.d.ts.map +1 -0
  25. package/dist/jsonld/context.js +95 -0
  26. package/dist/jsonld/context.js.map +1 -0
  27. package/dist/jsonld/converter.d.ts +50 -0
  28. package/dist/jsonld/converter.d.ts.map +1 -0
  29. package/dist/jsonld/converter.js +139 -0
  30. package/dist/jsonld/converter.js.map +1 -0
  31. package/dist/jsonld/index.d.ts +8 -0
  32. package/dist/jsonld/index.d.ts.map +1 -0
  33. package/dist/jsonld/index.js +8 -0
  34. package/dist/jsonld/index.js.map +1 -0
  35. package/dist/models/activity-snapshot.d.ts +49 -0
  36. package/dist/models/activity-snapshot.d.ts.map +1 -0
  37. package/dist/models/activity-snapshot.js +13 -0
  38. package/dist/models/activity-snapshot.js.map +1 -0
  39. package/dist/models/allergy.d.ts +49 -0
  40. package/dist/models/allergy.d.ts.map +1 -0
  41. package/dist/models/allergy.js +13 -0
  42. package/dist/models/allergy.js.map +1 -0
  43. package/dist/models/common.d.ts +174 -0
  44. package/dist/models/common.d.ts.map +1 -0
  45. package/dist/models/common.js +12 -0
  46. package/dist/models/common.js.map +1 -0
  47. package/dist/models/condition.d.ts +59 -0
  48. package/dist/models/condition.d.ts.map +1 -0
  49. package/dist/models/condition.js +13 -0
  50. package/dist/models/condition.js.map +1 -0
  51. package/dist/models/coverage.d.ts +117 -0
  52. package/dist/models/coverage.d.ts.map +1 -0
  53. package/dist/models/coverage.js +16 -0
  54. package/dist/models/coverage.js.map +1 -0
  55. package/dist/models/family-history.d.ts +38 -0
  56. package/dist/models/family-history.d.ts.map +1 -0
  57. package/dist/models/family-history.js +13 -0
  58. package/dist/models/family-history.js.map +1 -0
  59. package/dist/models/health-profile.d.ts +54 -0
  60. package/dist/models/health-profile.d.ts.map +1 -0
  61. package/dist/models/health-profile.js +11 -0
  62. package/dist/models/health-profile.js.map +1 -0
  63. package/dist/models/immunization.d.ts +78 -0
  64. package/dist/models/immunization.d.ts.map +1 -0
  65. package/dist/models/immunization.js +12 -0
  66. package/dist/models/immunization.js.map +1 -0
  67. package/dist/models/index.d.ts +20 -0
  68. package/dist/models/index.d.ts.map +1 -0
  69. package/dist/models/index.js +7 -0
  70. package/dist/models/index.js.map +1 -0
  71. package/dist/models/lab-result.d.ts +83 -0
  72. package/dist/models/lab-result.d.ts.map +1 -0
  73. package/dist/models/lab-result.js +12 -0
  74. package/dist/models/lab-result.js.map +1 -0
  75. package/dist/models/medication.d.ts +144 -0
  76. package/dist/models/medication.d.ts.map +1 -0
  77. package/dist/models/medication.js +13 -0
  78. package/dist/models/medication.js.map +1 -0
  79. package/dist/models/patient-profile.d.ts +171 -0
  80. package/dist/models/patient-profile.d.ts.map +1 -0
  81. package/dist/models/patient-profile.js +14 -0
  82. package/dist/models/patient-profile.js.map +1 -0
  83. package/dist/models/procedure.d.ts +53 -0
  84. package/dist/models/procedure.d.ts.map +1 -0
  85. package/dist/models/procedure.js +12 -0
  86. package/dist/models/procedure.js.map +1 -0
  87. package/dist/models/sleep-snapshot.d.ts +54 -0
  88. package/dist/models/sleep-snapshot.d.ts.map +1 -0
  89. package/dist/models/sleep-snapshot.js +13 -0
  90. package/dist/models/sleep-snapshot.js.map +1 -0
  91. package/dist/models/vital-sign.d.ts +74 -0
  92. package/dist/models/vital-sign.d.ts.map +1 -0
  93. package/dist/models/vital-sign.js +13 -0
  94. package/dist/models/vital-sign.js.map +1 -0
  95. package/dist/pod/index.d.ts +2 -0
  96. package/dist/pod/index.d.ts.map +1 -0
  97. package/dist/pod/index.js +2 -0
  98. package/dist/pod/index.js.map +1 -0
  99. package/dist/pod/pod-builder.d.ts +63 -0
  100. package/dist/pod/pod-builder.d.ts.map +1 -0
  101. package/dist/pod/pod-builder.js +245 -0
  102. package/dist/pod/pod-builder.js.map +1 -0
  103. package/dist/serializer/index.d.ts +8 -0
  104. package/dist/serializer/index.d.ts.map +1 -0
  105. package/dist/serializer/index.js +8 -0
  106. package/dist/serializer/index.js.map +1 -0
  107. package/dist/serializer/turtle-builder.d.ts +93 -0
  108. package/dist/serializer/turtle-builder.d.ts.map +1 -0
  109. package/dist/serializer/turtle-builder.js +204 -0
  110. package/dist/serializer/turtle-builder.js.map +1 -0
  111. package/dist/serializer/turtle-serializer.d.ts +66 -0
  112. package/dist/serializer/turtle-serializer.d.ts.map +1 -0
  113. package/dist/serializer/turtle-serializer.js +404 -0
  114. package/dist/serializer/turtle-serializer.js.map +1 -0
  115. package/dist/validator/index.d.ts +2 -0
  116. package/dist/validator/index.d.ts.map +1 -0
  117. package/dist/validator/index.js +2 -0
  118. package/dist/validator/index.js.map +1 -0
  119. package/dist/validator/validator.d.ts +16 -0
  120. package/dist/validator/validator.d.ts.map +1 -0
  121. package/dist/validator/validator.js +295 -0
  122. package/dist/validator/validator.js.map +1 -0
  123. package/dist/vocabularies/index.d.ts +8 -0
  124. package/dist/vocabularies/index.d.ts.map +1 -0
  125. package/dist/vocabularies/index.js +7 -0
  126. package/dist/vocabularies/index.js.map +1 -0
  127. package/dist/vocabularies/namespaces.d.ts +125 -0
  128. package/dist/vocabularies/namespaces.d.ts.map +1 -0
  129. package/dist/vocabularies/namespaces.js +361 -0
  130. package/dist/vocabularies/namespaces.js.map +1 -0
  131. package/package.json +48 -0
@@ -0,0 +1,204 @@
1
+ /**
2
+ * Fluent builder API for constructing Turtle (Terse RDF Triple Language) documents.
3
+ *
4
+ * Provides a class-based API for building well-formed Turtle output with
5
+ * proper prefix declarations, typed literals, URI references, blank nodes,
6
+ * and RDF lists.
7
+ *
8
+ * @example
9
+ * ```typescript
10
+ * import { TurtleBuilder } from '@the-cascade-protocol/sdk';
11
+ *
12
+ * const turtle = new TurtleBuilder()
13
+ * .prefix('cascade', 'https://ns.cascadeprotocol.org/core/v1#')
14
+ * .prefix('health', 'https://ns.cascadeprotocol.org/health/v1#')
15
+ * .subject('<urn:uuid:abc>')
16
+ * .type('health:MedicationRecord')
17
+ * .literal('health:medicationName', 'Lisinopril')
18
+ * .boolean('health:isActive', true)
19
+ * .done()
20
+ * .build();
21
+ * ```
22
+ *
23
+ * @module serializer
24
+ */
25
+ // ─── String Escaping ────────────────────────────────────────────────────────
26
+ /**
27
+ * Escape a string value for use in a Turtle literal.
28
+ *
29
+ * Handles backslashes, double quotes, newlines, carriage returns, and tabs.
30
+ * For very long strings (> 200 chars) or strings containing embedded newlines,
31
+ * uses triple-quoted long literals.
32
+ */
33
+ export function escapeTurtleString(value) {
34
+ // Use triple-quoted long literal for very long strings or strings with embedded newlines
35
+ if (value.length > 200 || value.includes('\n')) {
36
+ const longEscaped = value
37
+ .replace(/\\/g, '\\\\')
38
+ .replace(/"""/g, '\\"\\"\\"');
39
+ return `"""${longEscaped}"""`;
40
+ }
41
+ const escaped = value
42
+ .replace(/\\/g, '\\\\')
43
+ .replace(/"/g, '\\"')
44
+ .replace(/\n/g, '\\n')
45
+ .replace(/\r/g, '\\r')
46
+ .replace(/\t/g, '\\t');
47
+ return `"${escaped}"`;
48
+ }
49
+ // ─── SubjectBuilder ─────────────────────────────────────────────────────────
50
+ /**
51
+ * Builder for adding predicate-object pairs to a single RDF subject.
52
+ *
53
+ * Obtained via `TurtleBuilder.subject()`. Call `.done()` to return to the
54
+ * parent `TurtleBuilder`.
55
+ */
56
+ export class SubjectBuilder {
57
+ _parent;
58
+ _subject;
59
+ _predicates = [];
60
+ /** @internal */
61
+ constructor(parent, subject) {
62
+ this._parent = parent;
63
+ this._subject = subject;
64
+ }
65
+ /** Add an `rdf:type` declaration. */
66
+ type(rdfType) {
67
+ this._predicates.push(`a ${rdfType}`);
68
+ return this;
69
+ }
70
+ /** Add a plain string literal. Optionally with a datatype IRI. */
71
+ literal(predicate, value, datatype) {
72
+ if (datatype) {
73
+ this._predicates.push(`${predicate} ${escapeTurtleString(value)}^^${datatype}`);
74
+ }
75
+ else {
76
+ this._predicates.push(`${predicate} ${escapeTurtleString(value)}`);
77
+ }
78
+ return this;
79
+ }
80
+ /** Add a URI reference (angle-bracket enclosed). */
81
+ uri(predicate, uriValue) {
82
+ // If already prefixed (e.g., cascade:ClinicalGenerated), use as-is
83
+ if (/^[a-zA-Z][\w-]*:[\w-]+$/.test(uriValue)) {
84
+ this._predicates.push(`${predicate} ${uriValue}`);
85
+ }
86
+ else {
87
+ this._predicates.push(`${predicate} <${uriValue}>`);
88
+ }
89
+ return this;
90
+ }
91
+ /** Add a boolean literal (unquoted `true` or `false`). */
92
+ boolean(predicate, value) {
93
+ this._predicates.push(`${predicate} ${value}`);
94
+ return this;
95
+ }
96
+ /** Add an integer literal with `^^xsd:integer` datatype. */
97
+ integer(predicate, value) {
98
+ this._predicates.push(`${predicate} "${value}"^^xsd:integer`);
99
+ return this;
100
+ }
101
+ /** Add a double/decimal literal with `^^xsd:double` datatype. */
102
+ double(predicate, value) {
103
+ this._predicates.push(`${predicate} "${value}"^^xsd:double`);
104
+ return this;
105
+ }
106
+ /** Add a plain numeric value (no datatype annotation, for integers). */
107
+ number(predicate, value) {
108
+ this._predicates.push(`${predicate} ${value}`);
109
+ return this;
110
+ }
111
+ /** Add a `^^xsd:dateTime` typed literal. */
112
+ dateTime(predicate, value) {
113
+ this._predicates.push(`${predicate} "${value}"^^xsd:dateTime`);
114
+ return this;
115
+ }
116
+ /** Add a `^^xsd:date` typed literal. */
117
+ date(predicate, value) {
118
+ this._predicates.push(`${predicate} "${value}"^^xsd:date`);
119
+ return this;
120
+ }
121
+ /** Add an RDF list (Turtle shorthand `( item1 item2 ... )`). Items are treated as string literals. */
122
+ list(predicate, items) {
123
+ const formatted = items.map((item) => escapeTurtleString(item)).join(' ');
124
+ this._predicates.push(`${predicate} ( ${formatted} )`);
125
+ return this;
126
+ }
127
+ /** Add a blank node with nested predicate-object pairs. */
128
+ blankNode(predicate, callback) {
129
+ const inner = new SubjectBuilder(this._parent, '');
130
+ callback(inner);
131
+ const innerLines = inner._predicates.map((p, i, arr) => {
132
+ const sep = i < arr.length - 1 ? ' ;' : '';
133
+ return ` ${p}${sep}`;
134
+ });
135
+ this._predicates.push(`${predicate} [\n${innerLines.join('\n')}\n ]`);
136
+ return this;
137
+ }
138
+ /** Finalize this subject block and return to the parent TurtleBuilder. */
139
+ done() {
140
+ this._parent._addSubjectBlock(this._subject, this._predicates);
141
+ return this._parent;
142
+ }
143
+ }
144
+ // ─── TurtleBuilder ──────────────────────────────────────────────────────────
145
+ /**
146
+ * Fluent builder for constructing complete Turtle documents.
147
+ *
148
+ * Usage:
149
+ * 1. Add prefix declarations with `.prefix()`
150
+ * 2. Add subject blocks with `.subject()` -> `SubjectBuilder` -> `.done()`
151
+ * 3. Call `.build()` to produce the final Turtle string
152
+ */
153
+ export class TurtleBuilder {
154
+ _prefixes = new Map();
155
+ _blocks = [];
156
+ /** Declare a namespace prefix. */
157
+ prefix(prefixName, uri) {
158
+ this._prefixes.set(prefixName, uri);
159
+ return this;
160
+ }
161
+ /** Begin a new subject block. Returns a SubjectBuilder for adding predicates. */
162
+ subject(uri) {
163
+ return new SubjectBuilder(this, uri);
164
+ }
165
+ /**
166
+ * @internal
167
+ * Called by SubjectBuilder.done() to register a completed subject block.
168
+ */
169
+ _addSubjectBlock(subject, predicates) {
170
+ if (predicates.length === 0)
171
+ return;
172
+ const lines = [];
173
+ if (predicates.length === 1) {
174
+ // Single predicate: subject and predicate on the same line
175
+ lines.push(`${subject} ${predicates[0]} .`);
176
+ }
177
+ else {
178
+ // First predicate on the same line as the subject
179
+ lines.push(`${subject} ${predicates[0]} ;`);
180
+ // Remaining predicates indented
181
+ for (let i = 1; i < predicates.length; i++) {
182
+ const isLast = i === predicates.length - 1;
183
+ lines.push(` ${predicates[i]}${isLast ? ' .' : ' ;'}`);
184
+ }
185
+ }
186
+ this._blocks.push(lines.join('\n'));
187
+ }
188
+ /** Build the complete Turtle document string. */
189
+ build() {
190
+ const parts = [];
191
+ // Prefix declarations
192
+ for (const [name, uri] of this._prefixes) {
193
+ parts.push(`@prefix ${name}: <${uri}> .`);
194
+ }
195
+ // Blank line between prefixes and content
196
+ if (this._prefixes.size > 0 && this._blocks.length > 0) {
197
+ parts.push('');
198
+ }
199
+ // Subject blocks
200
+ parts.push(...this._blocks);
201
+ return parts.join('\n') + '\n';
202
+ }
203
+ }
204
+ //# sourceMappingURL=turtle-builder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"turtle-builder.js","sourceRoot":"","sources":["../../src/serializer/turtle-builder.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,66 @@
1
+ /**
2
+ * High-level serialization functions for converting Cascade Protocol
3
+ * data model objects to Turtle (RDF) format.
4
+ *
5
+ * Uses the {@link TurtleBuilder} internally and produces output conforming
6
+ * to the Cascade Protocol conformance fixtures.
7
+ *
8
+ * @example
9
+ * ```typescript
10
+ * import { serialize, serializeMedication } from '@the-cascade-protocol/sdk';
11
+ *
12
+ * const turtle = serializeMedication(myMed);
13
+ * // or generically:
14
+ * const turtle2 = serialize(myRecord);
15
+ * ```
16
+ *
17
+ * @module serializer
18
+ */
19
+ import type { CascadeRecord } from '../models/common.js';
20
+ import type { Medication } from '../models/medication.js';
21
+ import type { Condition } from '../models/condition.js';
22
+ import type { Allergy } from '../models/allergy.js';
23
+ import type { LabResult } from '../models/lab-result.js';
24
+ import type { VitalSign } from '../models/vital-sign.js';
25
+ import type { Immunization } from '../models/immunization.js';
26
+ import type { Procedure } from '../models/procedure.js';
27
+ import type { FamilyHistory } from '../models/family-history.js';
28
+ import type { Coverage } from '../models/coverage.js';
29
+ import type { PatientProfile } from '../models/patient-profile.js';
30
+ import type { ActivitySnapshot } from '../models/activity-snapshot.js';
31
+ import type { SleepSnapshot } from '../models/sleep-snapshot.js';
32
+ /**
33
+ * Serialize any Cascade Protocol record to Turtle format.
34
+ *
35
+ * Dispatches based on the `type` field of the record. The output matches
36
+ * the conformance fixture expected Turtle format.
37
+ *
38
+ * @param record - Any CascadeRecord (Medication, Condition, VitalSign, etc.)
39
+ * @returns A complete Turtle document string
40
+ */
41
+ export declare function serialize(record: CascadeRecord): string;
42
+ /** Serialize a Medication record to Turtle. */
43
+ export declare function serializeMedication(med: Medication): string;
44
+ /** Serialize a Condition record to Turtle. */
45
+ export declare function serializeCondition(cond: Condition): string;
46
+ /** Serialize an Allergy record to Turtle. */
47
+ export declare function serializeAllergy(allergy: Allergy): string;
48
+ /** Serialize a LabResult record to Turtle. */
49
+ export declare function serializeLabResult(lab: LabResult): string;
50
+ /** Serialize a VitalSign record to Turtle. */
51
+ export declare function serializeVitalSign(vital: VitalSign): string;
52
+ /** Serialize an Immunization record to Turtle. */
53
+ export declare function serializeImmunization(imm: Immunization): string;
54
+ /** Serialize a Procedure record to Turtle. */
55
+ export declare function serializeProcedure(proc: Procedure): string;
56
+ /** Serialize a FamilyHistory record to Turtle. */
57
+ export declare function serializeFamilyHistory(fam: FamilyHistory): string;
58
+ /** Serialize a Coverage record to Turtle. */
59
+ export declare function serializeCoverage(cov: Coverage): string;
60
+ /** Serialize a PatientProfile record to Turtle. */
61
+ export declare function serializePatientProfile(profile: PatientProfile): string;
62
+ /** Serialize an ActivitySnapshot record to Turtle. */
63
+ export declare function serializeActivitySnapshot(activity: ActivitySnapshot): string;
64
+ /** Serialize a SleepSnapshot record to Turtle. */
65
+ export declare function serializeSleepSnapshot(sleep: SleepSnapshot): string;
66
+ //# sourceMappingURL=turtle-serializer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"turtle-serializer.d.ts","sourceRoot":"","sources":["../../src/serializer/turtle-serializer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAIH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACxD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AACpD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AACzD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AACzD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACxD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AACjE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACtD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AACnE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;AACvE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAwLjE;;;;;;;;GAQG;AACH,wBAAgB,SAAS,CAAC,MAAM,EAAE,aAAa,GAAG,MAAM,CAEvD;AAkLD,+CAA+C;AAC/C,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,UAAU,GAAG,MAAM,CAE3D;AAED,8CAA8C;AAC9C,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,SAAS,GAAG,MAAM,CAE1D;AAED,6CAA6C;AAC7C,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,CAEzD;AAED,8CAA8C;AAC9C,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,SAAS,GAAG,MAAM,CAEzD;AAED,8CAA8C;AAC9C,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,SAAS,GAAG,MAAM,CAE3D;AAED,kDAAkD;AAClD,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,YAAY,GAAG,MAAM,CAE/D;AAED,8CAA8C;AAC9C,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,SAAS,GAAG,MAAM,CAE1D;AAED,kDAAkD;AAClD,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,aAAa,GAAG,MAAM,CAEjE;AAED,6CAA6C;AAC7C,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,QAAQ,GAAG,MAAM,CAEvD;AAED,mDAAmD;AACnD,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,cAAc,GAAG,MAAM,CAEvE;AAED,sDAAsD;AACtD,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,gBAAgB,GAAG,MAAM,CAE5E;AAED,kDAAkD;AAClD,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,aAAa,GAAG,MAAM,CAEnE"}
@@ -0,0 +1,404 @@
1
+ /**
2
+ * High-level serialization functions for converting Cascade Protocol
3
+ * data model objects to Turtle (RDF) format.
4
+ *
5
+ * Uses the {@link TurtleBuilder} internally and produces output conforming
6
+ * to the Cascade Protocol conformance fixtures.
7
+ *
8
+ * @example
9
+ * ```typescript
10
+ * import { serialize, serializeMedication } from '@the-cascade-protocol/sdk';
11
+ *
12
+ * const turtle = serializeMedication(myMed);
13
+ * // or generically:
14
+ * const turtle2 = serialize(myRecord);
15
+ * ```
16
+ *
17
+ * @module serializer
18
+ */
19
+ import { TurtleBuilder } from './turtle-builder.js';
20
+ import { NAMESPACES, PROPERTY_PREDICATES, TYPE_MAPPING, TYPE_TO_MAPPING_KEY } from '../vocabularies/namespaces.js';
21
+ // ─── Internal Helpers ───────────────────────────────────────────────────────
22
+ /**
23
+ * Type-specific predicate overrides.
24
+ *
25
+ * When a JSON field name maps to different RDF predicates depending on the
26
+ * record type, these overrides take precedence over PROPERTY_PREDICATES.
27
+ *
28
+ * For example, `snomedCode` maps to `health:snomedCode` for Conditions
29
+ * but `clinical:snomedCode` for VitalSigns.
30
+ */
31
+ const TYPE_PREDICATE_OVERRIDES = {
32
+ VitalSign: {
33
+ snomedCode: 'clinical:snomedCode',
34
+ interpretation: 'clinical:interpretation',
35
+ },
36
+ };
37
+ /**
38
+ * Get the predicate for a given field and record type, respecting overrides.
39
+ */
40
+ function getPredicateForField(key, recordType) {
41
+ const overrides = TYPE_PREDICATE_OVERRIDES[recordType];
42
+ if (overrides && key in overrides) {
43
+ return overrides[key];
44
+ }
45
+ return PROPERTY_PREDICATES[key];
46
+ }
47
+ /**
48
+ * Fields whose values should be serialized as URI references (angle-bracket enclosed)
49
+ * rather than string literals, when the value looks like a full URI.
50
+ */
51
+ const URI_FIELDS = new Set([
52
+ 'rxNormCode',
53
+ 'icd10Code',
54
+ 'snomedCode',
55
+ 'loincCode',
56
+ 'testCode',
57
+ ]);
58
+ /**
59
+ * Fields whose values are arrays of URIs or strings and should be serialized
60
+ * as repeated predicate triples (one per value).
61
+ */
62
+ const ARRAY_FIELDS = new Set([
63
+ 'drugCodes',
64
+ 'affectsVitalSigns',
65
+ 'monitoredVitalSigns',
66
+ ]);
67
+ /**
68
+ * Explicit set of fields that are dateTime-typed even though their names
69
+ * don't contain "date" or "time" as a substring.
70
+ */
71
+ const EXPLICIT_DATETIME_FIELDS = new Set([
72
+ 'effectivePeriodStart',
73
+ 'effectivePeriodEnd',
74
+ 'effectiveStart',
75
+ 'effectiveEnd',
76
+ ]);
77
+ /**
78
+ * Fields whose values contain date/time and get ^^xsd:dateTime typing.
79
+ * We check the key name and a set of explicit fields.
80
+ */
81
+ function isDateTimeField(key) {
82
+ if (EXPLICIT_DATETIME_FIELDS.has(key))
83
+ return true;
84
+ const lower = key.toLowerCase();
85
+ // Specific date-only field
86
+ if (key === 'dateOfBirth' || key === 'date')
87
+ return false;
88
+ return lower.includes('date') || lower.includes('time');
89
+ }
90
+ /**
91
+ * Fields whose values are date-only (no time component) and get ^^xsd:date typing.
92
+ */
93
+ function isDateOnlyField(key) {
94
+ return key === 'dateOfBirth';
95
+ }
96
+ /**
97
+ * Fields that represent integers with ^^xsd:integer typing in the expected output.
98
+ */
99
+ const INTEGER_FIELDS = new Set([
100
+ 'computedAge',
101
+ 'refillsAllowed',
102
+ 'supplyDurationDays',
103
+ 'onsetAge',
104
+ ]);
105
+ /**
106
+ * Fields that are boolean and should be serialized unquoted.
107
+ */
108
+ function isBooleanField(_key, value) {
109
+ return typeof value === 'boolean';
110
+ }
111
+ /**
112
+ * Determine which prefixes are needed for a given record.
113
+ */
114
+ function collectPrefixes(record) {
115
+ const prefixes = new Map();
116
+ // Always include cascade and xsd
117
+ prefixes.set('cascade', NAMESPACES.cascade);
118
+ prefixes.set('xsd', NAMESPACES.xsd);
119
+ // Get the rdfType to determine base vocabulary
120
+ const mappingKey = TYPE_TO_MAPPING_KEY[record.type];
121
+ if (mappingKey) {
122
+ const mapping = TYPE_MAPPING[mappingKey];
123
+ if (mapping) {
124
+ const rdfType = mapping.rdfType;
125
+ const nsPrefix = rdfType.split(':')[0];
126
+ if (nsPrefix && nsPrefix in NAMESPACES) {
127
+ prefixes.set(nsPrefix, NAMESPACES[nsPrefix]);
128
+ }
129
+ }
130
+ }
131
+ // Scan all fields and add namespaces for predicates and URI values
132
+ for (const [key, value] of Object.entries(record)) {
133
+ if (key === 'id' || key === 'type' || value === undefined || value === null)
134
+ continue;
135
+ const pred = getPredicateForField(key, record.type);
136
+ if (pred) {
137
+ const nsPrefix = pred.split(':')[0];
138
+ if (nsPrefix && nsPrefix in NAMESPACES) {
139
+ prefixes.set(nsPrefix, NAMESPACES[nsPrefix]);
140
+ }
141
+ }
142
+ // Check URI values for namespace references
143
+ if (typeof value === 'string' && URI_FIELDS.has(key)) {
144
+ addPrefixForUri(value, prefixes);
145
+ }
146
+ if (Array.isArray(value) && ARRAY_FIELDS.has(key)) {
147
+ for (const item of value) {
148
+ if (typeof item === 'string' && item.startsWith('http')) {
149
+ addPrefixForUri(item, prefixes);
150
+ }
151
+ }
152
+ }
153
+ }
154
+ return prefixes;
155
+ }
156
+ function addPrefixForUri(uri, prefixes) {
157
+ for (const [prefix, ns] of Object.entries(NAMESPACES)) {
158
+ if (uri.startsWith(ns) || uri.startsWith(ns.replace(/#$/, '/'))) {
159
+ prefixes.set(prefix, ns);
160
+ break;
161
+ }
162
+ }
163
+ }
164
+ /**
165
+ * Determine the stable order for prefix declarations.
166
+ * The order follows: cascade, health, clinical, coverage, then
167
+ * external namespaces (rxnorm, sct, loinc, icd10, foaf), then xsd.
168
+ */
169
+ function sortedPrefixes(prefixes) {
170
+ const order = [
171
+ 'cascade', 'health', 'clinical', 'coverage', 'checkup', 'pots',
172
+ 'fhir', 'rxnorm', 'sct', 'loinc', 'icd10', 'ucum',
173
+ 'prov', 'foaf', 'ldp', 'dcterms', 'xsd',
174
+ ];
175
+ const entries = Array.from(prefixes.entries());
176
+ entries.sort((a, b) => {
177
+ const ai = order.indexOf(a[0]);
178
+ const bi = order.indexOf(b[0]);
179
+ const aIdx = ai >= 0 ? ai : order.length;
180
+ const bIdx = bi >= 0 ? bi : order.length;
181
+ return aIdx - bIdx;
182
+ });
183
+ return entries;
184
+ }
185
+ // ─── Generic Serializer ─────────────────────────────────────────────────────
186
+ /**
187
+ * Serialize any Cascade Protocol record to Turtle format.
188
+ *
189
+ * Dispatches based on the `type` field of the record. The output matches
190
+ * the conformance fixture expected Turtle format.
191
+ *
192
+ * @param record - Any CascadeRecord (Medication, Condition, VitalSign, etc.)
193
+ * @returns A complete Turtle document string
194
+ */
195
+ export function serialize(record) {
196
+ return serializeRecord(record);
197
+ }
198
+ /**
199
+ * Internal workhorse that serializes any record.
200
+ */
201
+ function serializeRecord(record) {
202
+ const mappingKey = TYPE_TO_MAPPING_KEY[record.type];
203
+ const mapping = mappingKey ? TYPE_MAPPING[mappingKey] : undefined;
204
+ if (!mapping) {
205
+ throw new Error(`Unknown record type: ${record.type}. No TYPE_MAPPING found.`);
206
+ }
207
+ const prefixes = collectPrefixes(record);
208
+ const builder = new TurtleBuilder();
209
+ // Add prefixes in stable order
210
+ for (const [name, uri] of sortedPrefixes(prefixes)) {
211
+ builder.prefix(name, uri);
212
+ }
213
+ // Build the subject
214
+ const subjectUri = record.id.startsWith('urn:') || record.id.startsWith('http')
215
+ ? `<${record.id}>`
216
+ : `<${record.id}>`;
217
+ const sub = builder.subject(subjectUri);
218
+ sub.type(mapping.rdfType);
219
+ // Serialize fields in a deterministic order:
220
+ // 1. The "name" field (primary identifier)
221
+ // 2. Required CascadeRecord fields (dataProvenance, schemaVersion)
222
+ // are placed after the type-specific required fields
223
+ // 3. All other fields in their natural object order
224
+ const rec = { ...record };
225
+ // Collect field entries, preserving the order they appear in the record,
226
+ // but ensuring a deterministic output that matches the conformance fixtures.
227
+ const fieldOrder = Object.keys(rec);
228
+ // Track fields we've already emitted
229
+ const emitted = new Set();
230
+ // Helper to emit a single field
231
+ const emitField = (key) => {
232
+ if (emitted.has(key))
233
+ return;
234
+ emitted.add(key);
235
+ const value = rec[key];
236
+ if (value === undefined || value === null)
237
+ return;
238
+ if (key === 'id' || key === 'type')
239
+ return;
240
+ const pred = getPredicateForField(key, record.type);
241
+ if (!pred)
242
+ return;
243
+ // dataProvenance is special: value is a prefixed name
244
+ if (key === 'dataProvenance') {
245
+ sub.uri(pred, `cascade:${String(value)}`);
246
+ return;
247
+ }
248
+ // Boolean fields
249
+ if (isBooleanField(key, value)) {
250
+ sub.boolean(pred, value);
251
+ return;
252
+ }
253
+ // Integer fields
254
+ if (INTEGER_FIELDS.has(key) && typeof value === 'number') {
255
+ sub.integer(pred, value);
256
+ return;
257
+ }
258
+ // Number fields (plain, untyped integers like clinical:value, referenceRangeLow, etc.)
259
+ if (typeof value === 'number') {
260
+ if (Number.isInteger(value)) {
261
+ sub.number(pred, value);
262
+ }
263
+ else {
264
+ sub.double(pred, value);
265
+ }
266
+ return;
267
+ }
268
+ // URI fields
269
+ if (URI_FIELDS.has(key) && typeof value === 'string') {
270
+ sub.uri(pred, value);
271
+ return;
272
+ }
273
+ // Array fields (repeated predicates for URI lists, RDF list for string lists)
274
+ if (Array.isArray(value) && ARRAY_FIELDS.has(key)) {
275
+ if (value.length === 0)
276
+ return;
277
+ // Check if items look like URIs
278
+ const isUriList = value.every((item) => typeof item === 'string' && item.startsWith('http'));
279
+ if (isUriList) {
280
+ for (const item of value) {
281
+ sub.uri(pred, item);
282
+ }
283
+ }
284
+ else {
285
+ sub.list(pred, value.map(String));
286
+ }
287
+ return;
288
+ }
289
+ // Date-only fields
290
+ if (isDateOnlyField(key) && typeof value === 'string') {
291
+ sub.date(pred, value);
292
+ return;
293
+ }
294
+ // DateTime fields
295
+ if (isDateTimeField(key) && typeof value === 'string') {
296
+ sub.dateTime(pred, value);
297
+ return;
298
+ }
299
+ // Nested objects (blank nodes) for PatientProfile
300
+ if (typeof value === 'object' && !Array.isArray(value)) {
301
+ serializeBlankNode(sub, pred, key, value);
302
+ return;
303
+ }
304
+ // Default: string literal
305
+ if (typeof value === 'string') {
306
+ sub.literal(pred, value);
307
+ return;
308
+ }
309
+ };
310
+ // Emit all fields in the order they appear in the object
311
+ for (const key of fieldOrder) {
312
+ emitField(key);
313
+ }
314
+ sub.done();
315
+ return builder.build();
316
+ }
317
+ /**
318
+ * Serialize a nested object as a Turtle blank node.
319
+ */
320
+ function serializeBlankNode(sub, predicate, key, obj) {
321
+ // Determine the blank node type based on the key
322
+ let bnodeType;
323
+ if (key === 'emergencyContact')
324
+ bnodeType = 'cascade:EmergencyContact';
325
+ else if (key === 'address')
326
+ bnodeType = 'cascade:Address';
327
+ else if (key === 'preferredPharmacy')
328
+ bnodeType = 'cascade:PharmacyInfo';
329
+ sub.blankNode(predicate, (b) => {
330
+ if (bnodeType) {
331
+ b.type(bnodeType);
332
+ }
333
+ for (const [k, v] of Object.entries(obj)) {
334
+ if (v === undefined || v === null)
335
+ continue;
336
+ // Look up predicate for nested field - try with 'cascade:' prefix for profile nested types
337
+ const nestedPred = `cascade:${k}`;
338
+ if (typeof v === 'string') {
339
+ b.literal(nestedPred, v);
340
+ }
341
+ else if (typeof v === 'boolean') {
342
+ b.boolean(nestedPred, v);
343
+ }
344
+ else if (typeof v === 'number') {
345
+ if (Number.isInteger(v)) {
346
+ b.number(nestedPred, v);
347
+ }
348
+ else {
349
+ b.double(nestedPred, v);
350
+ }
351
+ }
352
+ }
353
+ });
354
+ }
355
+ // ─── Type-Specific Serializers ──────────────────────────────────────────────
356
+ /** Serialize a Medication record to Turtle. */
357
+ export function serializeMedication(med) {
358
+ return serialize(med);
359
+ }
360
+ /** Serialize a Condition record to Turtle. */
361
+ export function serializeCondition(cond) {
362
+ return serialize(cond);
363
+ }
364
+ /** Serialize an Allergy record to Turtle. */
365
+ export function serializeAllergy(allergy) {
366
+ return serialize(allergy);
367
+ }
368
+ /** Serialize a LabResult record to Turtle. */
369
+ export function serializeLabResult(lab) {
370
+ return serialize(lab);
371
+ }
372
+ /** Serialize a VitalSign record to Turtle. */
373
+ export function serializeVitalSign(vital) {
374
+ return serialize(vital);
375
+ }
376
+ /** Serialize an Immunization record to Turtle. */
377
+ export function serializeImmunization(imm) {
378
+ return serialize(imm);
379
+ }
380
+ /** Serialize a Procedure record to Turtle. */
381
+ export function serializeProcedure(proc) {
382
+ return serialize(proc);
383
+ }
384
+ /** Serialize a FamilyHistory record to Turtle. */
385
+ export function serializeFamilyHistory(fam) {
386
+ return serialize(fam);
387
+ }
388
+ /** Serialize a Coverage record to Turtle. */
389
+ export function serializeCoverage(cov) {
390
+ return serialize(cov);
391
+ }
392
+ /** Serialize a PatientProfile record to Turtle. */
393
+ export function serializePatientProfile(profile) {
394
+ return serialize(profile);
395
+ }
396
+ /** Serialize an ActivitySnapshot record to Turtle. */
397
+ export function serializeActivitySnapshot(activity) {
398
+ return serialize(activity);
399
+ }
400
+ /** Serialize a SleepSnapshot record to Turtle. */
401
+ export function serializeSleepSnapshot(sleep) {
402
+ return serialize(sleep);
403
+ }
404
+ //# sourceMappingURL=turtle-serializer.js.map