@atomic-ehr/codegen 0.0.1-canary.20251008092936.b434b77 → 0.0.1-canary.20251008121245.8324bc2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/index.js +26 -26
- package/dist/index.d.ts +6 -6
- package/dist/index.js +160 -27
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -256,12 +256,6 @@ interface BindingTypeSchema {
|
|
|
256
256
|
dependencies?: Identifier[];
|
|
257
257
|
}
|
|
258
258
|
type Field = RegularField | ChoiceFieldDeclaration | ChoiceFieldInstance;
|
|
259
|
-
interface TypeschemaGeneratorOptions {
|
|
260
|
-
verbose?: boolean;
|
|
261
|
-
logger?: CodegenLogger;
|
|
262
|
-
treeshake?: string[];
|
|
263
|
-
manager?: ReturnType<typeof CanonicalManager> | null;
|
|
264
|
-
}
|
|
265
259
|
type TypeschemaParserOptions = {
|
|
266
260
|
format?: "auto" | "ndjson" | "json";
|
|
267
261
|
validate?: boolean;
|
|
@@ -301,6 +295,12 @@ type RichValueSet = Omit<ValueSet, "name" | "url"> & {
|
|
|
301
295
|
name?: Name;
|
|
302
296
|
url?: CanonicalUrl;
|
|
303
297
|
};
|
|
298
|
+
interface TypeschemaGeneratorOptions {
|
|
299
|
+
verbose?: boolean;
|
|
300
|
+
logger?: CodegenLogger;
|
|
301
|
+
treeshake?: string[];
|
|
302
|
+
manager?: ReturnType<typeof CanonicalManager> | null;
|
|
303
|
+
}
|
|
304
304
|
|
|
305
305
|
type Register = {
|
|
306
306
|
appendFs(fs: FHIRSchema): void;
|
package/dist/index.js
CHANGED
|
@@ -1409,6 +1409,18 @@ var enrichFHIRSchema = (schema, packageMeta) => {
|
|
|
1409
1409
|
base: schema.base
|
|
1410
1410
|
};
|
|
1411
1411
|
};
|
|
1412
|
+
var isFhirSchemaBased = (schema) => {
|
|
1413
|
+
return schema.identifier.kind !== "value-set";
|
|
1414
|
+
};
|
|
1415
|
+
var isSpecialization = (schema) => {
|
|
1416
|
+
return schema.identifier.kind === "resource" || schema.identifier.kind === "complex-type" || schema.identifier.kind === "logical";
|
|
1417
|
+
};
|
|
1418
|
+
var isProfile = (schema) => {
|
|
1419
|
+
return schema.identifier.kind === "profile";
|
|
1420
|
+
};
|
|
1421
|
+
var isNotChoiceFieldDeclaration = (field) => {
|
|
1422
|
+
return field.choices === void 0;
|
|
1423
|
+
};
|
|
1412
1424
|
function isBindingSchema(schema) {
|
|
1413
1425
|
return schema.identifier.kind === "binding";
|
|
1414
1426
|
}
|
|
@@ -5089,11 +5101,6 @@ var groupByPackages = (typeSchemas) => {
|
|
|
5089
5101
|
};
|
|
5090
5102
|
var collectComplexTypes = (tss) => tss.filter((t) => t.identifier.kind === "complex-type");
|
|
5091
5103
|
var collectResources = (tss) => tss.filter((t) => t.identifier.kind === "resource");
|
|
5092
|
-
var notChoiceDeclaration = (field) => {
|
|
5093
|
-
if (field.choices) return void 0;
|
|
5094
|
-
if (field.choiceOf) return field;
|
|
5095
|
-
return field;
|
|
5096
|
-
};
|
|
5097
5104
|
var resourceRelatives = (schemas) => {
|
|
5098
5105
|
const regularSchemas = collectResources(schemas);
|
|
5099
5106
|
const directPairs = [];
|
|
@@ -5121,8 +5128,86 @@ var resourceRelatives = (schemas) => {
|
|
|
5121
5128
|
}
|
|
5122
5129
|
return allPairs;
|
|
5123
5130
|
};
|
|
5124
|
-
var
|
|
5125
|
-
|
|
5131
|
+
var mkTypeSchemaIndex = (schemas) => {
|
|
5132
|
+
const index = {};
|
|
5133
|
+
const append = (schema) => {
|
|
5134
|
+
const url = schema.identifier.url;
|
|
5135
|
+
if (!index[url]) {
|
|
5136
|
+
index[url] = {};
|
|
5137
|
+
}
|
|
5138
|
+
index[url][schema.identifier.package] = schema;
|
|
5139
|
+
};
|
|
5140
|
+
for (const schema of schemas) {
|
|
5141
|
+
append(schema);
|
|
5142
|
+
}
|
|
5143
|
+
const relations = resourceRelatives(schemas);
|
|
5144
|
+
const resolve2 = (id) => index[id.url]?.[id.package];
|
|
5145
|
+
const resourceChildren = (id) => {
|
|
5146
|
+
return relations.filter((relative2) => relative2.parent.name === id.name).map((relative2) => relative2.child);
|
|
5147
|
+
};
|
|
5148
|
+
const hierarchy = (schema) => {
|
|
5149
|
+
const res = [];
|
|
5150
|
+
let cur = schema;
|
|
5151
|
+
while (cur) {
|
|
5152
|
+
res.push(cur);
|
|
5153
|
+
const base = cur.base;
|
|
5154
|
+
if (base === void 0) break;
|
|
5155
|
+
const resolved = resolve2(base);
|
|
5156
|
+
if (!resolved) {
|
|
5157
|
+
throw new Error(`Failed to resolve base type: ${JSON.stringify(base)}`);
|
|
5158
|
+
}
|
|
5159
|
+
cur = resolved;
|
|
5160
|
+
}
|
|
5161
|
+
return res;
|
|
5162
|
+
};
|
|
5163
|
+
const findLastSpecialization = (id) => {
|
|
5164
|
+
const schema = resolve2(id);
|
|
5165
|
+
if (!schema) return id;
|
|
5166
|
+
const nonConstraintSchema = hierarchy(schema).find((s) => s.identifier.kind !== "profile");
|
|
5167
|
+
if (!nonConstraintSchema) {
|
|
5168
|
+
throw new Error(`No non-constraint schema found in hierarchy for ${id.name}`);
|
|
5169
|
+
}
|
|
5170
|
+
return nonConstraintSchema.identifier;
|
|
5171
|
+
};
|
|
5172
|
+
const flatProfile = (schema) => {
|
|
5173
|
+
const hierarchySchemas = hierarchy(schema);
|
|
5174
|
+
const constraintSchemas = hierarchySchemas.filter((s) => s.identifier.kind === "profile");
|
|
5175
|
+
const nonConstraintSchema = hierarchySchemas.find((s) => s.identifier.kind !== "profile");
|
|
5176
|
+
if (!nonConstraintSchema)
|
|
5177
|
+
throw new Error(`No non-constraint schema found in hierarchy for ${schema.identifier.name}`);
|
|
5178
|
+
const mergedFields = {};
|
|
5179
|
+
for (const anySchema of constraintSchemas.slice().reverse()) {
|
|
5180
|
+
const schema2 = anySchema;
|
|
5181
|
+
if (!schema2.fields) continue;
|
|
5182
|
+
for (const [fieldName, fieldConstraints] of Object.entries(schema2.fields)) {
|
|
5183
|
+
if (mergedFields[fieldName]) {
|
|
5184
|
+
mergedFields[fieldName] = { ...mergedFields[fieldName], ...fieldConstraints };
|
|
5185
|
+
} else {
|
|
5186
|
+
mergedFields[fieldName] = { ...fieldConstraints };
|
|
5187
|
+
}
|
|
5188
|
+
}
|
|
5189
|
+
}
|
|
5190
|
+
const deps = {};
|
|
5191
|
+
for (const e of constraintSchemas.flatMap((e2) => e2.dependencies ?? [])) {
|
|
5192
|
+
deps[e.url] = e;
|
|
5193
|
+
}
|
|
5194
|
+
const dependencies = Object.values(deps);
|
|
5195
|
+
return {
|
|
5196
|
+
...schema,
|
|
5197
|
+
base: nonConstraintSchema.identifier,
|
|
5198
|
+
fields: mergedFields,
|
|
5199
|
+
dependencies
|
|
5200
|
+
};
|
|
5201
|
+
};
|
|
5202
|
+
return {
|
|
5203
|
+
schemaIndex: index,
|
|
5204
|
+
relations,
|
|
5205
|
+
resolve: resolve2,
|
|
5206
|
+
resourceChildren,
|
|
5207
|
+
hierarchy,
|
|
5208
|
+
findLastSpecialization,
|
|
5209
|
+
flatProfile
|
|
5210
|
+
};
|
|
5126
5211
|
};
|
|
5127
5212
|
|
|
5128
5213
|
// src/api/writer-generator/typescript.ts
|
|
@@ -5153,6 +5238,7 @@ var tsFhirPackageDir = (name) => {
|
|
|
5153
5238
|
return kebabCase(name);
|
|
5154
5239
|
};
|
|
5155
5240
|
var tsModuleName = (id) => {
|
|
5241
|
+
if (id.kind === "profile") return `${tsResourceName(id)}_profile`;
|
|
5156
5242
|
return pascalCase(id.name);
|
|
5157
5243
|
};
|
|
5158
5244
|
var tsModuleFileName = (id) => {
|
|
@@ -5187,7 +5273,7 @@ var normalizeTsName = (n) => {
|
|
|
5187
5273
|
return n.replace(/[- ]/g, "_");
|
|
5188
5274
|
};
|
|
5189
5275
|
var TypeScript = class extends Writer {
|
|
5190
|
-
|
|
5276
|
+
tsIndex = mkTypeSchemaIndex([]);
|
|
5191
5277
|
tsImportType(tsPackageName, ...entities) {
|
|
5192
5278
|
this.lineSM(`import type { ${entities.join(", ")} } from "${tsPackageName}"`);
|
|
5193
5279
|
}
|
|
@@ -5258,39 +5344,36 @@ var TypeScript = class extends Writer {
|
|
|
5258
5344
|
if (!schema.fields) return;
|
|
5259
5345
|
if (schema.identifier.kind === "resource") {
|
|
5260
5346
|
const possibleResourceTypes = [schema.identifier];
|
|
5261
|
-
possibleResourceTypes.push(...resourceChildren(
|
|
5347
|
+
possibleResourceTypes.push(...this.tsIndex.resourceChildren(schema.identifier));
|
|
5262
5348
|
this.lineSM(`resourceType: ${possibleResourceTypes.map((e) => `"${e.name}"`).join(" | ")}`);
|
|
5263
5349
|
this.line();
|
|
5264
5350
|
}
|
|
5265
5351
|
const fields = Object.entries(schema.fields).sort((a, b) => a[0].localeCompare(b[0]));
|
|
5266
|
-
for (const [fieldName,
|
|
5267
|
-
|
|
5268
|
-
if (field === void 0) continue;
|
|
5352
|
+
for (const [fieldName, field] of fields) {
|
|
5353
|
+
if (!isNotChoiceFieldDeclaration(field)) continue;
|
|
5269
5354
|
this.debugComment(fieldName, ":", field);
|
|
5270
|
-
const
|
|
5355
|
+
const tsName = tsFieldName(fieldName);
|
|
5271
5356
|
const optionalSymbol = field.required ? "" : "?";
|
|
5272
5357
|
const arraySymbol = field.array ? "[]" : "";
|
|
5273
|
-
if (field.type === void 0)
|
|
5274
|
-
|
|
5275
|
-
}
|
|
5276
|
-
let type = field.type.name;
|
|
5358
|
+
if (field.type === void 0) continue;
|
|
5359
|
+
let tsType = field.type.name;
|
|
5277
5360
|
if (field.type.kind === "nested") {
|
|
5278
|
-
|
|
5361
|
+
tsType = tsResourceName(field.type);
|
|
5279
5362
|
}
|
|
5280
5363
|
if (field.type.kind === "primitive-type") {
|
|
5281
|
-
|
|
5364
|
+
tsType = primitiveType2tsType[field.type.name] ?? "string";
|
|
5282
5365
|
}
|
|
5283
|
-
if (schema.identifier.name === "Reference" &&
|
|
5284
|
-
|
|
5366
|
+
if (schema.identifier.name === "Reference" && tsName === "reference") {
|
|
5367
|
+
tsType = "`${T}/${string}`";
|
|
5285
5368
|
}
|
|
5286
5369
|
if (field.reference?.length) {
|
|
5287
5370
|
const references = field.reference.map((ref) => `"${ref.name}"`).join(" | ");
|
|
5288
|
-
|
|
5371
|
+
tsType = `Reference<${references}>`;
|
|
5289
5372
|
}
|
|
5290
5373
|
if (field.enum) {
|
|
5291
|
-
|
|
5374
|
+
tsType = field.enum.map((e) => `"${e}"`).join(" | ");
|
|
5292
5375
|
}
|
|
5293
|
-
this.lineSM(`${
|
|
5376
|
+
this.lineSM(`${tsName}${optionalSymbol}:`, `${tsType}${arraySymbol}`);
|
|
5294
5377
|
if (["resource", "complex-type"].includes(schema.identifier.kind)) {
|
|
5295
5378
|
this.addFieldExtension(fieldName, field);
|
|
5296
5379
|
}
|
|
@@ -5304,6 +5387,51 @@ var TypeScript = class extends Writer {
|
|
|
5304
5387
|
}
|
|
5305
5388
|
}
|
|
5306
5389
|
}
|
|
5390
|
+
generateProfileType(schema) {
|
|
5391
|
+
const name = tsResourceName(schema.identifier);
|
|
5392
|
+
this.debugComment(schema.identifier);
|
|
5393
|
+
this.curlyBlock(["export", "interface", name], () => {
|
|
5394
|
+
this.lineSM(`__profileUrl: "${schema.identifier.url}"`);
|
|
5395
|
+
this.line();
|
|
5396
|
+
if (!isFhirSchemaBased(schema)) return;
|
|
5397
|
+
for (const [fieldName, field] of Object.entries(schema.fields ?? {})) {
|
|
5398
|
+
if (!isNotChoiceFieldDeclaration(field)) continue;
|
|
5399
|
+
this.debugComment(fieldName, field);
|
|
5400
|
+
const tsName = tsFieldName(fieldName);
|
|
5401
|
+
let tsType;
|
|
5402
|
+
if (field.type.kind === "nested") {
|
|
5403
|
+
tsType = tsResourceName(field.type);
|
|
5404
|
+
} else if (field.enum) {
|
|
5405
|
+
tsType = field.enum.map((e) => `'${e}'`).join(" | ");
|
|
5406
|
+
} else if (field.reference && field.reference.length > 0) {
|
|
5407
|
+
const specializationId = this.tsIndex.findLastSpecialization(schema.identifier);
|
|
5408
|
+
const specialization = this.tsIndex.resolve(specializationId);
|
|
5409
|
+
if (specialization === void 0 || !isSpecialization(specialization))
|
|
5410
|
+
throw new Error(`Invalid specialization for ${schema.identifier}`);
|
|
5411
|
+
const sField = specialization.fields?.[fieldName];
|
|
5412
|
+
if (sField === void 0 || !isNotChoiceFieldDeclaration(sField))
|
|
5413
|
+
throw new Error(`Invalid field declaration for ${fieldName}`);
|
|
5414
|
+
const sRefs = (sField.reference ?? []).map((e) => e.name);
|
|
5415
|
+
const references = field.reference.map((ref) => {
|
|
5416
|
+
const resRef = this.tsIndex.findLastSpecialization(ref);
|
|
5417
|
+
if (resRef.name !== ref.name) {
|
|
5418
|
+
return `"${resRef.name}" /*${ref.name}*/`;
|
|
5419
|
+
}
|
|
5420
|
+
return `'${ref.name}'`;
|
|
5421
|
+
}).join(" | ");
|
|
5422
|
+
if (sRefs.length === 1 && sRefs[0] === "Resource" && references !== '"Resource"') {
|
|
5423
|
+
tsType = `Reference<"Resource" /* ${references} */ >`;
|
|
5424
|
+
} else {
|
|
5425
|
+
tsType = `Reference<${references}>`;
|
|
5426
|
+
}
|
|
5427
|
+
} else {
|
|
5428
|
+
tsType = primitiveType2tsType[field.type.name] ?? field.type.name;
|
|
5429
|
+
}
|
|
5430
|
+
this.lineSM(`${tsName}${!field.required ? "?" : ""}: ${tsType}${field.array ? "[]" : ""}`);
|
|
5431
|
+
}
|
|
5432
|
+
});
|
|
5433
|
+
this.line();
|
|
5434
|
+
}
|
|
5307
5435
|
generateResourceModule(schema) {
|
|
5308
5436
|
this.cat(`${tsModuleFileName(schema.identifier)}`, () => {
|
|
5309
5437
|
this.generateDisclaimer();
|
|
@@ -5312,6 +5440,11 @@ var TypeScript = class extends Writer {
|
|
|
5312
5440
|
this.generateComplexTypeReexports(schema);
|
|
5313
5441
|
this.generateNestedTypes(schema);
|
|
5314
5442
|
this.generateType(schema);
|
|
5443
|
+
} else if (isProfile(schema)) {
|
|
5444
|
+
const flatProfile = this.tsIndex.flatProfile(schema);
|
|
5445
|
+
this.debugComment(flatProfile.dependencies);
|
|
5446
|
+
this.generateDependenciesImports(flatProfile);
|
|
5447
|
+
this.generateProfileType(flatProfile);
|
|
5315
5448
|
} else {
|
|
5316
5449
|
throw new Error(`Profile generation not implemented for kind: ${schema.identifier.kind}`);
|
|
5317
5450
|
}
|
|
@@ -5321,10 +5454,10 @@ var TypeScript = class extends Writer {
|
|
|
5321
5454
|
const typesToGenerate = [
|
|
5322
5455
|
...collectComplexTypes(schemas),
|
|
5323
5456
|
...collectResources(schemas)
|
|
5324
|
-
// ...collectLogicalModels(
|
|
5325
|
-
// ...collectProfiles(
|
|
5457
|
+
// ...collectLogicalModels(schemas),
|
|
5458
|
+
// ...collectProfiles(schemas),
|
|
5326
5459
|
];
|
|
5327
|
-
this.
|
|
5460
|
+
this.tsIndex = mkTypeSchemaIndex(typesToGenerate);
|
|
5328
5461
|
const grouped = groupByPackages(typesToGenerate);
|
|
5329
5462
|
this.cd("/", () => {
|
|
5330
5463
|
for (const [packageName, packageSchemas] of Object.entries(grouped)) {
|