@atomic-ehr/codegen 0.0.1-canary.20251008121245.8324bc2 → 0.0.1-canary.20251008162621.549c5d8
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 +23 -23
- package/dist/index.js +175 -37
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1409,21 +1409,28 @@ var enrichFHIRSchema = (schema, packageMeta) => {
|
|
|
1409
1409
|
base: schema.base
|
|
1410
1410
|
};
|
|
1411
1411
|
};
|
|
1412
|
-
var
|
|
1413
|
-
return
|
|
1412
|
+
var isNestedIdentifier = (id) => {
|
|
1413
|
+
return id?.kind === "nested";
|
|
1414
1414
|
};
|
|
1415
|
-
var
|
|
1416
|
-
return
|
|
1415
|
+
var isProfileIdentifier = (id) => {
|
|
1416
|
+
return id?.kind === "profile";
|
|
1417
1417
|
};
|
|
1418
|
-
var
|
|
1419
|
-
return schema
|
|
1418
|
+
var isFhirSchemaBased = (schema) => {
|
|
1419
|
+
return schema?.identifier.kind !== "value-set";
|
|
1420
1420
|
};
|
|
1421
|
-
var
|
|
1422
|
-
return
|
|
1421
|
+
var isSpecializationTypeSchema = (schema) => {
|
|
1422
|
+
return schema?.identifier.kind === "resource" || schema?.identifier.kind === "complex-type" || schema?.identifier.kind === "logical";
|
|
1423
|
+
};
|
|
1424
|
+
var isProfileTypeSchema = (schema) => {
|
|
1425
|
+
return schema?.identifier.kind === "profile";
|
|
1423
1426
|
};
|
|
1424
1427
|
function isBindingSchema(schema) {
|
|
1425
|
-
return schema
|
|
1428
|
+
return schema?.identifier.kind === "binding";
|
|
1426
1429
|
}
|
|
1430
|
+
var isNotChoiceDeclarationField = (field) => {
|
|
1431
|
+
if (!field) return false;
|
|
1432
|
+
return field.choices === void 0;
|
|
1433
|
+
};
|
|
1427
1434
|
|
|
1428
1435
|
// src/typeschema/register.ts
|
|
1429
1436
|
var registerFromManager = async (manager, logger) => {
|
|
@@ -2063,7 +2070,11 @@ function extractDependencies(identifier, base, fields, nestedTypes) {
|
|
|
2063
2070
|
uniqDeps[dep.url] = dep;
|
|
2064
2071
|
}
|
|
2065
2072
|
const localNestedTypeUrls = new Set(nestedTypes?.map((nt) => nt.identifier.url));
|
|
2066
|
-
const result = Object.values(uniqDeps).filter((e) =>
|
|
2073
|
+
const result = Object.values(uniqDeps).filter((e) => {
|
|
2074
|
+
if (isProfileIdentifier(identifier)) return true;
|
|
2075
|
+
if (!isNestedIdentifier(e)) return true;
|
|
2076
|
+
return !localNestedTypeUrls.has(e.url);
|
|
2077
|
+
}).sort((a, b) => a.url.localeCompare(b.url));
|
|
2067
2078
|
return result.length > 0 ? result : void 0;
|
|
2068
2079
|
}
|
|
2069
2080
|
function transformFhirSchemaResource(register, fhirSchema, logger) {
|
|
@@ -5030,6 +5041,7 @@ var Writer = class extends FileSystemWriter {
|
|
|
5030
5041
|
if (tokens.length === 0) {
|
|
5031
5042
|
this.write("\n");
|
|
5032
5043
|
} else {
|
|
5044
|
+
this.writeIndent();
|
|
5033
5045
|
this.write(`${tokens.join(" ")}
|
|
5034
5046
|
`);
|
|
5035
5047
|
}
|
|
@@ -5068,6 +5080,11 @@ var Writer = class extends FileSystemWriter {
|
|
|
5068
5080
|
this.disclaimer().forEach((e) => this.comment(e));
|
|
5069
5081
|
this.line();
|
|
5070
5082
|
}
|
|
5083
|
+
indentBlock(gencontent) {
|
|
5084
|
+
this.indent();
|
|
5085
|
+
gencontent();
|
|
5086
|
+
this.deindent();
|
|
5087
|
+
}
|
|
5071
5088
|
curlyBlock(tokens, gencontent, endTokens) {
|
|
5072
5089
|
this.line(`${tokens.filter(Boolean).join(" ")} {`);
|
|
5073
5090
|
this.indent();
|
|
@@ -5101,6 +5118,7 @@ var groupByPackages = (typeSchemas) => {
|
|
|
5101
5118
|
};
|
|
5102
5119
|
var collectComplexTypes = (tss) => tss.filter((t) => t.identifier.kind === "complex-type");
|
|
5103
5120
|
var collectResources = (tss) => tss.filter((t) => t.identifier.kind === "resource");
|
|
5121
|
+
var collectProfiles = (tss) => tss.filter(isProfileTypeSchema);
|
|
5104
5122
|
var resourceRelatives = (schemas) => {
|
|
5105
5123
|
const regularSchemas = collectResources(schemas);
|
|
5106
5124
|
const directPairs = [];
|
|
@@ -5154,7 +5172,9 @@ var mkTypeSchemaIndex = (schemas) => {
|
|
|
5154
5172
|
if (base === void 0) break;
|
|
5155
5173
|
const resolved = resolve2(base);
|
|
5156
5174
|
if (!resolved) {
|
|
5157
|
-
throw new Error(
|
|
5175
|
+
throw new Error(
|
|
5176
|
+
`Failed to resolve base type: ${res.map((e) => `${e.identifier.url} (${e.identifier.kind})`).join(", ")}`
|
|
5177
|
+
);
|
|
5158
5178
|
}
|
|
5159
5179
|
cur = resolved;
|
|
5160
5180
|
}
|
|
@@ -5199,6 +5219,12 @@ var mkTypeSchemaIndex = (schemas) => {
|
|
|
5199
5219
|
dependencies
|
|
5200
5220
|
};
|
|
5201
5221
|
};
|
|
5222
|
+
const isWithMetaField = (profile) => {
|
|
5223
|
+
return hierarchy(profile).filter(isSpecializationTypeSchema).some((schema) => {
|
|
5224
|
+
console.log(schema.fields?.meta);
|
|
5225
|
+
return schema.fields?.meta !== void 0;
|
|
5226
|
+
});
|
|
5227
|
+
};
|
|
5202
5228
|
return {
|
|
5203
5229
|
schemaIndex: index,
|
|
5204
5230
|
relations,
|
|
@@ -5206,7 +5232,8 @@ var mkTypeSchemaIndex = (schemas) => {
|
|
|
5206
5232
|
resourceChildren,
|
|
5207
5233
|
hierarchy,
|
|
5208
5234
|
findLastSpecialization,
|
|
5209
|
-
flatProfile
|
|
5235
|
+
flatProfile,
|
|
5236
|
+
isWithMetaField
|
|
5210
5237
|
};
|
|
5211
5238
|
};
|
|
5212
5239
|
|
|
@@ -5270,6 +5297,8 @@ var tsResourceName = (id) => {
|
|
|
5270
5297
|
};
|
|
5271
5298
|
var tsFieldName = (n) => normalizeTsName(n);
|
|
5272
5299
|
var normalizeTsName = (n) => {
|
|
5300
|
+
const tsKeywords = /* @__PURE__ */ new Set(["abstract", "any", "as", "async", "await", "boolean", "bigint", "break", "case", "catch", "class", "const", "constructor", "continue", "debugger", "declare", "default", "delete", "do", "else", "enum", "export", "extends", "extern", "false", "finally", "for", "function", "from", "get", "goto", "if", "implements", "import", "in", "infer", "instanceof", "interface", "keyof", "let", "module", "namespace", "never", "new", "null", "number", "object", "of", "override", "private", "protected", "public", "readonly", "return", "satisfies", "set", "static", "string", "super", "switch", "this", "throw", "true", "try", "type", "typeof", "unknown", "var", "void", "while"]);
|
|
5301
|
+
if (tsKeywords.has(n)) n = `${n}_`;
|
|
5273
5302
|
return n.replace(/[- ]/g, "_");
|
|
5274
5303
|
};
|
|
5275
5304
|
var TypeScript = class extends Writer {
|
|
@@ -5295,19 +5324,30 @@ var TypeScript = class extends Writer {
|
|
|
5295
5324
|
}
|
|
5296
5325
|
generateDependenciesImports(schema) {
|
|
5297
5326
|
if (schema.dependencies) {
|
|
5298
|
-
const
|
|
5299
|
-
|
|
5300
|
-
|
|
5301
|
-
|
|
5302
|
-
|
|
5303
|
-
|
|
5304
|
-
|
|
5305
|
-
|
|
5306
|
-
}))
|
|
5307
|
-
|
|
5308
|
-
|
|
5327
|
+
const imports = [];
|
|
5328
|
+
const skipped = [];
|
|
5329
|
+
for (const dep of schema.dependencies) {
|
|
5330
|
+
if (["complex-type", "resource", "logical"].includes(dep.kind)) {
|
|
5331
|
+
imports.push({
|
|
5332
|
+
tsPackage: `../${kebabCase(dep.package)}/${pascalCase(dep.name)}`,
|
|
5333
|
+
name: uppercaseFirstLetter(dep.name)
|
|
5334
|
+
});
|
|
5335
|
+
} else if (isNestedIdentifier(dep)) {
|
|
5336
|
+
imports.push({
|
|
5337
|
+
tsPackage: `../${kebabCase(dep.package)}/${pascalCase(canonicalToName(dep.url) ?? "")}`,
|
|
5338
|
+
name: tsResourceName(dep)
|
|
5339
|
+
});
|
|
5340
|
+
} else {
|
|
5341
|
+
skipped.push(dep);
|
|
5342
|
+
}
|
|
5343
|
+
}
|
|
5344
|
+
imports.sort((a, b) => a.name.localeCompare(b.name));
|
|
5345
|
+
for (const dep of imports) {
|
|
5309
5346
|
this.tsImportType(dep.tsPackage, dep.name);
|
|
5310
5347
|
}
|
|
5348
|
+
for (const dep of skipped) {
|
|
5349
|
+
this.debugComment("skip:", dep);
|
|
5350
|
+
}
|
|
5311
5351
|
this.line();
|
|
5312
5352
|
}
|
|
5313
5353
|
}
|
|
@@ -5350,7 +5390,7 @@ var TypeScript = class extends Writer {
|
|
|
5350
5390
|
}
|
|
5351
5391
|
const fields = Object.entries(schema.fields).sort((a, b) => a[0].localeCompare(b[0]));
|
|
5352
5392
|
for (const [fieldName, field] of fields) {
|
|
5353
|
-
if (!
|
|
5393
|
+
if (!isNotChoiceDeclarationField(field)) continue;
|
|
5354
5394
|
this.debugComment(fieldName, ":", field);
|
|
5355
5395
|
const tsName = tsFieldName(fieldName);
|
|
5356
5396
|
const optionalSymbol = field.required ? "" : "?";
|
|
@@ -5389,13 +5429,14 @@ var TypeScript = class extends Writer {
|
|
|
5389
5429
|
}
|
|
5390
5430
|
generateProfileType(schema) {
|
|
5391
5431
|
const name = tsResourceName(schema.identifier);
|
|
5392
|
-
this.debugComment(schema.identifier);
|
|
5432
|
+
this.debugComment("identifier", schema.identifier);
|
|
5433
|
+
this.debugComment("base", schema.base);
|
|
5393
5434
|
this.curlyBlock(["export", "interface", name], () => {
|
|
5394
5435
|
this.lineSM(`__profileUrl: "${schema.identifier.url}"`);
|
|
5395
5436
|
this.line();
|
|
5396
5437
|
if (!isFhirSchemaBased(schema)) return;
|
|
5397
5438
|
for (const [fieldName, field] of Object.entries(schema.fields ?? {})) {
|
|
5398
|
-
if (!
|
|
5439
|
+
if (!isNotChoiceDeclarationField(field)) continue;
|
|
5399
5440
|
this.debugComment(fieldName, field);
|
|
5400
5441
|
const tsName = tsFieldName(fieldName);
|
|
5401
5442
|
let tsType;
|
|
@@ -5406,10 +5447,10 @@ var TypeScript = class extends Writer {
|
|
|
5406
5447
|
} else if (field.reference && field.reference.length > 0) {
|
|
5407
5448
|
const specializationId = this.tsIndex.findLastSpecialization(schema.identifier);
|
|
5408
5449
|
const specialization = this.tsIndex.resolve(specializationId);
|
|
5409
|
-
if (
|
|
5450
|
+
if (!isSpecializationTypeSchema(specialization))
|
|
5410
5451
|
throw new Error(`Invalid specialization for ${schema.identifier}`);
|
|
5411
5452
|
const sField = specialization.fields?.[fieldName];
|
|
5412
|
-
if (sField === void 0 || !
|
|
5453
|
+
if (sField === void 0 || !isNotChoiceDeclarationField(sField))
|
|
5413
5454
|
throw new Error(`Invalid field declaration for ${fieldName}`);
|
|
5414
5455
|
const sRefs = (sField.reference ?? []).map((e) => e.name);
|
|
5415
5456
|
const references = field.reference.map((ref) => {
|
|
@@ -5432,32 +5473,129 @@ var TypeScript = class extends Writer {
|
|
|
5432
5473
|
});
|
|
5433
5474
|
this.line();
|
|
5434
5475
|
}
|
|
5476
|
+
generateAttachProfile(flatProfile) {
|
|
5477
|
+
const tsBaseResourceName = tsResourceName(flatProfile.base);
|
|
5478
|
+
const tsProfileName = tsResourceName(flatProfile.identifier);
|
|
5479
|
+
const profileFields = Object.entries(flatProfile.fields || {}).filter(([_fieldName, field]) => {
|
|
5480
|
+
return field && isNotChoiceDeclarationField(field) && field.type !== void 0;
|
|
5481
|
+
}).map(([fieldName]) => tsFieldName(fieldName));
|
|
5482
|
+
this.curlyBlock(
|
|
5483
|
+
[
|
|
5484
|
+
`export const attach_${tsProfileName} =`,
|
|
5485
|
+
`(resource: ${tsBaseResourceName}, profile: ${tsProfileName}): ${tsBaseResourceName}`,
|
|
5486
|
+
"=>"
|
|
5487
|
+
],
|
|
5488
|
+
() => {
|
|
5489
|
+
this.curlyBlock(["return"], () => {
|
|
5490
|
+
this.line("...resource,");
|
|
5491
|
+
this.curlyBlock(["meta:"], () => {
|
|
5492
|
+
this.line(`profile: ['${flatProfile.identifier.url}']`);
|
|
5493
|
+
}, [","]);
|
|
5494
|
+
profileFields.forEach((fieldName) => {
|
|
5495
|
+
this.line(`${fieldName}:`, `profile.${fieldName},`);
|
|
5496
|
+
});
|
|
5497
|
+
});
|
|
5498
|
+
}
|
|
5499
|
+
);
|
|
5500
|
+
this.line();
|
|
5501
|
+
}
|
|
5502
|
+
generateExtractProfile(flatProfile) {
|
|
5503
|
+
const tsBaseResourceName = tsResourceName(flatProfile.base);
|
|
5504
|
+
const tsProfileName = tsResourceName(flatProfile.identifier);
|
|
5505
|
+
const profileFields = Object.entries(flatProfile.fields || {}).filter(([_fieldName, field]) => {
|
|
5506
|
+
return isNotChoiceDeclarationField(field) && field.type !== void 0;
|
|
5507
|
+
}).map(([fieldName]) => fieldName);
|
|
5508
|
+
const specialization = this.tsIndex.resolve(this.tsIndex.findLastSpecialization(flatProfile.identifier));
|
|
5509
|
+
if (!isSpecializationTypeSchema(specialization))
|
|
5510
|
+
throw new Error(`Specialization not found for ${flatProfile.identifier.url}`);
|
|
5511
|
+
const shouldCast = {};
|
|
5512
|
+
this.curlyBlock(
|
|
5513
|
+
[
|
|
5514
|
+
`export const extract_${tsBaseResourceName} =`,
|
|
5515
|
+
`(resource: ${tsBaseResourceName}): ${tsProfileName}`,
|
|
5516
|
+
"=>"
|
|
5517
|
+
],
|
|
5518
|
+
() => {
|
|
5519
|
+
profileFields.forEach((fieldName) => {
|
|
5520
|
+
const tsField = tsFieldName(fieldName);
|
|
5521
|
+
const pField = flatProfile.fields?.[fieldName];
|
|
5522
|
+
const rField = specialization.fields?.[fieldName];
|
|
5523
|
+
if (!isNotChoiceDeclarationField(pField) || !isNotChoiceDeclarationField(rField)) return;
|
|
5524
|
+
if (pField.required && !rField.required) {
|
|
5525
|
+
this.curlyBlock(
|
|
5526
|
+
[`if (resource.${tsField} === undefined)`],
|
|
5527
|
+
() => this.lineSM(
|
|
5528
|
+
`throw new Error("'${tsField}' is required for ${flatProfile.identifier.url}")`
|
|
5529
|
+
)
|
|
5530
|
+
);
|
|
5531
|
+
}
|
|
5532
|
+
const pRefs = pField?.reference?.map((ref) => ref.name);
|
|
5533
|
+
const rRefs = rField?.reference?.map((ref) => ref.name);
|
|
5534
|
+
if (pRefs && rRefs && pRefs.length !== rRefs.length) {
|
|
5535
|
+
const predName = `reference_pred_${tsField}`;
|
|
5536
|
+
this.curlyBlock(["const", predName, "=", "(ref?: Reference)", "=>"], () => {
|
|
5537
|
+
this.line("return !ref");
|
|
5538
|
+
this.indentBlock(() => {
|
|
5539
|
+
rRefs.forEach((ref) => {
|
|
5540
|
+
this.line(`|| ref.reference?.startsWith('${ref}/')`);
|
|
5541
|
+
});
|
|
5542
|
+
this.line(";");
|
|
5543
|
+
});
|
|
5544
|
+
});
|
|
5545
|
+
let cond = !pField?.required ? `!resource.${tsField} || ` : "";
|
|
5546
|
+
if (pField.array) {
|
|
5547
|
+
cond += `resource.${tsField}.every( (ref) => ${predName}(ref) )`;
|
|
5548
|
+
} else {
|
|
5549
|
+
cond += `${predName}(resource.${tsField})`;
|
|
5550
|
+
}
|
|
5551
|
+
this.curlyBlock(["if (", cond, ")"], () => {
|
|
5552
|
+
this.lineSM(
|
|
5553
|
+
`throw new Error("'${fieldName}' has different references in profile and specialization")`
|
|
5554
|
+
);
|
|
5555
|
+
});
|
|
5556
|
+
this.line();
|
|
5557
|
+
shouldCast[fieldName] = true;
|
|
5558
|
+
}
|
|
5559
|
+
});
|
|
5560
|
+
this.curlyBlock(["return"], () => {
|
|
5561
|
+
this.line(`__profileUrl: '${flatProfile.identifier.url}',`);
|
|
5562
|
+
profileFields.forEach((fieldName) => {
|
|
5563
|
+
const tsField = tsFieldName(fieldName);
|
|
5564
|
+
if (shouldCast[fieldName]) {
|
|
5565
|
+
this.line(`${tsField}:`, `resource.${tsField} as ${tsProfileName}['${tsField}'],`);
|
|
5566
|
+
} else {
|
|
5567
|
+
this.line(`${tsField}:`, `resource.${tsField},`);
|
|
5568
|
+
}
|
|
5569
|
+
});
|
|
5570
|
+
});
|
|
5571
|
+
}
|
|
5572
|
+
);
|
|
5573
|
+
}
|
|
5435
5574
|
generateResourceModule(schema) {
|
|
5436
5575
|
this.cat(`${tsModuleFileName(schema.identifier)}`, () => {
|
|
5437
5576
|
this.generateDisclaimer();
|
|
5438
|
-
if (["complex-type", "resource", "logical"
|
|
5577
|
+
if (["complex-type", "resource", "logical"].includes(schema.identifier.kind)) {
|
|
5439
5578
|
this.generateDependenciesImports(schema);
|
|
5440
5579
|
this.generateComplexTypeReexports(schema);
|
|
5441
5580
|
this.generateNestedTypes(schema);
|
|
5442
5581
|
this.generateType(schema);
|
|
5443
|
-
} else if (
|
|
5582
|
+
} else if (isProfileTypeSchema(schema)) {
|
|
5444
5583
|
const flatProfile = this.tsIndex.flatProfile(schema);
|
|
5445
|
-
this.debugComment(flatProfile.dependencies);
|
|
5446
5584
|
this.generateDependenciesImports(flatProfile);
|
|
5447
5585
|
this.generateProfileType(flatProfile);
|
|
5448
|
-
|
|
5449
|
-
|
|
5450
|
-
}
|
|
5586
|
+
this.generateAttachProfile(flatProfile);
|
|
5587
|
+
this.generateExtractProfile(flatProfile);
|
|
5588
|
+
} else throw new Error(`Profile generation not implemented for kind: ${schema.identifier.kind}`);
|
|
5451
5589
|
});
|
|
5452
5590
|
}
|
|
5453
5591
|
generate(schemas) {
|
|
5592
|
+
this.tsIndex = mkTypeSchemaIndex(schemas);
|
|
5454
5593
|
const typesToGenerate = [
|
|
5455
5594
|
...collectComplexTypes(schemas),
|
|
5456
|
-
...collectResources(schemas)
|
|
5595
|
+
...collectResources(schemas),
|
|
5457
5596
|
// ...collectLogicalModels(schemas),
|
|
5458
|
-
|
|
5597
|
+
...collectProfiles(schemas).filter((p) => this.tsIndex.isWithMetaField(p))
|
|
5459
5598
|
];
|
|
5460
|
-
this.tsIndex = mkTypeSchemaIndex(typesToGenerate);
|
|
5461
5599
|
const grouped = groupByPackages(typesToGenerate);
|
|
5462
5600
|
this.cd("/", () => {
|
|
5463
5601
|
for (const [packageName, packageSchemas] of Object.entries(grouped)) {
|