@atomic-ehr/codegen 0.0.15 → 0.0.16
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 +22 -16
- package/assets/api/writer-generator/python/fhirpy_base_model.py +1 -0
- package/assets/api/writer-generator/python/profile_helpers.py +502 -0
- package/assets/api/writer-generator/python/resource_preprocessor.py +41 -0
- package/dist/cli/index.js +5 -5
- package/dist/index.d.ts +11 -0
- package/dist/index.js +1577 -422
- package/dist/index.js.map +1 -1
- package/package.json +8 -7
- package/assets/api/writer-generator/python/resource_family_validator.py +0 -92
package/dist/index.js
CHANGED
|
@@ -110,8 +110,9 @@ function mkLogger(opts = {}) {
|
|
|
110
110
|
var mkCodegenLogger = (opts = {}) => mkLogger(opts);
|
|
111
111
|
|
|
112
112
|
// src/api/writer-generator/utils.ts
|
|
113
|
+
var WORD_SPLIT_RE = /(?<=[a-z])(?=[A-Z])|[-_.\s]/;
|
|
113
114
|
var words = (s) => {
|
|
114
|
-
return s.split(
|
|
115
|
+
return s.split(WORD_SPLIT_RE).filter(Boolean);
|
|
115
116
|
};
|
|
116
117
|
var kebabCase = (s) => {
|
|
117
118
|
return words(s).map((s2) => s2.toLowerCase()).join("-");
|
|
@@ -294,9 +295,8 @@ var Writer = class extends FileSystemWriter {
|
|
|
294
295
|
tokens = tokens.map((token) => {
|
|
295
296
|
if (typeof token === "string") {
|
|
296
297
|
return token;
|
|
297
|
-
} else {
|
|
298
|
-
return JSON.stringify(token, null, 2);
|
|
299
298
|
}
|
|
299
|
+
return JSON.stringify(token, null, 2);
|
|
300
300
|
});
|
|
301
301
|
this.comment(...tokens);
|
|
302
302
|
}
|
|
@@ -334,6 +334,7 @@ var Writer = class extends FileSystemWriter {
|
|
|
334
334
|
this.line(`]${endTokens?.filter(Boolean).join(" ") ?? ""}`);
|
|
335
335
|
}
|
|
336
336
|
};
|
|
337
|
+
var LEADING_DIGIT_RE = /^\d/;
|
|
337
338
|
var extractNameFromCanonical = (canonical, dropFragment = true) => {
|
|
338
339
|
let localName = canonical.split("/").pop();
|
|
339
340
|
if (!localName) return void 0;
|
|
@@ -341,7 +342,7 @@ var extractNameFromCanonical = (canonical, dropFragment = true) => {
|
|
|
341
342
|
localName = localName.split("#")[0];
|
|
342
343
|
}
|
|
343
344
|
if (!localName) return void 0;
|
|
344
|
-
if (
|
|
345
|
+
if (LEADING_DIGIT_RE.test(localName)) {
|
|
345
346
|
localName = `number_${localName}`;
|
|
346
347
|
}
|
|
347
348
|
return localName;
|
|
@@ -505,9 +506,8 @@ var resolveCSharpAssets = (fn) => {
|
|
|
505
506
|
const __dirname = Path5__default.dirname(__filename);
|
|
506
507
|
if (__filename.endsWith("dist/index.js")) {
|
|
507
508
|
return Path5__default.resolve(__dirname, "..", "assets", "api", "writer-generator", "csharp", fn);
|
|
508
|
-
} else {
|
|
509
|
-
return Path5__default.resolve(__dirname, "../../../..", "assets", "api", "writer-generator", "csharp", fn);
|
|
510
509
|
}
|
|
510
|
+
return Path5__default.resolve(__dirname, "../../../..", "assets", "api", "writer-generator", "csharp", fn);
|
|
511
511
|
};
|
|
512
512
|
var PRIMITIVE_TYPE_MAP = {
|
|
513
513
|
boolean: "bool",
|
|
@@ -543,13 +543,14 @@ var formatClassName = (schema) => {
|
|
|
543
543
|
var formatBaseClass = (schema) => {
|
|
544
544
|
return schema.base ? `: ${schema.base.name}` : "";
|
|
545
545
|
};
|
|
546
|
+
var LEADING_DIGIT_RE2 = /^\d/;
|
|
546
547
|
var canonicalToName = (canonical, dropFragment = true) => {
|
|
547
548
|
if (!canonical) return void 0;
|
|
548
549
|
let localName = canonical.split("/").pop();
|
|
549
550
|
if (!localName) return void 0;
|
|
550
551
|
if (dropFragment && localName.includes("#")) localName = localName.split("#")[0];
|
|
551
552
|
if (!localName) return void 0;
|
|
552
|
-
if (
|
|
553
|
+
if (LEADING_DIGIT_RE2.test(localName)) {
|
|
553
554
|
localName = `number_${localName}`;
|
|
554
555
|
}
|
|
555
556
|
return formatName(localName);
|
|
@@ -781,6 +782,116 @@ var CSharp = class extends Writer {
|
|
|
781
782
|
fs__default.copyFileSync(sourceFile, destFile);
|
|
782
783
|
}
|
|
783
784
|
};
|
|
785
|
+
|
|
786
|
+
// src/api/writer-generator/python/naming-utils.ts
|
|
787
|
+
var PRIMITIVE_TYPE_MAP2 = {
|
|
788
|
+
boolean: "bool",
|
|
789
|
+
instant: "str",
|
|
790
|
+
time: "str",
|
|
791
|
+
date: "str",
|
|
792
|
+
dateTime: "str",
|
|
793
|
+
decimal: "float",
|
|
794
|
+
integer: "int",
|
|
795
|
+
unsignedInt: "int",
|
|
796
|
+
positiveInt: "PositiveInt",
|
|
797
|
+
integer64: "int",
|
|
798
|
+
base64Binary: "str",
|
|
799
|
+
uri: "str",
|
|
800
|
+
url: "str",
|
|
801
|
+
canonical: "str",
|
|
802
|
+
oid: "str",
|
|
803
|
+
uuid: "str",
|
|
804
|
+
string: "str",
|
|
805
|
+
code: "str",
|
|
806
|
+
markdown: "str",
|
|
807
|
+
id: "str",
|
|
808
|
+
xhtml: "str"
|
|
809
|
+
};
|
|
810
|
+
var PYTHON_KEYWORDS = /* @__PURE__ */ new Set([
|
|
811
|
+
"False",
|
|
812
|
+
"None",
|
|
813
|
+
"True",
|
|
814
|
+
"and",
|
|
815
|
+
"as",
|
|
816
|
+
"assert",
|
|
817
|
+
"async",
|
|
818
|
+
"await",
|
|
819
|
+
"break",
|
|
820
|
+
"class",
|
|
821
|
+
"continue",
|
|
822
|
+
"def",
|
|
823
|
+
"del",
|
|
824
|
+
"elif",
|
|
825
|
+
"else",
|
|
826
|
+
"except",
|
|
827
|
+
"finally",
|
|
828
|
+
"for",
|
|
829
|
+
"from",
|
|
830
|
+
"global",
|
|
831
|
+
"if",
|
|
832
|
+
"import",
|
|
833
|
+
"in",
|
|
834
|
+
"is",
|
|
835
|
+
"lambda",
|
|
836
|
+
"nonlocal",
|
|
837
|
+
"not",
|
|
838
|
+
"or",
|
|
839
|
+
"pass",
|
|
840
|
+
"raise",
|
|
841
|
+
"return",
|
|
842
|
+
"try",
|
|
843
|
+
"while",
|
|
844
|
+
"with",
|
|
845
|
+
"yield"
|
|
846
|
+
]);
|
|
847
|
+
var fixReservedWords = (name) => {
|
|
848
|
+
return PYTHON_KEYWORDS.has(name) ? `${name}_` : name;
|
|
849
|
+
};
|
|
850
|
+
var canonicalToName2 = (canonical, dropFragment = true) => {
|
|
851
|
+
if (!canonical) return void 0;
|
|
852
|
+
let localName = canonical.split("/").pop();
|
|
853
|
+
if (!localName) return void 0;
|
|
854
|
+
if (dropFragment && localName.includes("#")) {
|
|
855
|
+
localName = localName.split("#")[0];
|
|
856
|
+
}
|
|
857
|
+
if (!localName) return void 0;
|
|
858
|
+
if (/^\d/.test(localName)) {
|
|
859
|
+
localName = `number_${localName}`;
|
|
860
|
+
}
|
|
861
|
+
return snakeCase(localName);
|
|
862
|
+
};
|
|
863
|
+
var deriveResourceName = (id) => {
|
|
864
|
+
if (id.kind === "nested") {
|
|
865
|
+
const url = id.url;
|
|
866
|
+
const path = canonicalToName2(url, false);
|
|
867
|
+
if (!path) return "";
|
|
868
|
+
const [resourceName, fragment] = path.split("#");
|
|
869
|
+
const name = uppercaseFirstLetterOfEach((fragment ?? "").split(".")).join("");
|
|
870
|
+
return pascalCase([resourceName, name].join(""));
|
|
871
|
+
}
|
|
872
|
+
return pascalCase(id.name);
|
|
873
|
+
};
|
|
874
|
+
var buildPyPackageName = (packageName) => {
|
|
875
|
+
const parts = packageName ? [snakeCase(packageName)] : [""];
|
|
876
|
+
return parts.join(".");
|
|
877
|
+
};
|
|
878
|
+
var pyFhirPackageByName = (rootPackageName, name) => [rootPackageName, buildPyPackageName(name)].join(".");
|
|
879
|
+
var pyFhirPackage = (rootPackageName, identifier) => pyFhirPackageByName(rootPackageName, identifier.package);
|
|
880
|
+
var pyPackage = (rootPackageName, identifier) => {
|
|
881
|
+
if (identifier.kind === "complex-type") {
|
|
882
|
+
return `${pyFhirPackage(rootPackageName, identifier)}.base`;
|
|
883
|
+
}
|
|
884
|
+
if (identifier.kind === "resource") {
|
|
885
|
+
return [pyFhirPackage(rootPackageName, identifier), snakeCase(identifier.name)].join(".");
|
|
886
|
+
}
|
|
887
|
+
return pyFhirPackage(rootPackageName, identifier);
|
|
888
|
+
};
|
|
889
|
+
var pyTypeFromIdentifier = (id) => {
|
|
890
|
+
if (isPrimitiveIdentifier(id)) return PRIMITIVE_TYPE_MAP2[id.name] ?? "str";
|
|
891
|
+
const prim = PRIMITIVE_TYPE_MAP2[id.name];
|
|
892
|
+
if (prim !== void 0) return prim;
|
|
893
|
+
return deriveResourceName(id);
|
|
894
|
+
};
|
|
784
895
|
var groupByPackages = (typeSchemas) => {
|
|
785
896
|
const grouped = {};
|
|
786
897
|
for (const ts of typeSchemas) {
|
|
@@ -1202,180 +1313,1193 @@ var mkTypeSchemaIndex = (schemas, {
|
|
|
1202
1313
|
allChoiceNames: field.choices
|
|
1203
1314
|
};
|
|
1204
1315
|
}
|
|
1205
|
-
return void 0;
|
|
1206
|
-
};
|
|
1207
|
-
const isWithMetaField = (profile) => {
|
|
1208
|
-
const genealogy = tryHierarchy(profile);
|
|
1209
|
-
if (!genealogy) return false;
|
|
1210
|
-
return genealogy.filter(isSpecializationTypeSchema).some((schema) => {
|
|
1211
|
-
return schema.fields?.meta !== void 0;
|
|
1316
|
+
return void 0;
|
|
1317
|
+
};
|
|
1318
|
+
const isWithMetaField = (profile) => {
|
|
1319
|
+
const genealogy = tryHierarchy(profile);
|
|
1320
|
+
if (!genealogy) return false;
|
|
1321
|
+
return genealogy.filter(isSpecializationTypeSchema).some((schema) => {
|
|
1322
|
+
return schema.fields?.meta !== void 0;
|
|
1323
|
+
});
|
|
1324
|
+
};
|
|
1325
|
+
const entityTree = () => {
|
|
1326
|
+
const tree = {};
|
|
1327
|
+
for (const [pkgId, shemas] of Object.entries(groupByPackages(schemas))) {
|
|
1328
|
+
tree[pkgId] = {
|
|
1329
|
+
"primitive-type": {},
|
|
1330
|
+
"complex-type": {},
|
|
1331
|
+
resource: {},
|
|
1332
|
+
"value-set": {},
|
|
1333
|
+
nested: {},
|
|
1334
|
+
binding: {},
|
|
1335
|
+
profile: {},
|
|
1336
|
+
"profile-snapshot": {},
|
|
1337
|
+
logical: {}
|
|
1338
|
+
};
|
|
1339
|
+
for (const schema of shemas) {
|
|
1340
|
+
tree[pkgId][schema.identifier.kind][schema.identifier.url] = {};
|
|
1341
|
+
}
|
|
1342
|
+
}
|
|
1343
|
+
return tree;
|
|
1344
|
+
};
|
|
1345
|
+
const exportTree = async (filename) => {
|
|
1346
|
+
const tree = entityTree();
|
|
1347
|
+
const raw = filename.endsWith(".yaml") ? YAML.stringify(tree) : JSON.stringify(tree, void 0, 2);
|
|
1348
|
+
await fsPromises.mkdir(Path5.dirname(filename), { recursive: true });
|
|
1349
|
+
await fsPromises.writeFile(filename, raw);
|
|
1350
|
+
};
|
|
1351
|
+
return {
|
|
1352
|
+
_schemaIndex: index,
|
|
1353
|
+
schemas,
|
|
1354
|
+
schemasByPackage: groupByPackages(schemas),
|
|
1355
|
+
register,
|
|
1356
|
+
collectComplexTypes: () => schemas.filter(isComplexTypeTypeSchema),
|
|
1357
|
+
collectResources: () => schemas.filter(isResourceTypeSchema),
|
|
1358
|
+
collectLogicalModels: () => schemas.filter(isLogicalTypeSchema),
|
|
1359
|
+
collectProfiles: () => schemas.filter(isProfileTypeSchema),
|
|
1360
|
+
collectSnapshotProfiles,
|
|
1361
|
+
resolve: resolve6,
|
|
1362
|
+
resolveType,
|
|
1363
|
+
resolveByUrl,
|
|
1364
|
+
tryHierarchy,
|
|
1365
|
+
hierarchy,
|
|
1366
|
+
findLastSpecialization,
|
|
1367
|
+
findLastSpecializationByIdentifier,
|
|
1368
|
+
flatProfile,
|
|
1369
|
+
constrainedChoice,
|
|
1370
|
+
isWithMetaField,
|
|
1371
|
+
entityTree,
|
|
1372
|
+
exportTree,
|
|
1373
|
+
irReport: () => irReport,
|
|
1374
|
+
replaceSchemas: (newSchemas) => mkTypeSchemaIndex(newSchemas, { register, logger, irReport: { ...irReport } })
|
|
1375
|
+
};
|
|
1376
|
+
};
|
|
1377
|
+
|
|
1378
|
+
// src/api/writer-generator/python/profile-naming.ts
|
|
1379
|
+
var normalizePyName = (n) => {
|
|
1380
|
+
let out = n.replace(/\[x\]/g, "_x_").replace(/[- :./]/g, "_");
|
|
1381
|
+
if (PYTHON_KEYWORDS.has(out)) out = `${out}_`;
|
|
1382
|
+
if (/^\d/.test(out)) out = `_${out}`;
|
|
1383
|
+
return out;
|
|
1384
|
+
};
|
|
1385
|
+
var pySnakeName = (name) => {
|
|
1386
|
+
if (!name) return "";
|
|
1387
|
+
const cleaned = name.replace(/\[x\]/g, "").replace(/[:./]/g, "_");
|
|
1388
|
+
return snakeCase(cleaned);
|
|
1389
|
+
};
|
|
1390
|
+
var pyFieldName = (n, formatName2 = snakeCase) => {
|
|
1391
|
+
const cleaned = n.replace(/\[x\]/g, "").replace(/[:./]/g, "_");
|
|
1392
|
+
const out = formatName2(cleaned);
|
|
1393
|
+
return PYTHON_KEYWORDS.has(out) ? `${out}_` : out;
|
|
1394
|
+
};
|
|
1395
|
+
var pyProfileClassName = (schema) => {
|
|
1396
|
+
const name = pascalCase(normalizePyName(schema.identifier.name));
|
|
1397
|
+
if (schema.base.name === "Extension") {
|
|
1398
|
+
return name.endsWith("Extension") ? name : `${name}Extension`;
|
|
1399
|
+
}
|
|
1400
|
+
return name.endsWith("Profile") ? name : `${name}Profile`;
|
|
1401
|
+
};
|
|
1402
|
+
var pyProfileModuleName = (tsIndex, schema) => {
|
|
1403
|
+
const baseSchema = tsIndex.findLastSpecialization(schema);
|
|
1404
|
+
const baseName = snakeCase(normalizePyName(baseSchema.identifier.name));
|
|
1405
|
+
const profileName = snakeCase(normalizePyName(schema.identifier.name));
|
|
1406
|
+
return `${baseName}_${profileName}`;
|
|
1407
|
+
};
|
|
1408
|
+
var pySliceStaticName = (name) => {
|
|
1409
|
+
const cleaned = name.replace(/\[x]/g, "").replace(/[^a-zA-Z0-9_]/g, "_");
|
|
1410
|
+
return `_${snakeCase(cleaned)}_slice_match`;
|
|
1411
|
+
};
|
|
1412
|
+
var pyValueFieldName = (id, formatName2 = snakeCase) => formatName2(`value${pascalCase(normalizePyName(id.name))}`);
|
|
1413
|
+
var resolveProfileMethodBaseNames = (extensions, sliceDefs) => {
|
|
1414
|
+
const extensionsRecord = {};
|
|
1415
|
+
for (const ext of extensions) {
|
|
1416
|
+
if (!ext.url) continue;
|
|
1417
|
+
extensionsRecord[`${ext.url}:${ext.path}`] = snakeCase(ext.nameCandidates.recommended);
|
|
1418
|
+
}
|
|
1419
|
+
const slicesRecord = {};
|
|
1420
|
+
for (const s of sliceDefs) {
|
|
1421
|
+
slicesRecord[`${s.fieldName}:${s.sliceName}`] = snakeCase(s.nameCandidates.recommended);
|
|
1422
|
+
}
|
|
1423
|
+
const allBaseNames = /* @__PURE__ */ new Set([...Object.values(extensionsRecord), ...Object.values(slicesRecord)]);
|
|
1424
|
+
return { extensions: extensionsRecord, slices: slicesRecord, allBaseNames };
|
|
1425
|
+
};
|
|
1426
|
+
|
|
1427
|
+
// src/api/writer-generator/python/profile-extensions.ts
|
|
1428
|
+
var resolveExtensionProfile = (tsIndex, pkgName, ext) => {
|
|
1429
|
+
if (ext.profile) {
|
|
1430
|
+
const schema2 = tsIndex.resolve(ext.profile);
|
|
1431
|
+
if (schema2) {
|
|
1432
|
+
return {
|
|
1433
|
+
className: pyProfileClassName(schema2),
|
|
1434
|
+
moduleName: pyProfileModuleName(tsIndex, schema2)
|
|
1435
|
+
};
|
|
1436
|
+
}
|
|
1437
|
+
}
|
|
1438
|
+
if (!ext.url) return void 0;
|
|
1439
|
+
const schema = tsIndex.resolveByUrl(pkgName, ext.url);
|
|
1440
|
+
if (!schema || !isProfileTypeSchema(schema)) return void 0;
|
|
1441
|
+
if (schema.identifier.package !== pkgName) return void 0;
|
|
1442
|
+
return {
|
|
1443
|
+
className: pyProfileClassName(schema),
|
|
1444
|
+
moduleName: pyProfileModuleName(tsIndex, schema)
|
|
1445
|
+
};
|
|
1446
|
+
};
|
|
1447
|
+
var generateExtensionMethods = (w, tsIndex, flatProfile, className, extensionBaseNames) => {
|
|
1448
|
+
const pkgName = flatProfile.identifier.package;
|
|
1449
|
+
for (const ext of flatProfile.extensions ?? []) {
|
|
1450
|
+
if (!ext.url) continue;
|
|
1451
|
+
const baseName = extensionBaseNames[`${ext.url}:${ext.path}`] ?? snakeCase(ext.nameCandidates.recommended);
|
|
1452
|
+
const targetPath = ext.path.split(".").filter((segment) => segment !== "extension");
|
|
1453
|
+
const extProfileInfo = resolveExtensionProfile(tsIndex, pkgName, ext);
|
|
1454
|
+
if (ext.isComplex && ext.subExtensions) {
|
|
1455
|
+
generateComplexExtensionGetter(w, ext, baseName, targetPath, extProfileInfo);
|
|
1456
|
+
generateComplexExtensionSetter(w, ext, className, baseName, targetPath, extProfileInfo);
|
|
1457
|
+
} else if (ext.valueFieldTypes?.length === 1 && ext.valueFieldTypes[0]) {
|
|
1458
|
+
const valueType = ext.valueFieldTypes[0];
|
|
1459
|
+
const valueField = pyValueFieldName(valueType, w.nameFormatFunction);
|
|
1460
|
+
const pyType = pyTypeFromIdentifier(valueType);
|
|
1461
|
+
generateSingleValueExtensionGetter(w, ext, baseName, targetPath, valueField, pyType, extProfileInfo);
|
|
1462
|
+
generateSingleValueExtensionSetter(w, ext, className, baseName, targetPath, valueField, extProfileInfo);
|
|
1463
|
+
} else {
|
|
1464
|
+
generateGenericExtensionGetter(w, ext, baseName, targetPath, extProfileInfo);
|
|
1465
|
+
generateGenericExtensionSetter(w, ext, className, baseName, targetPath, extProfileInfo);
|
|
1466
|
+
}
|
|
1467
|
+
}
|
|
1468
|
+
};
|
|
1469
|
+
var emitExtLookup = (w, ext, targetPath) => {
|
|
1470
|
+
if (targetPath.length === 0) {
|
|
1471
|
+
w.line(`exts = getattr(self._resource, "extension", None) or []`);
|
|
1472
|
+
} else {
|
|
1473
|
+
w.line(
|
|
1474
|
+
`target = ensure_path(self._resource.model_dump(by_alias=True, exclude_none=True) if hasattr(self._resource, "model_dump") else self._resource, ${JSON.stringify(targetPath)})`
|
|
1475
|
+
);
|
|
1476
|
+
w.line(`exts = target.get("extension", []) if isinstance(target, dict) else []`);
|
|
1477
|
+
}
|
|
1478
|
+
w.line(`ext = next((e for e in exts if is_extension(e, ${JSON.stringify(ext.url)})), None)`);
|
|
1479
|
+
};
|
|
1480
|
+
var emitExtPush = (w, targetPath, extExpr) => {
|
|
1481
|
+
if (targetPath.length === 0) {
|
|
1482
|
+
w.line(`push_extension(self._resource, ${extExpr})`);
|
|
1483
|
+
} else {
|
|
1484
|
+
w.line(`target = ensure_path(self._resource, ${JSON.stringify(targetPath)})`);
|
|
1485
|
+
w.line(`push_extension(target, ${extExpr})`);
|
|
1486
|
+
}
|
|
1487
|
+
};
|
|
1488
|
+
var emitGetterOverloads = (w, methodName, flatPyType, extProfileInfo) => {
|
|
1489
|
+
const profileClass = extProfileInfo?.className;
|
|
1490
|
+
w.line("@overload");
|
|
1491
|
+
w.line(`def ${methodName}(self) -> ${flatPyType} | None: ...`);
|
|
1492
|
+
w.line("@overload");
|
|
1493
|
+
w.line(`def ${methodName}(self, mode: Literal["raw"]) -> Extension | None: ...`);
|
|
1494
|
+
if (profileClass) {
|
|
1495
|
+
w.line("@overload");
|
|
1496
|
+
w.line(`def ${methodName}(self, mode: Literal["profile"]) -> ${profileClass} | None: ...`);
|
|
1497
|
+
}
|
|
1498
|
+
const modeType = profileClass ? `Literal["raw", "profile"] | None` : `Literal["raw"] | None`;
|
|
1499
|
+
const returnUnion = profileClass ? `${flatPyType} | Extension | ${profileClass} | None` : `${flatPyType} | Extension | None`;
|
|
1500
|
+
w.line(`def ${methodName}(self, mode: ${modeType} = None) -> ${returnUnion}:`);
|
|
1501
|
+
};
|
|
1502
|
+
var emitGetterModeDispatch = (w, extProfileInfo) => {
|
|
1503
|
+
w.line("ext_obj = ext if not isinstance(ext, dict) else Extension(**ext)");
|
|
1504
|
+
w.line(`if mode == "raw":`);
|
|
1505
|
+
w.indentBlock(() => w.line("return ext_obj"));
|
|
1506
|
+
if (extProfileInfo) {
|
|
1507
|
+
w.line(`if mode == "profile":`);
|
|
1508
|
+
w.indentBlock(() => w.line(`return ${extProfileInfo.className}.apply(ext_obj)`));
|
|
1509
|
+
}
|
|
1510
|
+
};
|
|
1511
|
+
var emitSetterDispatchPreamble = (w, ext, targetPath, extProfileInfo) => {
|
|
1512
|
+
let startedChain = false;
|
|
1513
|
+
if (extProfileInfo) {
|
|
1514
|
+
w.line(`if isinstance(value, ${extProfileInfo.className}):`);
|
|
1515
|
+
w.indentBlock(() => emitExtPush(w, targetPath, "value.to_resource()"));
|
|
1516
|
+
startedChain = true;
|
|
1517
|
+
}
|
|
1518
|
+
const keyword = startedChain ? "elif" : "if";
|
|
1519
|
+
w.line(`${keyword} is_extension(value):`);
|
|
1520
|
+
w.indentBlock(() => {
|
|
1521
|
+
w.line(`if _get_key(value, "url") != ${JSON.stringify(ext.url)}:`);
|
|
1522
|
+
w.indentBlock(
|
|
1523
|
+
() => w.line(`raise ValueError(f"Expected extension url '${ext.url}', got {_get_key(value, 'url')!r}")`)
|
|
1524
|
+
);
|
|
1525
|
+
emitExtPush(w, targetPath, "value");
|
|
1526
|
+
});
|
|
1527
|
+
};
|
|
1528
|
+
var buildSetterParamType = (flatType, extProfileInfo) => {
|
|
1529
|
+
const parts = [];
|
|
1530
|
+
if (extProfileInfo) parts.push(extProfileInfo.className);
|
|
1531
|
+
parts.push("Extension", flatType);
|
|
1532
|
+
return `"${parts.join(" | ")}"`;
|
|
1533
|
+
};
|
|
1534
|
+
var generateExtensionGetter = (w, ext, baseName, flatPyType, targetPath, extProfileInfo, emitFlatReturn) => {
|
|
1535
|
+
emitGetterOverloads(w, `get_${baseName}`, flatPyType, extProfileInfo);
|
|
1536
|
+
w.indentBlock(() => {
|
|
1537
|
+
emitExtLookup(w, ext, targetPath);
|
|
1538
|
+
w.line("if ext is None:");
|
|
1539
|
+
w.indentBlock(() => w.line("return None"));
|
|
1540
|
+
emitGetterModeDispatch(w, extProfileInfo);
|
|
1541
|
+
emitFlatReturn();
|
|
1542
|
+
});
|
|
1543
|
+
w.line();
|
|
1544
|
+
};
|
|
1545
|
+
var generateComplexExtensionGetter = (w, ext, baseName, targetPath, extProfileInfo) => {
|
|
1546
|
+
generateExtensionGetter(w, ext, baseName, "dict[str, Any]", targetPath, extProfileInfo, () => {
|
|
1547
|
+
const configItems = (ext.subExtensions ?? []).map((sub) => {
|
|
1548
|
+
const valueField = sub.valueFieldType ? pyValueFieldName(sub.valueFieldType, w.nameFormatFunction) : "value";
|
|
1549
|
+
const isArray = sub.max === "*";
|
|
1550
|
+
return `{"name": ${JSON.stringify(sub.url)}, "valueField": ${JSON.stringify(valueField)}, "isArray": ${isArray ? "True" : "False"}}`;
|
|
1551
|
+
});
|
|
1552
|
+
w.line(`config = [${configItems.join(", ")}]`);
|
|
1553
|
+
w.line("return extract_complex_extension(ext, config)");
|
|
1554
|
+
});
|
|
1555
|
+
};
|
|
1556
|
+
var generateExtensionSetter = (w, ext, className, baseName, flatParamType, targetPath, extProfileInfo, emitElseBody) => {
|
|
1557
|
+
const paramType = buildSetterParamType(flatParamType, extProfileInfo);
|
|
1558
|
+
w.line(`def set_${baseName}(self, value: ${paramType}) -> "${className}":`);
|
|
1559
|
+
w.indentBlock(() => {
|
|
1560
|
+
emitSetterDispatchPreamble(w, ext, targetPath, extProfileInfo);
|
|
1561
|
+
w.line("else:");
|
|
1562
|
+
w.indentBlock(emitElseBody);
|
|
1563
|
+
w.line("return self");
|
|
1564
|
+
});
|
|
1565
|
+
w.line();
|
|
1566
|
+
};
|
|
1567
|
+
var generateComplexExtensionSetter = (w, ext, className, baseName, targetPath, extProfileInfo) => {
|
|
1568
|
+
generateExtensionSetter(w, ext, className, baseName, "dict[str, Any]", targetPath, extProfileInfo, () => {
|
|
1569
|
+
w.line("sub_extensions = []");
|
|
1570
|
+
for (const sub of ext.subExtensions ?? []) {
|
|
1571
|
+
const valueField = sub.valueFieldType ? pyValueFieldName(sub.valueFieldType, w.nameFormatFunction) : "value";
|
|
1572
|
+
if (sub.max === "*") {
|
|
1573
|
+
w.line(`for item in value.get(${JSON.stringify(sub.url)}, []):`);
|
|
1574
|
+
w.indentBlock(() => {
|
|
1575
|
+
w.line(
|
|
1576
|
+
`sub_extensions.append({"url": ${JSON.stringify(sub.url)}, ${JSON.stringify(valueField)}: item})`
|
|
1577
|
+
);
|
|
1578
|
+
});
|
|
1579
|
+
} else {
|
|
1580
|
+
w.line(`if value.get(${JSON.stringify(sub.url)}) is not None:`);
|
|
1581
|
+
w.indentBlock(() => {
|
|
1582
|
+
w.line(
|
|
1583
|
+
`sub_extensions.append({"url": ${JSON.stringify(sub.url)}, ${JSON.stringify(valueField)}: value[${JSON.stringify(sub.url)}]})`
|
|
1584
|
+
);
|
|
1585
|
+
});
|
|
1586
|
+
}
|
|
1587
|
+
}
|
|
1588
|
+
const extObj = `Extension(url=${JSON.stringify(ext.url)}, extension=sub_extensions)`;
|
|
1589
|
+
emitExtPush(w, targetPath, extObj);
|
|
1590
|
+
});
|
|
1591
|
+
};
|
|
1592
|
+
var generateSingleValueExtensionGetter = (w, ext, baseName, targetPath, valueField, pyType, extProfileInfo) => {
|
|
1593
|
+
generateExtensionGetter(w, ext, baseName, pyType, targetPath, extProfileInfo, () => {
|
|
1594
|
+
w.line(`return cast('${pyType} | None', get_extension_value(ext, ${JSON.stringify(valueField)}))`);
|
|
1595
|
+
});
|
|
1596
|
+
};
|
|
1597
|
+
var generateSingleValueExtensionSetter = (w, ext, className, baseName, targetPath, valueField, extProfileInfo) => {
|
|
1598
|
+
generateExtensionSetter(w, ext, className, baseName, "Any", targetPath, extProfileInfo, () => {
|
|
1599
|
+
emitExtPush(w, targetPath, `Extension(url=${JSON.stringify(ext.url)}, ${valueField}=value)`);
|
|
1600
|
+
});
|
|
1601
|
+
};
|
|
1602
|
+
var generateGenericExtensionGetter = (w, ext, baseName, targetPath, extProfileInfo) => {
|
|
1603
|
+
generateExtensionGetter(w, ext, baseName, "dict[str, Any]", targetPath, extProfileInfo, () => {
|
|
1604
|
+
w.line(
|
|
1605
|
+
'return cast("dict[str, Any] | None", ext if isinstance(ext, dict) else ext.model_dump(by_alias=True, exclude_none=True))'
|
|
1606
|
+
);
|
|
1607
|
+
});
|
|
1608
|
+
};
|
|
1609
|
+
var generateGenericExtensionSetter = (w, ext, className, baseName, targetPath, extProfileInfo) => {
|
|
1610
|
+
generateExtensionSetter(w, ext, className, baseName, "dict[str, Any]", targetPath, extProfileInfo, () => {
|
|
1611
|
+
emitExtPush(w, targetPath, `{"url": ${JSON.stringify(ext.url)}, **value}`);
|
|
1612
|
+
});
|
|
1613
|
+
};
|
|
1614
|
+
|
|
1615
|
+
// src/api/writer-generator/python/profile-slices.ts
|
|
1616
|
+
var collectRequiredSliceNames = (field) => {
|
|
1617
|
+
if (!field.array || !field.slicing?.slices) return void 0;
|
|
1618
|
+
if (field.slicing.discriminator?.some((d) => d.type === "type")) return void 0;
|
|
1619
|
+
const names = Object.entries(field.slicing.slices).filter(([_, s]) => s.min !== void 0 && s.min >= 1 && s.match && Object.keys(s.match).length > 0).map(([name]) => name);
|
|
1620
|
+
return names.length > 0 ? names : void 0;
|
|
1621
|
+
};
|
|
1622
|
+
var generateStaticSliceFields = (w, sliceDefs) => {
|
|
1623
|
+
for (const sliceDef of sliceDefs) {
|
|
1624
|
+
const staticName = pySliceStaticName(sliceDef.sliceName);
|
|
1625
|
+
w.line(`${staticName}: dict[str, Any] = ${JSON.stringify(sliceDef.match)}`);
|
|
1626
|
+
}
|
|
1627
|
+
if (sliceDefs.length > 0) w.line();
|
|
1628
|
+
};
|
|
1629
|
+
var normalizeMatchForPython = (tsIndex, match, schema) => {
|
|
1630
|
+
if (!schema || !("fields" in schema) || !schema.fields) return match;
|
|
1631
|
+
const result = {};
|
|
1632
|
+
for (const [key, value] of Object.entries(match)) {
|
|
1633
|
+
const fieldDef = schema.fields[key];
|
|
1634
|
+
if (!fieldDef || !isNotChoiceDeclarationField(fieldDef)) {
|
|
1635
|
+
result[key] = value;
|
|
1636
|
+
continue;
|
|
1637
|
+
}
|
|
1638
|
+
const nestedSchema = fieldDef.type ? tsIndex.resolveType(fieldDef.type) : void 0;
|
|
1639
|
+
const normalizeOne = (v) => v !== null && typeof v === "object" && !Array.isArray(v) ? normalizeMatchForPython(tsIndex, v, nestedSchema) : v;
|
|
1640
|
+
if (Array.isArray(value)) {
|
|
1641
|
+
result[key] = value.map(normalizeOne);
|
|
1642
|
+
} else if (value !== null && typeof value === "object") {
|
|
1643
|
+
const normalized = normalizeOne(value);
|
|
1644
|
+
result[key] = fieldDef.array ? [normalized] : normalized;
|
|
1645
|
+
} else {
|
|
1646
|
+
result[key] = value;
|
|
1647
|
+
}
|
|
1648
|
+
}
|
|
1649
|
+
return result;
|
|
1650
|
+
};
|
|
1651
|
+
var extractTypeDiscriminatorResource = (isTypeDiscriminated, rawMatch) => {
|
|
1652
|
+
if (!isTypeDiscriminated || !rawMatch) return void 0;
|
|
1653
|
+
for (const val of Object.values(rawMatch)) {
|
|
1654
|
+
if (val !== null && typeof val === "object" && !Array.isArray(val)) {
|
|
1655
|
+
const rt = val.resourceType;
|
|
1656
|
+
if (typeof rt === "string") return rt;
|
|
1657
|
+
}
|
|
1658
|
+
}
|
|
1659
|
+
return void 0;
|
|
1660
|
+
};
|
|
1661
|
+
var collectSliceDefs = (tsIndex, flatProfile) => {
|
|
1662
|
+
const pkgName = flatProfile.identifier.package;
|
|
1663
|
+
return Object.entries(flatProfile.fields).flatMap(([fieldName, field]) => {
|
|
1664
|
+
if (!isNotChoiceDeclarationField(field) || !field.slicing?.slices || !field.type) return [];
|
|
1665
|
+
const choiceBaseNames = /* @__PURE__ */ new Set();
|
|
1666
|
+
const baseSchema = tsIndex.resolveType(field.type);
|
|
1667
|
+
if (baseSchema && "fields" in baseSchema && baseSchema.fields) {
|
|
1668
|
+
for (const [n, f] of Object.entries(baseSchema.fields)) {
|
|
1669
|
+
if (isChoiceDeclarationField(f)) choiceBaseNames.add(n);
|
|
1670
|
+
}
|
|
1671
|
+
}
|
|
1672
|
+
return Object.entries(field.slicing.slices).filter(([_, slice]) => Object.keys(slice.match ?? {}).length > 0).map(([sliceName, slice]) => {
|
|
1673
|
+
const matchFields = Object.keys(slice.match ?? {});
|
|
1674
|
+
const required = (slice.required ?? []).filter(
|
|
1675
|
+
(name) => !matchFields.includes(name) && !choiceBaseNames.has(name)
|
|
1676
|
+
);
|
|
1677
|
+
const cc = slice.elements ? tsIndex.constrainedChoice(pkgName, field.type, slice.elements) : void 0;
|
|
1678
|
+
const constrainedChoice = cc && !isPrimitiveIdentifier(cc.variantType) ? cc : void 0;
|
|
1679
|
+
const isTypeDiscriminated = field.slicing?.discriminator?.some((d) => d.type === "type") ?? false;
|
|
1680
|
+
const typeDiscriminatorResource = extractTypeDiscriminatorResource(
|
|
1681
|
+
isTypeDiscriminated,
|
|
1682
|
+
slice.match
|
|
1683
|
+
);
|
|
1684
|
+
return {
|
|
1685
|
+
fieldName,
|
|
1686
|
+
sliceName,
|
|
1687
|
+
match: normalizeMatchForPython(tsIndex, slice.match ?? {}, baseSchema),
|
|
1688
|
+
required,
|
|
1689
|
+
array: Boolean(field.array),
|
|
1690
|
+
max: slice.max ?? 0,
|
|
1691
|
+
constrainedChoice,
|
|
1692
|
+
elementTypeName: field.type && !isPrimitiveIdentifier(field.type) ? pyTypeFromIdentifier(field.type) : void 0,
|
|
1693
|
+
elementTypeId: field.type && !isPrimitiveIdentifier(field.type) ? field.type : void 0,
|
|
1694
|
+
isTypeDiscriminated,
|
|
1695
|
+
typeDiscriminatorResource,
|
|
1696
|
+
nameCandidates: slice.nameCandidates
|
|
1697
|
+
};
|
|
1698
|
+
});
|
|
1699
|
+
});
|
|
1700
|
+
};
|
|
1701
|
+
var sliceElementRetType = (sliceDef) => sliceDef.elementTypeName && sliceDef.typeDiscriminatorResource ? `${sliceDef.elementTypeName}[${sliceDef.typeDiscriminatorResource}]` : sliceDef.elementTypeName ?? "Any";
|
|
1702
|
+
var generateSliceGetters = (w, sliceDefs, sliceBaseNames) => {
|
|
1703
|
+
for (const sliceDef of sliceDefs) {
|
|
1704
|
+
const baseName = sliceBaseNames[`${sliceDef.fieldName}:${sliceDef.sliceName}`] ?? sliceDef.nameCandidates.recommended;
|
|
1705
|
+
const staticName = pySliceStaticName(sliceDef.sliceName);
|
|
1706
|
+
const fieldName = pyFieldName(sliceDef.fieldName, w.nameFormatFunction);
|
|
1707
|
+
const matchKeys = JSON.stringify(Object.keys(sliceDef.match));
|
|
1708
|
+
if (sliceDef.isTypeDiscriminated) {
|
|
1709
|
+
const retType = sliceElementRetType(sliceDef);
|
|
1710
|
+
const isUnbounded = sliceDef.array && sliceDef.max === 0;
|
|
1711
|
+
if (isUnbounded) {
|
|
1712
|
+
w.line(`def get_${baseName}(self, mode: str | None = None) -> list[${retType}] | None:`);
|
|
1713
|
+
w.indentBlock(() => {
|
|
1714
|
+
w.line(`match = self.__class__.${staticName}`);
|
|
1715
|
+
w.line(
|
|
1716
|
+
`result = get_array_slices(getattr(self._resource, ${JSON.stringify(fieldName)}, None), match)`
|
|
1717
|
+
);
|
|
1718
|
+
w.line(`return cast('list[${retType}] | None', result or None)`);
|
|
1719
|
+
});
|
|
1720
|
+
} else {
|
|
1721
|
+
w.line(`def get_${baseName}(self, mode: str | None = None) -> ${retType} | None:`);
|
|
1722
|
+
w.indentBlock(() => {
|
|
1723
|
+
w.line(`match = self.__class__.${staticName}`);
|
|
1724
|
+
w.line(
|
|
1725
|
+
`return cast('${retType} | None', get_array_slice(getattr(self._resource, ${JSON.stringify(fieldName)}, None), match))`
|
|
1726
|
+
);
|
|
1727
|
+
});
|
|
1728
|
+
}
|
|
1729
|
+
} else {
|
|
1730
|
+
const flatRetType = "dict[str, Any]";
|
|
1731
|
+
const rawRetType = sliceDef.elementTypeName ?? "Any";
|
|
1732
|
+
w.line("@overload");
|
|
1733
|
+
w.line(`def get_${baseName}(self) -> ${flatRetType} | None: ...`);
|
|
1734
|
+
w.line("@overload");
|
|
1735
|
+
w.line(`def get_${baseName}(self, mode: Literal["raw"]) -> ${rawRetType} | None: ...`);
|
|
1736
|
+
w.line(
|
|
1737
|
+
`def get_${baseName}(self, mode: Literal["raw"] | None = None) -> ${flatRetType} | ${rawRetType} | None:`
|
|
1738
|
+
);
|
|
1739
|
+
w.indentBlock(() => {
|
|
1740
|
+
w.line(`match = self.__class__.${staticName}`);
|
|
1741
|
+
if (sliceDef.array) {
|
|
1742
|
+
w.line(
|
|
1743
|
+
`item = get_array_slice(getattr(self._resource, ${JSON.stringify(fieldName)}, None), match)`
|
|
1744
|
+
);
|
|
1745
|
+
} else {
|
|
1746
|
+
w.line(`item = getattr(self._resource, ${JSON.stringify(fieldName)}, None)`);
|
|
1747
|
+
w.line("if item is None or not matches_value(item, match):");
|
|
1748
|
+
w.indentBlock(() => {
|
|
1749
|
+
w.line("return None");
|
|
1750
|
+
});
|
|
1751
|
+
}
|
|
1752
|
+
w.line('if mode == "raw":');
|
|
1753
|
+
w.indentBlock(() => {
|
|
1754
|
+
w.line(`return cast('${rawRetType} | None', item)`);
|
|
1755
|
+
});
|
|
1756
|
+
w.line(
|
|
1757
|
+
"item_dict = item if isinstance(item, dict) else item.model_dump(by_alias=True, exclude_none=True)"
|
|
1758
|
+
);
|
|
1759
|
+
if (sliceDef.constrainedChoice) {
|
|
1760
|
+
const variant = JSON.stringify(sliceDef.constrainedChoice.variant);
|
|
1761
|
+
w.line(`return unwrap_slice_choice(item_dict, ${matchKeys}, ${variant})`);
|
|
1762
|
+
} else {
|
|
1763
|
+
w.line(`return strip_match_keys(item_dict, ${matchKeys})`);
|
|
1764
|
+
}
|
|
1765
|
+
});
|
|
1766
|
+
}
|
|
1767
|
+
w.line();
|
|
1768
|
+
}
|
|
1769
|
+
};
|
|
1770
|
+
var generateSliceSetters = (w, className, sliceDefs, sliceBaseNames) => {
|
|
1771
|
+
for (const sliceDef of sliceDefs) {
|
|
1772
|
+
const baseName = sliceBaseNames[`${sliceDef.fieldName}:${sliceDef.sliceName}`] ?? sliceDef.nameCandidates.recommended;
|
|
1773
|
+
const staticName = pySliceStaticName(sliceDef.sliceName);
|
|
1774
|
+
const fieldName = pyFieldName(sliceDef.fieldName, w.nameFormatFunction);
|
|
1775
|
+
if (sliceDef.isTypeDiscriminated) {
|
|
1776
|
+
const retType = sliceElementRetType(sliceDef);
|
|
1777
|
+
const isUnbounded = sliceDef.array && sliceDef.max === 0;
|
|
1778
|
+
if (isUnbounded) {
|
|
1779
|
+
w.line(`def set_${baseName}(self, values: list[${retType}]) -> "${className}":`);
|
|
1780
|
+
w.indentBlock(() => {
|
|
1781
|
+
w.line(`match = self.__class__.${staticName}`);
|
|
1782
|
+
w.line(`items = list(getattr(self._resource, ${JSON.stringify(fieldName)}, None) or [])`);
|
|
1783
|
+
w.line("set_array_slices(items, match, values)");
|
|
1784
|
+
w.line(`setattr(self._resource, ${JSON.stringify(fieldName)}, items)`);
|
|
1785
|
+
w.line("return self");
|
|
1786
|
+
});
|
|
1787
|
+
} else {
|
|
1788
|
+
w.line(`def set_${baseName}(self, value: ${retType} | None = None) -> "${className}":`);
|
|
1789
|
+
w.indentBlock(() => {
|
|
1790
|
+
w.line(`match = self.__class__.${staticName}`);
|
|
1791
|
+
w.line(`items = getattr(self._resource, ${JSON.stringify(fieldName)}, None) or []`);
|
|
1792
|
+
w.line("set_array_slice(items, match, value)");
|
|
1793
|
+
w.line(`setattr(self._resource, ${JSON.stringify(fieldName)}, items)`);
|
|
1794
|
+
w.line("return self");
|
|
1795
|
+
});
|
|
1796
|
+
}
|
|
1797
|
+
} else {
|
|
1798
|
+
const inputOptional = sliceDef.required.length === 0;
|
|
1799
|
+
const sig = inputOptional ? `def set_${baseName}(self, value: dict[str, Any] | None = None) -> "${className}":` : `def set_${baseName}(self, value: dict[str, Any]) -> "${className}":`;
|
|
1800
|
+
w.line(sig);
|
|
1801
|
+
w.indentBlock(() => {
|
|
1802
|
+
w.line(`match = self.__class__.${staticName}`);
|
|
1803
|
+
const inputExpr = inputOptional ? "(value or {})" : "value";
|
|
1804
|
+
if (sliceDef.constrainedChoice) {
|
|
1805
|
+
const variant = JSON.stringify(sliceDef.constrainedChoice.variant);
|
|
1806
|
+
w.line(`wrapped = wrap_slice_choice(${inputExpr}, ${variant})`);
|
|
1807
|
+
w.line("merged = apply_slice_match(wrapped, match)");
|
|
1808
|
+
} else {
|
|
1809
|
+
w.line(`merged = apply_slice_match(${inputExpr}, match)`);
|
|
1810
|
+
}
|
|
1811
|
+
if (sliceDef.elementTypeName) {
|
|
1812
|
+
w.line(`merged = ${sliceDef.elementTypeName}(**merged)`);
|
|
1813
|
+
}
|
|
1814
|
+
if (sliceDef.array) {
|
|
1815
|
+
w.line(`items = getattr(self._resource, ${JSON.stringify(fieldName)}, None) or []`);
|
|
1816
|
+
w.line("set_array_slice(items, match, merged)");
|
|
1817
|
+
w.line(`setattr(self._resource, ${JSON.stringify(fieldName)}, items)`);
|
|
1818
|
+
} else {
|
|
1819
|
+
w.line(`setattr(self._resource, ${JSON.stringify(fieldName)}, merged)`);
|
|
1820
|
+
}
|
|
1821
|
+
w.line("return self");
|
|
1822
|
+
});
|
|
1823
|
+
}
|
|
1824
|
+
w.line();
|
|
1825
|
+
}
|
|
1826
|
+
};
|
|
1827
|
+
|
|
1828
|
+
// src/api/writer-generator/python/profile-factory.ts
|
|
1829
|
+
var fieldPyType = (field, resolveRef) => {
|
|
1830
|
+
const resolved = resolveRef ? resolveRef(field.type) : field.type;
|
|
1831
|
+
const base = pyTypeFromIdentifier(resolved);
|
|
1832
|
+
if (base === "str" && field.enum && !field.enum.isOpen && field.enum.values.length > 0) {
|
|
1833
|
+
const literal = `Literal[${field.enum.values.map((v) => JSON.stringify(v)).join(", ")}]`;
|
|
1834
|
+
return field.array ? `list[${literal}]` : literal;
|
|
1835
|
+
}
|
|
1836
|
+
return field.array ? `list[${base}]` : base;
|
|
1837
|
+
};
|
|
1838
|
+
var tryPromoteChoice = (field, fields, params, promotedChoices) => {
|
|
1839
|
+
if (!isChoiceDeclarationField(field) || !field.required || field.choices.length !== 1) return;
|
|
1840
|
+
const choiceName = field.choices[0];
|
|
1841
|
+
if (!choiceName) return;
|
|
1842
|
+
const choiceField = fields[choiceName];
|
|
1843
|
+
if (!choiceField || !isChoiceInstanceField(choiceField)) return;
|
|
1844
|
+
const pyType = pyTypeFromIdentifier(choiceField.type) + (choiceField.array ? "[]" : "");
|
|
1845
|
+
params.push({ name: choiceName, pyType, typeId: choiceField.type });
|
|
1846
|
+
promotedChoices.add(choiceName);
|
|
1847
|
+
};
|
|
1848
|
+
var collectBaseRequiredParams = (tsIndex, flatProfile, resolveRef, params, coveredNames) => {
|
|
1849
|
+
const covered = new Set(coveredNames);
|
|
1850
|
+
const baseSchema = tsIndex.resolveType(flatProfile.base);
|
|
1851
|
+
if (!baseSchema || !("fields" in baseSchema) || !baseSchema.fields) return;
|
|
1852
|
+
for (const [name, field] of Object.entries(baseSchema.fields)) {
|
|
1853
|
+
if (covered.has(name)) continue;
|
|
1854
|
+
if (!field.required) continue;
|
|
1855
|
+
if (isChoiceInstanceField(field)) continue;
|
|
1856
|
+
if (isChoiceDeclarationField(field)) continue;
|
|
1857
|
+
if (isNotChoiceDeclarationField(field) && field.type) {
|
|
1858
|
+
const pyType = fieldPyType(field, resolveRef);
|
|
1859
|
+
params.push({ name, pyType, typeId: field.type });
|
|
1860
|
+
}
|
|
1861
|
+
}
|
|
1862
|
+
};
|
|
1863
|
+
var collectProfileFactoryInfo = (tsIndex, flatProfile) => {
|
|
1864
|
+
const autoFields = [];
|
|
1865
|
+
const sliceAutoFields = [];
|
|
1866
|
+
const params = [];
|
|
1867
|
+
const autoAccessors = [];
|
|
1868
|
+
const pendingChoiceInstances = [];
|
|
1869
|
+
const fields = flatProfile.fields;
|
|
1870
|
+
const promotedChoices = /* @__PURE__ */ new Set();
|
|
1871
|
+
const resolveRef = tsIndex.findLastSpecializationByIdentifier;
|
|
1872
|
+
const choiceGroups = /* @__PURE__ */ new Map();
|
|
1873
|
+
for (const [, field] of Object.entries(fields)) {
|
|
1874
|
+
if (isChoiceDeclarationField(field)) {
|
|
1875
|
+
for (const choice of field.choices) choiceGroups.set(choice, field.choices);
|
|
1876
|
+
}
|
|
1877
|
+
}
|
|
1878
|
+
if (isResourceIdentifier(flatProfile.base)) {
|
|
1879
|
+
autoFields.push({ name: "resourceType", value: JSON.stringify(flatProfile.base.name) });
|
|
1880
|
+
}
|
|
1881
|
+
for (const [name, field] of Object.entries(fields)) {
|
|
1882
|
+
if (field.excluded) continue;
|
|
1883
|
+
if (isChoiceInstanceField(field)) {
|
|
1884
|
+
pendingChoiceInstances.push([name, field]);
|
|
1885
|
+
continue;
|
|
1886
|
+
}
|
|
1887
|
+
if (isChoiceDeclarationField(field)) {
|
|
1888
|
+
tryPromoteChoice(field, fields, params, promotedChoices);
|
|
1889
|
+
continue;
|
|
1890
|
+
}
|
|
1891
|
+
if (field.valueConstraint) {
|
|
1892
|
+
const value = JSON.stringify(field.valueConstraint.value);
|
|
1893
|
+
autoFields.push({ name, value: field.array ? `[${value}]` : value });
|
|
1894
|
+
if (isNotChoiceDeclarationField(field) && field.type) {
|
|
1895
|
+
const pyType = fieldPyType(field, resolveRef);
|
|
1896
|
+
autoAccessors.push({ name, pyType, typeId: field.type });
|
|
1897
|
+
}
|
|
1898
|
+
continue;
|
|
1899
|
+
}
|
|
1900
|
+
if (isNotChoiceDeclarationField(field)) {
|
|
1901
|
+
const sliceNames = collectRequiredSliceNames(field);
|
|
1902
|
+
if (sliceNames) {
|
|
1903
|
+
if (field.type) {
|
|
1904
|
+
const pyType = fieldPyType(field, resolveRef);
|
|
1905
|
+
sliceAutoFields.push({ name, pyType, typeId: field.type, sliceNames });
|
|
1906
|
+
autoAccessors.push({ name, pyType, typeId: field.type });
|
|
1907
|
+
}
|
|
1908
|
+
continue;
|
|
1909
|
+
}
|
|
1910
|
+
}
|
|
1911
|
+
if (field.required) {
|
|
1912
|
+
const pyType = fieldPyType(field, resolveRef);
|
|
1913
|
+
params.push({ name, pyType, typeId: field.type });
|
|
1914
|
+
}
|
|
1915
|
+
}
|
|
1916
|
+
collectBaseRequiredParams(tsIndex, flatProfile, resolveRef, params, [
|
|
1917
|
+
...autoFields.map((f) => f.name),
|
|
1918
|
+
...sliceAutoFields.map((f) => f.name),
|
|
1919
|
+
...params.map((f) => f.name),
|
|
1920
|
+
...promotedChoices
|
|
1921
|
+
]);
|
|
1922
|
+
const choiceAccessors = [];
|
|
1923
|
+
for (const [name, field] of pendingChoiceInstances) {
|
|
1924
|
+
if (promotedChoices.has(name)) continue;
|
|
1925
|
+
const pyType = pyTypeFromIdentifier(field.type) + (field.array ? "[]" : "");
|
|
1926
|
+
const choiceSiblings = (choiceGroups.get(name) ?? []).filter((s) => s !== name && !promotedChoices.has(s));
|
|
1927
|
+
choiceAccessors.push({ name, pyType, typeId: field.type, choiceSiblings });
|
|
1928
|
+
}
|
|
1929
|
+
return { autoFields, sliceAutoFields, params, accessors: [...autoAccessors, ...choiceAccessors] };
|
|
1930
|
+
};
|
|
1931
|
+
var buildParamSignature = (factoryInfo, formatName2) => {
|
|
1932
|
+
const parts = [];
|
|
1933
|
+
for (const f of factoryInfo.sliceAutoFields) {
|
|
1934
|
+
parts.push(`${pyFieldName(f.name, formatName2)}: ${f.pyType} | None = None`);
|
|
1935
|
+
}
|
|
1936
|
+
for (const p of factoryInfo.params) {
|
|
1937
|
+
parts.push(`${pyFieldName(p.name, formatName2)}: ${p.pyType}`);
|
|
1938
|
+
}
|
|
1939
|
+
if (parts.length === 0) return "";
|
|
1940
|
+
return `*, ${parts.join(", ")}`;
|
|
1941
|
+
};
|
|
1942
|
+
var buildCallArgs = (factoryInfo, formatName2) => {
|
|
1943
|
+
const parts = [];
|
|
1944
|
+
for (const f of factoryInfo.sliceAutoFields) {
|
|
1945
|
+
const name = pyFieldName(f.name, formatName2);
|
|
1946
|
+
parts.push(`${name}=${name}`);
|
|
1947
|
+
}
|
|
1948
|
+
for (const p of factoryInfo.params) {
|
|
1949
|
+
const name = pyFieldName(p.name, formatName2);
|
|
1950
|
+
parts.push(`${name}=${name}`);
|
|
1951
|
+
}
|
|
1952
|
+
return parts.join(", ");
|
|
1953
|
+
};
|
|
1954
|
+
var generateCreateResource = (w, baseTypeName, annotatedBaseTypeName, isResourceBase, hasParams, factoryInfo) => {
|
|
1955
|
+
const fmt = w.nameFormatFunction;
|
|
1956
|
+
w.line("@classmethod");
|
|
1957
|
+
if (hasParams) {
|
|
1958
|
+
w.line(`def create_resource(cls, ${buildParamSignature(factoryInfo, fmt)}) -> ${annotatedBaseTypeName}:`);
|
|
1959
|
+
} else {
|
|
1960
|
+
w.line(`def create_resource(cls) -> ${annotatedBaseTypeName}:`);
|
|
1961
|
+
}
|
|
1962
|
+
w.indentBlock(() => {
|
|
1963
|
+
for (const f of factoryInfo.sliceAutoFields) {
|
|
1964
|
+
const fieldName = pyFieldName(f.name, fmt);
|
|
1965
|
+
const matchRefs = f.sliceNames.map((s) => `cls.${pySliceStaticName(s)}`);
|
|
1966
|
+
if (matchRefs.length === 1) {
|
|
1967
|
+
w.line(`${fieldName}_with_defaults = ensure_slice_defaults(list(${fieldName} or []), ${matchRefs[0]})`);
|
|
1968
|
+
} else {
|
|
1969
|
+
w.line(`${fieldName}_with_defaults = ensure_slice_defaults(`);
|
|
1970
|
+
w.indentBlock(() => {
|
|
1971
|
+
w.line(`list(${fieldName} or []),`);
|
|
1972
|
+
for (const ref of matchRefs) w.line(`${ref},`);
|
|
1973
|
+
});
|
|
1974
|
+
w.line(")");
|
|
1975
|
+
}
|
|
1976
|
+
}
|
|
1977
|
+
if (factoryInfo.sliceAutoFields.length > 0) w.line();
|
|
1978
|
+
const buildArgs = [];
|
|
1979
|
+
for (const f of factoryInfo.autoFields) {
|
|
1980
|
+
buildArgs.push(`${pyFieldName(f.name, fmt)}=${f.value}`);
|
|
1981
|
+
}
|
|
1982
|
+
for (const f of factoryInfo.sliceAutoFields) {
|
|
1983
|
+
buildArgs.push(`${pyFieldName(f.name, fmt)}=${pyFieldName(f.name, fmt)}_with_defaults`);
|
|
1984
|
+
}
|
|
1985
|
+
for (const p of factoryInfo.params) {
|
|
1986
|
+
buildArgs.push(`${pyFieldName(p.name, fmt)}=${pyFieldName(p.name, fmt)}`);
|
|
1987
|
+
}
|
|
1988
|
+
if (isResourceBase) {
|
|
1989
|
+
buildArgs.push(`meta={"profile": [cls.canonical_url]}`);
|
|
1990
|
+
}
|
|
1991
|
+
if (buildArgs.length <= 2) {
|
|
1992
|
+
w.line(`return build_resource(${baseTypeName}, ${buildArgs.join(", ")})`);
|
|
1993
|
+
} else {
|
|
1994
|
+
w.line(`return build_resource(`);
|
|
1995
|
+
w.indentBlock(() => {
|
|
1996
|
+
w.line(`${baseTypeName},`);
|
|
1997
|
+
for (const arg of buildArgs) {
|
|
1998
|
+
w.line(`${arg},`);
|
|
1999
|
+
}
|
|
2000
|
+
});
|
|
2001
|
+
w.line(")");
|
|
2002
|
+
}
|
|
2003
|
+
});
|
|
2004
|
+
};
|
|
2005
|
+
var generateFieldAccessors = (w, className, factoryInfo, extSliceMethodBaseNames) => {
|
|
2006
|
+
const fmt = w.nameFormatFunction;
|
|
2007
|
+
for (const p of factoryInfo.params) {
|
|
2008
|
+
const fieldName = pyFieldName(p.name, fmt);
|
|
2009
|
+
const methodSuffix = pySnakeName(p.name);
|
|
2010
|
+
w.line(`def get_${methodSuffix}(self) -> ${p.pyType} | None:`);
|
|
2011
|
+
w.indentBlock(() => {
|
|
2012
|
+
w.line(`return cast('${p.pyType} | None', getattr(self._resource, ${JSON.stringify(fieldName)}, None))`);
|
|
2013
|
+
});
|
|
2014
|
+
w.line();
|
|
2015
|
+
w.line(`def set_${methodSuffix}(self, value: ${p.pyType}) -> "${className}":`);
|
|
2016
|
+
w.indentBlock(() => {
|
|
2017
|
+
w.line(`setattr(self._resource, ${JSON.stringify(fieldName)}, value)`);
|
|
2018
|
+
w.line("return self");
|
|
2019
|
+
});
|
|
2020
|
+
w.line();
|
|
2021
|
+
}
|
|
2022
|
+
for (const a of factoryInfo.accessors) {
|
|
2023
|
+
const methodSuffix = pySnakeName(a.name);
|
|
2024
|
+
if (extSliceMethodBaseNames.has(methodSuffix)) continue;
|
|
2025
|
+
const fieldName = pyFieldName(a.name, fmt);
|
|
2026
|
+
w.line(`def get_${methodSuffix}(self) -> ${a.pyType} | None:`);
|
|
2027
|
+
w.indentBlock(() => {
|
|
2028
|
+
w.line(`return cast('${a.pyType} | None', getattr(self._resource, ${JSON.stringify(fieldName)}, None))`);
|
|
2029
|
+
});
|
|
2030
|
+
w.line();
|
|
2031
|
+
w.line(`def set_${methodSuffix}(self, value: ${a.pyType}) -> "${className}":`);
|
|
2032
|
+
w.indentBlock(() => {
|
|
2033
|
+
if (a.choiceSiblings?.length) {
|
|
2034
|
+
for (const sibling of a.choiceSiblings) {
|
|
2035
|
+
w.line(`setattr(self._resource, ${JSON.stringify(pyFieldName(sibling, fmt))}, None)`);
|
|
2036
|
+
}
|
|
2037
|
+
}
|
|
2038
|
+
w.line(`setattr(self._resource, ${JSON.stringify(fieldName)}, value)`);
|
|
2039
|
+
w.line("return self");
|
|
2040
|
+
});
|
|
2041
|
+
w.line();
|
|
2042
|
+
}
|
|
2043
|
+
};
|
|
2044
|
+
|
|
2045
|
+
// src/api/writer-generator/python/profile-validation.ts
|
|
2046
|
+
var collectValidateBody = (flatProfile, resolveRef, errorLines, warningLines, formatName2) => {
|
|
2047
|
+
const helpers = /* @__PURE__ */ new Set();
|
|
2048
|
+
const fields = flatProfile.fields;
|
|
2049
|
+
for (const [name, field] of Object.entries(fields)) {
|
|
2050
|
+
const pyName = pyFieldName(name, formatName2);
|
|
2051
|
+
if (isChoiceInstanceField(field)) {
|
|
2052
|
+
collectProhibitedChoiceValidation(fields, name, pyName, helpers, errorLines);
|
|
2053
|
+
continue;
|
|
2054
|
+
}
|
|
2055
|
+
if (isChoiceDeclarationField(field)) {
|
|
2056
|
+
if (field.required) {
|
|
2057
|
+
helpers.add("validate_choice_required");
|
|
2058
|
+
const pyChoices = field.choices.map((c) => pyFieldName(c, formatName2));
|
|
2059
|
+
errorLines.push(
|
|
2060
|
+
`errors.extend(validate_choice_required(self._resource, profile_name, ${JSON.stringify(pyChoices)}))`
|
|
2061
|
+
);
|
|
2062
|
+
}
|
|
2063
|
+
continue;
|
|
2064
|
+
}
|
|
2065
|
+
if (field.excluded) {
|
|
2066
|
+
helpers.add("validate_excluded");
|
|
2067
|
+
errorLines.push(
|
|
2068
|
+
`errors.extend(validate_excluded(self._resource, profile_name, ${JSON.stringify(pyName)}))`
|
|
2069
|
+
);
|
|
2070
|
+
continue;
|
|
2071
|
+
}
|
|
2072
|
+
if (field.required) {
|
|
2073
|
+
helpers.add("validate_required");
|
|
2074
|
+
errorLines.push(
|
|
2075
|
+
`errors.extend(validate_required(self._resource, profile_name, ${JSON.stringify(pyName)}))`
|
|
2076
|
+
);
|
|
2077
|
+
}
|
|
2078
|
+
if (field.valueConstraint) {
|
|
2079
|
+
helpers.add("validate_fixed_value");
|
|
2080
|
+
const value = JSON.stringify(field.valueConstraint.value);
|
|
2081
|
+
errorLines.push(
|
|
2082
|
+
`errors.extend(validate_fixed_value(self._resource, profile_name, ${JSON.stringify(pyName)}, ${value}))`
|
|
2083
|
+
);
|
|
2084
|
+
}
|
|
2085
|
+
if (isNotChoiceDeclarationField(field)) {
|
|
2086
|
+
if (field.enum) {
|
|
2087
|
+
helpers.add("validate_enum");
|
|
2088
|
+
const target = field.enum.isOpen ? warningLines : errorLines;
|
|
2089
|
+
const listName = field.enum.isOpen ? "warnings" : "errors";
|
|
2090
|
+
target.push(
|
|
2091
|
+
`${listName}.extend(validate_enum(self._resource, profile_name, ${JSON.stringify(pyName)}, ${JSON.stringify(field.enum.values)}))`
|
|
2092
|
+
);
|
|
2093
|
+
}
|
|
2094
|
+
if (field.mustSupport && !field.required) {
|
|
2095
|
+
helpers.add("validate_must_support");
|
|
2096
|
+
warningLines.push(
|
|
2097
|
+
`warnings.extend(validate_must_support(self._resource, profile_name, ${JSON.stringify(pyName)}))`
|
|
2098
|
+
);
|
|
2099
|
+
}
|
|
2100
|
+
if (field.reference && field.reference.length > 0) {
|
|
2101
|
+
helpers.add("validate_reference");
|
|
2102
|
+
const allowed = field.reference.map((ref) => resolveRef(ref).name);
|
|
2103
|
+
errorLines.push(
|
|
2104
|
+
`errors.extend(validate_reference(self._resource, profile_name, ${JSON.stringify(pyName)}, ${JSON.stringify(allowed)}))`
|
|
2105
|
+
);
|
|
2106
|
+
}
|
|
2107
|
+
if (field.slicing?.slices) {
|
|
2108
|
+
collectSliceCardinalityValidation(field, pyName, helpers, errorLines);
|
|
2109
|
+
}
|
|
2110
|
+
}
|
|
2111
|
+
}
|
|
2112
|
+
return helpers;
|
|
2113
|
+
};
|
|
2114
|
+
var collectProhibitedChoiceValidation = (fields, name, pyName, helpers, errorLines) => {
|
|
2115
|
+
const field = fields[name];
|
|
2116
|
+
if (!field || !isChoiceInstanceField(field)) return;
|
|
2117
|
+
const decl = fields[field.choiceOf];
|
|
2118
|
+
if (!decl || !isChoiceDeclarationField(decl) || !decl.prohibited?.includes(name)) return;
|
|
2119
|
+
helpers.add("validate_excluded");
|
|
2120
|
+
errorLines.push(`errors.extend(validate_excluded(self._resource, profile_name, ${JSON.stringify(pyName)}))`);
|
|
2121
|
+
};
|
|
2122
|
+
var collectSliceCardinalityValidation = (field, name, helpers, errorLines) => {
|
|
2123
|
+
if (!field.slicing?.slices) return;
|
|
2124
|
+
for (const [sliceName, slice] of Object.entries(field.slicing.slices)) {
|
|
2125
|
+
if (slice.min === void 0 && slice.max === void 0) continue;
|
|
2126
|
+
const match = slice.match ?? {};
|
|
2127
|
+
if (Object.keys(match).length === 0) continue;
|
|
2128
|
+
const min = slice.min ?? 0;
|
|
2129
|
+
const max = slice.max ?? 0;
|
|
2130
|
+
helpers.add("validate_slice_cardinality");
|
|
2131
|
+
errorLines.push(
|
|
2132
|
+
`errors.extend(validate_slice_cardinality(self._resource, profile_name, ${JSON.stringify(name)}, ${JSON.stringify(match)}, ${JSON.stringify(sliceName)}, ${min}, ${max}))`
|
|
2133
|
+
);
|
|
2134
|
+
}
|
|
2135
|
+
};
|
|
2136
|
+
|
|
2137
|
+
// src/api/writer-generator/python/profile.ts
|
|
2138
|
+
var modulePathForTypeId = (rootPackageName, typeId) => {
|
|
2139
|
+
const pkg = pyFhirPackageByName(rootPackageName, typeId.package);
|
|
2140
|
+
if (isResourceIdentifier(typeId)) return `${pkg}.${snakeCase(typeId.name)}`;
|
|
2141
|
+
if (isNestedIdentifier(typeId)) {
|
|
2142
|
+
const path = canonicalToName2(typeId.url, false);
|
|
2143
|
+
const parentName = path?.split("#")[0];
|
|
2144
|
+
return parentName ? `${pkg}.${snakeCase(parentName)}` : `${pkg}.base`;
|
|
2145
|
+
}
|
|
2146
|
+
return `${pkg}.base`;
|
|
2147
|
+
};
|
|
2148
|
+
var addExactTypeImport = (typeImports, rootPackageName, skipName, typeId) => {
|
|
2149
|
+
if (isPrimitiveIdentifier(typeId) || PRIMITIVE_TYPE_MAP2[typeId.name] !== void 0) return;
|
|
2150
|
+
const name = deriveResourceName(typeId);
|
|
2151
|
+
if (!name || name === skipName) return;
|
|
2152
|
+
const modulePath = modulePathForTypeId(rootPackageName, typeId);
|
|
2153
|
+
const names = typeImports.get(modulePath) ?? /* @__PURE__ */ new Set();
|
|
2154
|
+
names.add(name);
|
|
2155
|
+
typeImports.set(modulePath, names);
|
|
2156
|
+
};
|
|
2157
|
+
var addTypeImport = (typeImports, rootPackageName, skipName, resolveRef, typeId) => {
|
|
2158
|
+
const ids = [typeId];
|
|
2159
|
+
const resolved = resolveRef(typeId);
|
|
2160
|
+
if (resolved !== typeId) ids.push(resolved);
|
|
2161
|
+
for (const id of ids) {
|
|
2162
|
+
if (isPrimitiveIdentifier(id) || PRIMITIVE_TYPE_MAP2[id.name] !== void 0) continue;
|
|
2163
|
+
const name = deriveResourceName(id);
|
|
2164
|
+
if (name === skipName) continue;
|
|
2165
|
+
const modulePath = modulePathForTypeId(rootPackageName, id);
|
|
2166
|
+
let names = typeImports.get(modulePath);
|
|
2167
|
+
if (!names) {
|
|
2168
|
+
names = /* @__PURE__ */ new Set();
|
|
2169
|
+
typeImports.set(modulePath, names);
|
|
2170
|
+
}
|
|
2171
|
+
names.add(name);
|
|
2172
|
+
}
|
|
2173
|
+
};
|
|
2174
|
+
var collectHelperImports = (isResourceBase, factoryInfo, sliceDefs, extensions, validationHelpers) => {
|
|
2175
|
+
const imports = ["build_resource"];
|
|
2176
|
+
if (isResourceBase) imports.push("ensure_profile");
|
|
2177
|
+
if (factoryInfo.sliceAutoFields.length > 0) imports.push("ensure_slice_defaults");
|
|
2178
|
+
if (sliceDefs.length > 0) {
|
|
2179
|
+
const hasNonTyped = sliceDefs.some((s) => !s.isTypeDiscriminated);
|
|
2180
|
+
const hasTypedBounded = sliceDefs.some((s) => s.isTypeDiscriminated && !(s.array && s.max === 0));
|
|
2181
|
+
const hasTypedUnbounded = sliceDefs.some((s) => s.isTypeDiscriminated && s.array && s.max === 0);
|
|
2182
|
+
if (hasNonTyped) imports.push("apply_slice_match", "matches_value", "strip_match_keys");
|
|
2183
|
+
if (hasTypedBounded || hasNonTyped) imports.push("get_array_slice", "set_array_slice");
|
|
2184
|
+
if (hasTypedUnbounded) imports.push("get_array_slices", "set_array_slices");
|
|
2185
|
+
if (sliceDefs.some((s) => s.constrainedChoice)) imports.push("wrap_slice_choice", "unwrap_slice_choice");
|
|
2186
|
+
}
|
|
2187
|
+
if (extensions.length > 0) {
|
|
2188
|
+
imports.push("_get_key", "is_extension", "get_extension_value", "push_extension");
|
|
2189
|
+
if (extensions.some((ext) => ext.isComplex && ext.subExtensions)) imports.push("extract_complex_extension");
|
|
2190
|
+
if (extensions.some((ext) => ext.path.split(".").some((s) => s !== "extension"))) imports.push("ensure_path");
|
|
2191
|
+
}
|
|
2192
|
+
imports.push(...validationHelpers);
|
|
2193
|
+
imports.sort();
|
|
2194
|
+
return imports;
|
|
2195
|
+
};
|
|
2196
|
+
var collectTypeImports = (rootPackageName, baseTypeName, resolveRef, factoryInfo, sliceDefs, extensions, schemas) => {
|
|
2197
|
+
const typeImports = /* @__PURE__ */ new Map();
|
|
2198
|
+
for (const p of factoryInfo.params) addTypeImport(typeImports, rootPackageName, baseTypeName, resolveRef, p.typeId);
|
|
2199
|
+
for (const f of factoryInfo.sliceAutoFields)
|
|
2200
|
+
addTypeImport(typeImports, rootPackageName, baseTypeName, resolveRef, f.typeId);
|
|
2201
|
+
for (const a of factoryInfo.accessors)
|
|
2202
|
+
addTypeImport(typeImports, rootPackageName, baseTypeName, resolveRef, a.typeId);
|
|
2203
|
+
for (const s of sliceDefs) {
|
|
2204
|
+
if (s.elementTypeId) addExactTypeImport(typeImports, rootPackageName, baseTypeName, s.elementTypeId);
|
|
2205
|
+
if (!s.isTypeDiscriminated) continue;
|
|
2206
|
+
if (!s.typeDiscriminatorResource) continue;
|
|
2207
|
+
const resourceId = schemas.find(
|
|
2208
|
+
(schema) => schema.identifier.kind === "resource" && schema.identifier.name === s.typeDiscriminatorResource
|
|
2209
|
+
)?.identifier;
|
|
2210
|
+
if (resourceId) addTypeImport(typeImports, rootPackageName, baseTypeName, resolveRef, resourceId);
|
|
2211
|
+
}
|
|
2212
|
+
for (const ext of extensions) {
|
|
2213
|
+
if (ext.isComplex && ext.subExtensions) continue;
|
|
2214
|
+
if (ext.valueFieldTypes?.length === 1 && ext.valueFieldTypes[0])
|
|
2215
|
+
addExactTypeImport(typeImports, rootPackageName, baseTypeName, ext.valueFieldTypes[0]);
|
|
2216
|
+
}
|
|
2217
|
+
return typeImports;
|
|
2218
|
+
};
|
|
2219
|
+
var collectExtProfileImports = (tsIndex, flatProfile, extensions) => {
|
|
2220
|
+
const extProfileImports = /* @__PURE__ */ new Map();
|
|
2221
|
+
for (const ext of extensions) {
|
|
2222
|
+
if (!ext.url) continue;
|
|
2223
|
+
const info = resolveExtensionProfile(tsIndex, flatProfile.identifier.package, ext);
|
|
2224
|
+
if (info && !extProfileImports.has(info.className)) extProfileImports.set(info.className, info);
|
|
2225
|
+
}
|
|
2226
|
+
return extProfileImports;
|
|
2227
|
+
};
|
|
2228
|
+
var emitModuleImports = (w, flatProfile, isResourceBase, extensions, factoryInfo, typeImports, extProfileImports, helperImports, sliceDefs) => {
|
|
2229
|
+
w.line("from __future__ import annotations");
|
|
2230
|
+
w.line();
|
|
2231
|
+
const usesLiteral = [...factoryInfo.params, ...factoryInfo.accessors, ...factoryInfo.sliceAutoFields].some(
|
|
2232
|
+
(f) => f.pyType.includes("Literal[")
|
|
2233
|
+
);
|
|
2234
|
+
const hasNonTypedSlice = sliceDefs.some((s) => !s.isTypeDiscriminated);
|
|
2235
|
+
const needsOverload = extensions.length > 0 || hasNonTypedSlice;
|
|
2236
|
+
const needsAny = extensions.length > 0 || sliceDefs.length > 0;
|
|
2237
|
+
const needsCast = factoryInfo.params.length > 0 || factoryInfo.accessors.length > 0 || sliceDefs.length > 0 || extensions.length > 0;
|
|
2238
|
+
const typingNames = [];
|
|
2239
|
+
if (needsAny) typingNames.push("Any");
|
|
2240
|
+
if (needsCast) typingNames.push("cast");
|
|
2241
|
+
if (needsOverload || usesLiteral) typingNames.push("Literal");
|
|
2242
|
+
if (needsOverload) typingNames.push("overload");
|
|
2243
|
+
if (typingNames.length > 0) {
|
|
2244
|
+
w.pyImportFrom("typing", ...[...typingNames].sort());
|
|
2245
|
+
w.line();
|
|
2246
|
+
}
|
|
2247
|
+
const baseTypeName = flatProfile.base.name;
|
|
2248
|
+
const basePkg = pyFhirPackageByName(w.opts.rootPackageName, flatProfile.base.package);
|
|
2249
|
+
if (isResourceBase) {
|
|
2250
|
+
w.pyImportFrom(`${basePkg}.${snakeCase(baseTypeName)}`, baseTypeName);
|
|
2251
|
+
} else {
|
|
2252
|
+
w.pyImportFrom(`${basePkg}.base`, baseTypeName);
|
|
2253
|
+
}
|
|
2254
|
+
if (extensions.length > 0) w.pyImportFrom(`${basePkg}.base`, "Extension");
|
|
2255
|
+
for (const [modulePath, names] of [...typeImports.entries()].sort(([a], [b]) => a.localeCompare(b))) {
|
|
2256
|
+
w.pyImportFrom(modulePath, ...[...names].sort());
|
|
2257
|
+
}
|
|
2258
|
+
for (const [extClassName, info] of [...extProfileImports.entries()].sort(([a], [b]) => a.localeCompare(b))) {
|
|
2259
|
+
w.pyImportFrom(`.${info.moduleName}`, extClassName);
|
|
2260
|
+
}
|
|
2261
|
+
w.pyImportFrom(`${w.opts.rootPackageName}.profile_helpers`, ...helperImports);
|
|
2262
|
+
w.line();
|
|
2263
|
+
w.line();
|
|
2264
|
+
};
|
|
2265
|
+
var generateFromResourceMethod = (w, annotatedBaseTypeName, className, isResourceBase) => {
|
|
2266
|
+
w.line("@classmethod");
|
|
2267
|
+
w.line(`def from_resource(cls, resource: ${annotatedBaseTypeName}) -> "${className}":`);
|
|
2268
|
+
w.indentBlock(() => {
|
|
2269
|
+
if (isResourceBase) {
|
|
2270
|
+
w.line('meta = getattr(resource, "meta", None)');
|
|
2271
|
+
w.line('profiles = getattr(meta, "profile", None) if meta is not None else None');
|
|
2272
|
+
w.line("if profiles is None or cls.canonical_url not in profiles:");
|
|
2273
|
+
w.indentBlock(() => {
|
|
2274
|
+
w.line(`raise ValueError(f"${className}: meta.profile must include {cls.canonical_url}")`);
|
|
2275
|
+
});
|
|
2276
|
+
}
|
|
2277
|
+
w.line("profile = cls(resource)");
|
|
2278
|
+
w.line("result = profile.validate()");
|
|
2279
|
+
w.line('if result["errors"]:');
|
|
2280
|
+
w.indentBlock(() => w.line('raise ValueError("; ".join(result["errors"]))'));
|
|
2281
|
+
w.line("return profile");
|
|
2282
|
+
});
|
|
2283
|
+
w.line();
|
|
2284
|
+
};
|
|
2285
|
+
var generateApplyMethod = (w, annotatedBaseTypeName, className, isResourceBase) => {
|
|
2286
|
+
w.line("@classmethod");
|
|
2287
|
+
w.line(`def apply(cls, resource: ${annotatedBaseTypeName}) -> "${className}":`);
|
|
2288
|
+
w.indentBlock(() => {
|
|
2289
|
+
if (isResourceBase) w.line("ensure_profile(resource, cls.canonical_url)");
|
|
2290
|
+
w.line("return cls(resource)");
|
|
2291
|
+
});
|
|
2292
|
+
w.line();
|
|
2293
|
+
};
|
|
2294
|
+
var generateCreateMethod = (w, className, hasParams, factoryInfo) => {
|
|
2295
|
+
w.line("@classmethod");
|
|
2296
|
+
if (hasParams) {
|
|
2297
|
+
w.line(`def create(cls, ${buildParamSignature(factoryInfo, w.nameFormatFunction)}) -> "${className}":`);
|
|
2298
|
+
w.indentBlock(
|
|
2299
|
+
() => w.line(`return cls.apply(cls.create_resource(${buildCallArgs(factoryInfo, w.nameFormatFunction)}))`)
|
|
2300
|
+
);
|
|
2301
|
+
} else {
|
|
2302
|
+
w.line(`def create(cls) -> "${className}":`);
|
|
2303
|
+
w.indentBlock(() => w.line("return cls.apply(cls.create_resource())"));
|
|
2304
|
+
}
|
|
2305
|
+
w.line();
|
|
2306
|
+
};
|
|
2307
|
+
var generateValidateMethod = (w, className, errorLines, warningLines) => {
|
|
2308
|
+
w.line("def validate(self) -> dict[str, list[str]]:");
|
|
2309
|
+
w.indentBlock(() => {
|
|
2310
|
+
w.line(`profile_name = "${className}"`);
|
|
2311
|
+
w.line("errors: list[str] = []");
|
|
2312
|
+
w.line("warnings: list[str] = []");
|
|
2313
|
+
for (const expr of errorLines) w.line(expr);
|
|
2314
|
+
for (const expr of warningLines) w.line(expr);
|
|
2315
|
+
w.line('return {"errors": errors, "warnings": warnings}');
|
|
2316
|
+
});
|
|
2317
|
+
};
|
|
2318
|
+
var generateClassBody = (ctx) => {
|
|
2319
|
+
const {
|
|
2320
|
+
w,
|
|
2321
|
+
tsIndex,
|
|
2322
|
+
flatProfile,
|
|
2323
|
+
baseTypeName,
|
|
2324
|
+
annotatedBaseTypeName,
|
|
2325
|
+
className,
|
|
2326
|
+
isResourceBase,
|
|
2327
|
+
errorLines,
|
|
2328
|
+
warningLines,
|
|
2329
|
+
factoryInfo,
|
|
2330
|
+
sliceDefs,
|
|
2331
|
+
resolvedNames
|
|
2332
|
+
} = ctx;
|
|
2333
|
+
const hasParams = factoryInfo.params.length > 0 || factoryInfo.sliceAutoFields.length > 0;
|
|
2334
|
+
w.line(`def __init__(self, resource: ${annotatedBaseTypeName}) -> None:`);
|
|
2335
|
+
w.indentBlock(() => w.line("self._resource = resource"));
|
|
2336
|
+
w.line();
|
|
2337
|
+
generateFromResourceMethod(w, annotatedBaseTypeName, className, isResourceBase);
|
|
2338
|
+
generateApplyMethod(w, annotatedBaseTypeName, className, isResourceBase);
|
|
2339
|
+
generateCreateResource(w, baseTypeName, annotatedBaseTypeName, isResourceBase, hasParams, factoryInfo);
|
|
2340
|
+
w.line();
|
|
2341
|
+
generateCreateMethod(w, className, hasParams, factoryInfo);
|
|
2342
|
+
w.line(`def to_resource(self) -> ${annotatedBaseTypeName}:`);
|
|
2343
|
+
w.indentBlock(() => w.line("return self._resource"));
|
|
2344
|
+
w.line();
|
|
2345
|
+
if (factoryInfo.params.length > 0 || factoryInfo.accessors.length > 0)
|
|
2346
|
+
generateFieldAccessors(w, className, factoryInfo, resolvedNames.allBaseNames);
|
|
2347
|
+
const extensions = flatProfile.extensions ?? [];
|
|
2348
|
+
if (extensions.length > 0) generateExtensionMethods(w, tsIndex, flatProfile, className, resolvedNames.extensions);
|
|
2349
|
+
if (sliceDefs.length > 0) {
|
|
2350
|
+
generateSliceGetters(w, sliceDefs, resolvedNames.slices);
|
|
2351
|
+
generateSliceSetters(w, className, sliceDefs, resolvedNames.slices);
|
|
2352
|
+
}
|
|
2353
|
+
generateValidateMethod(w, className, errorLines, warningLines);
|
|
2354
|
+
};
|
|
2355
|
+
var generateProfileModule = (w, tsIndex, flatProfile) => {
|
|
2356
|
+
const className = pyProfileClassName(flatProfile);
|
|
2357
|
+
const baseTypeName = flatProfile.base.name;
|
|
2358
|
+
const isResourceBase = isResourceIdentifier(flatProfile.base);
|
|
2359
|
+
const canonicalUrl = flatProfile.identifier.url ?? "";
|
|
2360
|
+
const factoryInfo = collectProfileFactoryInfo(tsIndex, flatProfile);
|
|
2361
|
+
const sliceDefs = collectSliceDefs(tsIndex, flatProfile);
|
|
2362
|
+
const typedResources = [
|
|
2363
|
+
...new Set(
|
|
2364
|
+
sliceDefs.filter((s) => s.isTypeDiscriminated && s.typeDiscriminatorResource).map((s) => s.typeDiscriminatorResource)
|
|
2365
|
+
)
|
|
2366
|
+
];
|
|
2367
|
+
const annotatedBaseTypeName = typedResources.length > 0 ? `${baseTypeName}[${typedResources.join(" | ")}, Resource]` : baseTypeName;
|
|
2368
|
+
const extensions = flatProfile.extensions ?? [];
|
|
2369
|
+
const resolvedNames = resolveProfileMethodBaseNames(extensions, sliceDefs);
|
|
2370
|
+
const errorLines = [];
|
|
2371
|
+
const warningLines = [];
|
|
2372
|
+
const validationHelpers = collectValidateBody(
|
|
2373
|
+
flatProfile,
|
|
2374
|
+
tsIndex.findLastSpecializationByIdentifier,
|
|
2375
|
+
errorLines,
|
|
2376
|
+
warningLines,
|
|
2377
|
+
w.nameFormatFunction
|
|
2378
|
+
);
|
|
2379
|
+
const helperImports = collectHelperImports(isResourceBase, factoryInfo, sliceDefs, extensions, validationHelpers);
|
|
2380
|
+
const typeImports = collectTypeImports(
|
|
2381
|
+
w.opts.rootPackageName,
|
|
2382
|
+
baseTypeName,
|
|
2383
|
+
tsIndex.findLastSpecializationByIdentifier,
|
|
2384
|
+
factoryInfo,
|
|
2385
|
+
sliceDefs,
|
|
2386
|
+
extensions,
|
|
2387
|
+
tsIndex.schemas
|
|
2388
|
+
);
|
|
2389
|
+
const extProfileImports = collectExtProfileImports(tsIndex, flatProfile, extensions);
|
|
2390
|
+
if (typedResources.length > 0) {
|
|
2391
|
+
const basePkg = pyFhirPackageByName(w.opts.rootPackageName, flatProfile.base.package);
|
|
2392
|
+
const resourceModule = `${basePkg}.resource`;
|
|
2393
|
+
const names = typeImports.get(resourceModule) ?? /* @__PURE__ */ new Set();
|
|
2394
|
+
names.add("Resource");
|
|
2395
|
+
typeImports.set(resourceModule, names);
|
|
2396
|
+
}
|
|
2397
|
+
emitModuleImports(
|
|
2398
|
+
w,
|
|
2399
|
+
flatProfile,
|
|
2400
|
+
isResourceBase,
|
|
2401
|
+
extensions,
|
|
2402
|
+
factoryInfo,
|
|
2403
|
+
typeImports,
|
|
2404
|
+
extProfileImports,
|
|
2405
|
+
helperImports,
|
|
2406
|
+
sliceDefs
|
|
2407
|
+
);
|
|
2408
|
+
w.line(`class ${className}:`);
|
|
2409
|
+
w.indentBlock(() => {
|
|
2410
|
+
if (flatProfile.description) {
|
|
2411
|
+
w.line(`"""${flatProfile.description}`);
|
|
2412
|
+
w.line();
|
|
2413
|
+
w.line(`CanonicalURL: ${canonicalUrl}`);
|
|
2414
|
+
w.line(`"""`);
|
|
2415
|
+
w.line();
|
|
2416
|
+
}
|
|
2417
|
+
w.line(`canonical_url: str = ${JSON.stringify(canonicalUrl)}`);
|
|
2418
|
+
w.line();
|
|
2419
|
+
generateStaticSliceFields(w, sliceDefs);
|
|
2420
|
+
generateClassBody({
|
|
2421
|
+
w,
|
|
2422
|
+
tsIndex,
|
|
2423
|
+
flatProfile,
|
|
2424
|
+
baseTypeName,
|
|
2425
|
+
annotatedBaseTypeName,
|
|
2426
|
+
className,
|
|
2427
|
+
isResourceBase,
|
|
2428
|
+
factoryInfo,
|
|
2429
|
+
sliceDefs,
|
|
2430
|
+
resolvedNames,
|
|
2431
|
+
errorLines,
|
|
2432
|
+
warningLines
|
|
2433
|
+
});
|
|
2434
|
+
});
|
|
2435
|
+
w.line();
|
|
2436
|
+
};
|
|
2437
|
+
var generateProfilesInit = (w, tsIndex, profiles) => {
|
|
2438
|
+
w.cat("__init__.py", () => {
|
|
2439
|
+
w.generateDisclaimer();
|
|
2440
|
+
const seen = /* @__PURE__ */ new Set();
|
|
2441
|
+
for (const profile of profiles) {
|
|
2442
|
+
const className = pyProfileClassName(profile);
|
|
2443
|
+
const moduleName = pyProfileModuleName(tsIndex, profile);
|
|
2444
|
+
if (seen.has(className)) continue;
|
|
2445
|
+
seen.add(className);
|
|
2446
|
+
w.pyImportFrom(`.${moduleName}`, className);
|
|
2447
|
+
}
|
|
2448
|
+
w.line();
|
|
2449
|
+
w.squareBlock(["__all__", "="], () => {
|
|
2450
|
+
for (const className of [...seen].sort()) w.line(`'${className}',`);
|
|
1212
2451
|
});
|
|
1213
|
-
};
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
profile: {},
|
|
1225
|
-
"profile-snapshot": {},
|
|
1226
|
-
logical: {}
|
|
1227
|
-
};
|
|
1228
|
-
for (const schema of shemas) {
|
|
1229
|
-
tree[pkgId][schema.identifier.kind][schema.identifier.url] = {};
|
|
1230
|
-
}
|
|
2452
|
+
});
|
|
2453
|
+
};
|
|
2454
|
+
var generateNewProfiles = (w, tsIndex, profiles) => {
|
|
2455
|
+
if (profiles.length === 0) return;
|
|
2456
|
+
w.cd("profiles", () => {
|
|
2457
|
+
for (const profile of profiles) {
|
|
2458
|
+
const moduleName = pyProfileModuleName(tsIndex, profile);
|
|
2459
|
+
w.cat(`${moduleName}.py`, () => {
|
|
2460
|
+
w.generateDisclaimer();
|
|
2461
|
+
generateProfileModule(w, tsIndex, profile);
|
|
2462
|
+
});
|
|
1231
2463
|
}
|
|
1232
|
-
|
|
1233
|
-
};
|
|
1234
|
-
const exportTree = async (filename) => {
|
|
1235
|
-
const tree = entityTree();
|
|
1236
|
-
const raw = filename.endsWith(".yaml") ? YAML.stringify(tree) : JSON.stringify(tree, void 0, 2);
|
|
1237
|
-
await fsPromises.mkdir(Path5.dirname(filename), { recursive: true });
|
|
1238
|
-
await fsPromises.writeFile(filename, raw);
|
|
1239
|
-
};
|
|
1240
|
-
return {
|
|
1241
|
-
_schemaIndex: index,
|
|
1242
|
-
schemas,
|
|
1243
|
-
schemasByPackage: groupByPackages(schemas),
|
|
1244
|
-
register,
|
|
1245
|
-
collectComplexTypes: () => schemas.filter(isComplexTypeTypeSchema),
|
|
1246
|
-
collectResources: () => schemas.filter(isResourceTypeSchema),
|
|
1247
|
-
collectLogicalModels: () => schemas.filter(isLogicalTypeSchema),
|
|
1248
|
-
collectProfiles: () => schemas.filter(isProfileTypeSchema),
|
|
1249
|
-
collectSnapshotProfiles,
|
|
1250
|
-
resolve: resolve6,
|
|
1251
|
-
resolveType,
|
|
1252
|
-
resolveByUrl,
|
|
1253
|
-
tryHierarchy,
|
|
1254
|
-
hierarchy,
|
|
1255
|
-
findLastSpecialization,
|
|
1256
|
-
findLastSpecializationByIdentifier,
|
|
1257
|
-
flatProfile,
|
|
1258
|
-
constrainedChoice,
|
|
1259
|
-
isWithMetaField,
|
|
1260
|
-
entityTree,
|
|
1261
|
-
exportTree,
|
|
1262
|
-
irReport: () => irReport,
|
|
1263
|
-
replaceSchemas: (newSchemas) => mkTypeSchemaIndex(newSchemas, { register, logger, irReport: { ...irReport } })
|
|
1264
|
-
};
|
|
2464
|
+
generateProfilesInit(w, tsIndex, profiles);
|
|
2465
|
+
});
|
|
1265
2466
|
};
|
|
1266
2467
|
|
|
1267
|
-
// src/api/writer-generator/python.ts
|
|
1268
|
-
var
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
integer: "int",
|
|
1276
|
-
unsignedInt: "int",
|
|
1277
|
-
positiveInt: "PositiveInt",
|
|
1278
|
-
integer64: "int",
|
|
1279
|
-
base64Binary: "str",
|
|
1280
|
-
uri: "str",
|
|
1281
|
-
url: "str",
|
|
1282
|
-
canonical: "str",
|
|
1283
|
-
oid: "str",
|
|
1284
|
-
uuid: "str",
|
|
1285
|
-
string: "str",
|
|
1286
|
-
code: "str",
|
|
1287
|
-
markdown: "str",
|
|
1288
|
-
id: "str",
|
|
1289
|
-
xhtml: "str"
|
|
2468
|
+
// src/api/writer-generator/python/writer.ts
|
|
2469
|
+
var resolvePyAssets = (fn) => {
|
|
2470
|
+
const __dirname = Path5.dirname(fileURLToPath(import.meta.url));
|
|
2471
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
2472
|
+
if (__filename.endsWith("dist/index.js")) {
|
|
2473
|
+
return Path5.resolve(__dirname, "..", "assets", "api", "writer-generator", "python", fn);
|
|
2474
|
+
}
|
|
2475
|
+
return Path5.resolve(__dirname, "../../../..", "assets", "api", "writer-generator", "python", fn);
|
|
1290
2476
|
};
|
|
1291
2477
|
var AVAILABLE_STRING_FORMATS = {
|
|
1292
2478
|
snake_case: snakeCase,
|
|
1293
2479
|
PascalCase: pascalCase,
|
|
1294
2480
|
camelCase
|
|
1295
2481
|
};
|
|
1296
|
-
var PYTHON_KEYWORDS = /* @__PURE__ */ new Set([
|
|
1297
|
-
"False",
|
|
1298
|
-
"None",
|
|
1299
|
-
"True",
|
|
1300
|
-
"and",
|
|
1301
|
-
"as",
|
|
1302
|
-
"assert",
|
|
1303
|
-
"async",
|
|
1304
|
-
"await",
|
|
1305
|
-
"break",
|
|
1306
|
-
"class",
|
|
1307
|
-
"continue",
|
|
1308
|
-
"def",
|
|
1309
|
-
"del",
|
|
1310
|
-
"elif",
|
|
1311
|
-
"else",
|
|
1312
|
-
"except",
|
|
1313
|
-
"finally",
|
|
1314
|
-
"for",
|
|
1315
|
-
"from",
|
|
1316
|
-
"global",
|
|
1317
|
-
"if",
|
|
1318
|
-
"import",
|
|
1319
|
-
"in",
|
|
1320
|
-
"is",
|
|
1321
|
-
"lambda",
|
|
1322
|
-
"nonlocal",
|
|
1323
|
-
"not",
|
|
1324
|
-
"or",
|
|
1325
|
-
"pass",
|
|
1326
|
-
"raise",
|
|
1327
|
-
"return",
|
|
1328
|
-
"try",
|
|
1329
|
-
"while",
|
|
1330
|
-
"with",
|
|
1331
|
-
"yield",
|
|
1332
|
-
"List"
|
|
1333
|
-
]);
|
|
1334
2482
|
var MAX_IMPORT_LINE_LENGTH = 100;
|
|
1335
2483
|
var GENERIC_FIELD_REWRITES = {
|
|
1336
2484
|
Coding: { code: "T" },
|
|
1337
2485
|
CodeableConcept: { coding: "Coding[T]" }
|
|
1338
2486
|
};
|
|
2487
|
+
var leafOf2 = (path) => path[path.length - 1] ?? "";
|
|
2488
|
+
var collectResourceGenericTypeVars = (schema) => {
|
|
2489
|
+
const all = /* @__PURE__ */ new Map();
|
|
2490
|
+
const addParams = (s) => {
|
|
2491
|
+
for (const p of s.generic?.params ?? []) {
|
|
2492
|
+
if (!all.has(p.typeVar)) all.set(p.typeVar, p.constraint.name);
|
|
2493
|
+
}
|
|
2494
|
+
};
|
|
2495
|
+
addParams(schema);
|
|
2496
|
+
for (const nested of schema.nested ?? []) addParams(nested);
|
|
2497
|
+
return Array.from(all.entries()).sort(([a], [b]) => a.localeCompare(b)).map(([typeVar, constraint]) => ({ typeVar, constraint }));
|
|
2498
|
+
};
|
|
1339
2499
|
var pyEnumType = (enumDef) => {
|
|
1340
2500
|
const values = enumDef.values.map((e) => `"${e}"`).join(", ");
|
|
1341
2501
|
return enumDef.isOpen ? `Literal[${values}] | str` : `Literal[${values}]`;
|
|
1342
2502
|
};
|
|
1343
|
-
var fixReservedWords = (name) => {
|
|
1344
|
-
return PYTHON_KEYWORDS.has(name) ? `${name}_` : name;
|
|
1345
|
-
};
|
|
1346
|
-
var canonicalToName2 = (canonical, dropFragment = true) => {
|
|
1347
|
-
if (!canonical) return void 0;
|
|
1348
|
-
let localName = canonical.split("/").pop();
|
|
1349
|
-
if (!localName) return void 0;
|
|
1350
|
-
if (dropFragment && localName.includes("#")) {
|
|
1351
|
-
localName = localName.split("#")[0];
|
|
1352
|
-
}
|
|
1353
|
-
if (!localName) return void 0;
|
|
1354
|
-
if (/^\d/.test(localName)) {
|
|
1355
|
-
localName = `number_${localName}`;
|
|
1356
|
-
}
|
|
1357
|
-
return snakeCase(localName);
|
|
1358
|
-
};
|
|
1359
|
-
var deriveResourceName = (id) => {
|
|
1360
|
-
if (id.kind === "nested") {
|
|
1361
|
-
const url = id.url;
|
|
1362
|
-
const path = canonicalToName2(url, false);
|
|
1363
|
-
if (!path) return "";
|
|
1364
|
-
const [resourceName, fragment] = path.split("#");
|
|
1365
|
-
const name = uppercaseFirstLetterOfEach((fragment ?? "").split(".")).join("");
|
|
1366
|
-
return pascalCase([resourceName, name].join(""));
|
|
1367
|
-
}
|
|
1368
|
-
return pascalCase(id.name);
|
|
1369
|
-
};
|
|
1370
|
-
var resolvePyAssets = (fn) => {
|
|
1371
|
-
const __dirname = Path5.dirname(fileURLToPath(import.meta.url));
|
|
1372
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
1373
|
-
if (__filename.endsWith("dist/index.js")) {
|
|
1374
|
-
return Path5.resolve(__dirname, "..", "assets", "api", "writer-generator", "python", fn);
|
|
1375
|
-
} else {
|
|
1376
|
-
return Path5.resolve(__dirname, "../../..", "assets", "api", "writer-generator", "python", fn);
|
|
1377
|
-
}
|
|
1378
|
-
};
|
|
1379
2503
|
var Python = class extends Writer {
|
|
1380
2504
|
nameFormatFunction;
|
|
1381
2505
|
tsIndex;
|
|
@@ -1384,19 +2508,29 @@ var Python = class extends Writer {
|
|
|
1384
2508
|
constructor(options) {
|
|
1385
2509
|
super({ ...options, resolveAssets: options.resolveAssets ?? resolvePyAssets });
|
|
1386
2510
|
this.nameFormatFunction = this.getFieldFormatFunction(options.fieldFormat);
|
|
1387
|
-
this.forFhirpyClient = options
|
|
2511
|
+
this.forFhirpyClient = this.resolveClient(options);
|
|
1388
2512
|
this.fieldFormat = options.fieldFormat;
|
|
1389
2513
|
}
|
|
2514
|
+
/** Resolve which client integration to emit. `client` wins; `fhirpyClient` is the deprecated fallback; default is fhirpy. */
|
|
2515
|
+
resolveClient(options) {
|
|
2516
|
+
if (options.client !== void 0) return options.client === "fhirpy";
|
|
2517
|
+
if (options.fhirpyClient !== void 0) {
|
|
2518
|
+
this.logger()?.warn('python: `fhirpyClient` is deprecated; use `client: "fhirpy" | "none"` instead.');
|
|
2519
|
+
return options.fhirpyClient;
|
|
2520
|
+
}
|
|
2521
|
+
return true;
|
|
2522
|
+
}
|
|
1390
2523
|
async generate(tsIndex) {
|
|
1391
2524
|
this.tsIndex = tsIndex;
|
|
1392
2525
|
const groups = {
|
|
1393
2526
|
groupedComplexTypes: groupByPackages(tsIndex.collectComplexTypes()),
|
|
1394
2527
|
groupedResources: groupByPackages(tsIndex.collectResources())
|
|
1395
2528
|
};
|
|
1396
|
-
this.
|
|
1397
|
-
this.
|
|
2529
|
+
const hasProfiles = (this.opts.generateProfile ?? false) && tsIndex.collectSnapshotProfiles().length > 0;
|
|
2530
|
+
this.generateRootPackages(groups, hasProfiles);
|
|
2531
|
+
this.generateSDKPackages(tsIndex, groups);
|
|
1398
2532
|
}
|
|
1399
|
-
generateRootPackages(groups) {
|
|
2533
|
+
generateRootPackages(groups, hasProfiles) {
|
|
1400
2534
|
this.generateRootInitFile(groups);
|
|
1401
2535
|
if (this.forFhirpyClient) {
|
|
1402
2536
|
if (this.fieldFormat === "camelCase") {
|
|
@@ -1405,20 +2539,24 @@ var Python = class extends Writer {
|
|
|
1405
2539
|
this.copyAssets(resolvePyAssets("fhirpy_base_model.py"), "fhirpy_base_model.py");
|
|
1406
2540
|
}
|
|
1407
2541
|
}
|
|
2542
|
+
if (hasProfiles) {
|
|
2543
|
+
this.copyAssets(resolvePyAssets("profile_helpers.py"), "profile_helpers.py");
|
|
2544
|
+
}
|
|
1408
2545
|
this.copyAssets(resolvePyAssets("requirements.txt"), "requirements.txt");
|
|
1409
2546
|
}
|
|
1410
|
-
generateSDKPackages(groups) {
|
|
2547
|
+
generateSDKPackages(tsIndex, groups) {
|
|
1411
2548
|
this.generateComplexTypesPackages(groups.groupedComplexTypes);
|
|
1412
|
-
this.generateResourcePackages(groups);
|
|
2549
|
+
this.generateResourcePackages(tsIndex, groups);
|
|
1413
2550
|
}
|
|
1414
2551
|
generateComplexTypesPackages(groupedComplexTypes) {
|
|
1415
2552
|
for (const [packageName, packageComplexTypes] of Object.entries(groupedComplexTypes)) {
|
|
1416
2553
|
this.cd(`/${snakeCase(packageName)}`, () => {
|
|
1417
|
-
this.generateBasePy(packageComplexTypes);
|
|
2554
|
+
this.generateBasePy(packageName, packageComplexTypes);
|
|
1418
2555
|
});
|
|
1419
2556
|
}
|
|
1420
2557
|
}
|
|
1421
|
-
generateResourcePackages(groups) {
|
|
2558
|
+
generateResourcePackages(tsIndex, groups) {
|
|
2559
|
+
const profilesByPackage = this.opts.generateProfile ? groupByPackages(tsIndex.collectSnapshotProfiles()) : {};
|
|
1422
2560
|
for (const [packageName, packageResources] of Object.entries(groups.groupedResources)) {
|
|
1423
2561
|
this.cd(`/${snakeCase(packageName)}`, () => {
|
|
1424
2562
|
this.generateResourcePackageContent(
|
|
@@ -1426,13 +2564,27 @@ var Python = class extends Writer {
|
|
|
1426
2564
|
packageResources,
|
|
1427
2565
|
groups.groupedComplexTypes[packageName] || []
|
|
1428
2566
|
);
|
|
2567
|
+
const packageProfiles = profilesByPackage[packageName];
|
|
2568
|
+
if (packageProfiles && packageProfiles.length > 0) {
|
|
2569
|
+
generateNewProfiles(this, tsIndex, packageProfiles);
|
|
2570
|
+
}
|
|
2571
|
+
});
|
|
2572
|
+
}
|
|
2573
|
+
for (const [packageName, packageProfiles] of Object.entries(profilesByPackage)) {
|
|
2574
|
+
if (groups.groupedResources[packageName]) continue;
|
|
2575
|
+
if (!packageProfiles || packageProfiles.length === 0) continue;
|
|
2576
|
+
this.cd(`/${snakeCase(packageName)}`, () => {
|
|
2577
|
+
generateNewProfiles(this, tsIndex, packageProfiles);
|
|
1429
2578
|
});
|
|
1430
2579
|
}
|
|
1431
2580
|
}
|
|
1432
2581
|
generateResourcePackageContent(packageName, packageResources, packageComplexTypes) {
|
|
1433
|
-
const pyPackageName = this.
|
|
2582
|
+
const pyPackageName = pyFhirPackageByName(this.opts.rootPackageName, packageName);
|
|
1434
2583
|
this.generateResourcePackageInit(pyPackageName, packageResources, packageComplexTypes);
|
|
1435
|
-
|
|
2584
|
+
const hasAnyResourceGenericParams = packageResources.some((s) => collectResourceGenericTypeVars(s).length > 0);
|
|
2585
|
+
if (hasAnyResourceGenericParams) {
|
|
2586
|
+
this.copyAssets(resolvePyAssets("resource_preprocessor.py"), "resource_preprocessor.py");
|
|
2587
|
+
}
|
|
1436
2588
|
for (const schema of packageResources) {
|
|
1437
2589
|
this.generateResourceModule(schema);
|
|
1438
2590
|
}
|
|
@@ -1443,13 +2595,14 @@ var Python = class extends Writer {
|
|
|
1443
2595
|
this.generateDisclaimer();
|
|
1444
2596
|
const pydanticModels = this.collectAndImportAllModels(groups);
|
|
1445
2597
|
this.generateModelRebuilds(pydanticModels);
|
|
2598
|
+
this.importProfileRegistrations(groups);
|
|
1446
2599
|
});
|
|
1447
2600
|
});
|
|
1448
2601
|
}
|
|
1449
2602
|
collectAndImportAllModels(groups) {
|
|
1450
2603
|
const models = [];
|
|
1451
2604
|
for (const packageName of Object.keys(groups.groupedResources)) {
|
|
1452
|
-
const fullPyPackageName = this.
|
|
2605
|
+
const fullPyPackageName = pyFhirPackageByName(this.opts.rootPackageName, packageName);
|
|
1453
2606
|
models.push(...this.importComplexTypes(fullPyPackageName, groups.groupedComplexTypes[packageName]));
|
|
1454
2607
|
models.push(...this.importResources(fullPyPackageName, false, groups.groupedResources[packageName]));
|
|
1455
2608
|
}
|
|
@@ -1461,7 +2614,15 @@ var Python = class extends Writer {
|
|
|
1461
2614
|
this.line(`${modelName}.model_rebuild()`);
|
|
1462
2615
|
}
|
|
1463
2616
|
}
|
|
1464
|
-
|
|
2617
|
+
importProfileRegistrations(groups) {
|
|
2618
|
+
if (!this.opts.generateProfile) return;
|
|
2619
|
+
this.line();
|
|
2620
|
+
for (const packageName of Object.keys(groups.groupedResources)) {
|
|
2621
|
+
const profilesPackage = `${pyFhirPackageByName(this.opts.rootPackageName, packageName)}.profiles`;
|
|
2622
|
+
this.line(`import ${profilesPackage} # noqa: F401`);
|
|
2623
|
+
}
|
|
2624
|
+
}
|
|
2625
|
+
generateBasePy(_packageName, packageComplexTypes) {
|
|
1465
2626
|
const hasGenericTypes = packageComplexTypes.some((s) => s.identifier.name in GENERIC_FIELD_REWRITES);
|
|
1466
2627
|
this.cat("base.py", () => {
|
|
1467
2628
|
this.generateDisclaimer();
|
|
@@ -1498,20 +2659,16 @@ var Python = class extends Writer {
|
|
|
1498
2659
|
this.line();
|
|
1499
2660
|
return baseTypes;
|
|
1500
2661
|
}
|
|
1501
|
-
buildImportLine(
|
|
2662
|
+
buildImportLine(entities, start, maxLen) {
|
|
1502
2663
|
let line = "";
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
if (
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
}
|
|
1509
|
-
line += entity;
|
|
1510
|
-
}
|
|
1511
|
-
if (remaining.length > 0) {
|
|
1512
|
-
line += ", \\";
|
|
2664
|
+
let i = start;
|
|
2665
|
+
while (i < entities.length && line.length < maxLen) {
|
|
2666
|
+
if (line.length > 0) line += ", ";
|
|
2667
|
+
line += entities[i];
|
|
2668
|
+
i++;
|
|
1513
2669
|
}
|
|
1514
|
-
|
|
2670
|
+
if (i < entities.length) line += ", \\";
|
|
2671
|
+
return { line, next: i };
|
|
1515
2672
|
}
|
|
1516
2673
|
importResources(fullPyPackageName, importEmptyResources, packageResources) {
|
|
1517
2674
|
if (!packageResources || packageResources.length === 0) return [];
|
|
@@ -1527,12 +2684,7 @@ var Python = class extends Writer {
|
|
|
1527
2684
|
const moduleName = `${fullPyPackageName}.${snakeCase(resource.identifier.name)}`;
|
|
1528
2685
|
const importNames = this.collectResourceImportNames(resource);
|
|
1529
2686
|
this.pyImportFrom(moduleName, ...importNames);
|
|
1530
|
-
|
|
1531
|
-
if (this.shouldImportResourceFamily(resource)) {
|
|
1532
|
-
const familyName = `${resource.identifier.name}Family`;
|
|
1533
|
-
this.pyImportFrom(`${fullPyPackageName}.resource_families`, familyName);
|
|
1534
|
-
}
|
|
1535
|
-
return names;
|
|
2687
|
+
return [...importNames];
|
|
1536
2688
|
}
|
|
1537
2689
|
collectResourceImportNames(resource) {
|
|
1538
2690
|
const names = [deriveResourceName(resource.identifier)];
|
|
@@ -1542,9 +2694,6 @@ var Python = class extends Writer {
|
|
|
1542
2694
|
}
|
|
1543
2695
|
return names;
|
|
1544
2696
|
}
|
|
1545
|
-
shouldImportResourceFamily(resource) {
|
|
1546
|
-
return resource.identifier.kind === "resource" && (resource.typeFamily?.resources?.length ?? 0) > 0;
|
|
1547
|
-
}
|
|
1548
2697
|
generateExportsDeclaration(packageComplexTypes, allResourceNames) {
|
|
1549
2698
|
this.squareBlock(["__all__", "="], () => {
|
|
1550
2699
|
const allExports = [
|
|
@@ -1557,12 +2706,22 @@ var Python = class extends Writer {
|
|
|
1557
2706
|
});
|
|
1558
2707
|
}
|
|
1559
2708
|
generateResourceModule(schema) {
|
|
2709
|
+
const typeVars = collectResourceGenericTypeVars(schema);
|
|
2710
|
+
const hasResourceGenericParams = typeVars.length > 0;
|
|
1560
2711
|
this.cat(`${snakeCase(schema.identifier.name)}.py`, () => {
|
|
1561
2712
|
this.generateDisclaimer();
|
|
1562
|
-
this.generateDefaultImports(false);
|
|
2713
|
+
this.generateDefaultImports(false, hasResourceGenericParams, true);
|
|
1563
2714
|
this.generateFhirBaseModelImport();
|
|
1564
2715
|
this.line();
|
|
1565
2716
|
this.generateDependenciesImports(schema);
|
|
2717
|
+
if (hasResourceGenericParams) {
|
|
2718
|
+
const pyFhirPackage2 = pyFhirPackageByName(this.opts.rootPackageName, schema.identifier.package);
|
|
2719
|
+
this.pyImportFrom(`${pyFhirPackage2}.resource_preprocessor`, "preprocess_resource_fields");
|
|
2720
|
+
this.line();
|
|
2721
|
+
for (const { typeVar, constraint } of typeVars) {
|
|
2722
|
+
this.line(`${typeVar} = TypeVar('${typeVar}', bound=${constraint}, default=${constraint})`);
|
|
2723
|
+
}
|
|
2724
|
+
}
|
|
1566
2725
|
this.line();
|
|
1567
2726
|
this.generateNestedTypes(schema);
|
|
1568
2727
|
this.line();
|
|
@@ -1587,6 +2746,11 @@ var Python = class extends Writer {
|
|
|
1587
2746
|
if (schema.base) bases.push(schema.base.name);
|
|
1588
2747
|
bases.push(...this.injectSuperClasses(schema.identifier.url));
|
|
1589
2748
|
if (schema.identifier.name in GENERIC_FIELD_REWRITES) bases.push("Generic[T]");
|
|
2749
|
+
const params = schema.generic?.params ?? [];
|
|
2750
|
+
if (params.length > 0) {
|
|
2751
|
+
const typeVars = params.map((p) => p.typeVar).join(", ");
|
|
2752
|
+
bases.push(`Generic[${typeVars}]`);
|
|
2753
|
+
}
|
|
1590
2754
|
return bases;
|
|
1591
2755
|
}
|
|
1592
2756
|
generateClassBody(schema) {
|
|
@@ -1598,10 +2762,26 @@ var Python = class extends Writer {
|
|
|
1598
2762
|
if (isResourceTypeSchema(schema)) {
|
|
1599
2763
|
this.generateResourceTypeField(schema);
|
|
1600
2764
|
}
|
|
1601
|
-
this.generateFields(schema
|
|
2765
|
+
this.generateFields(schema);
|
|
2766
|
+
if (this.opts.generateProfile && schema.identifier.name === "Extension") {
|
|
2767
|
+
this.generateExtensionEqualityMethods();
|
|
2768
|
+
}
|
|
1602
2769
|
if (isResourceTypeSchema(schema)) {
|
|
1603
2770
|
this.generateResourceMethods(schema);
|
|
1604
2771
|
}
|
|
2772
|
+
if ((schema.generic?.params?.length ?? 0) > 0) {
|
|
2773
|
+
this.generateResourcePreprocessorMethod(schema);
|
|
2774
|
+
}
|
|
2775
|
+
}
|
|
2776
|
+
generateResourcePreprocessorMethod(schema) {
|
|
2777
|
+
const pyFhirPackage2 = pyFhirPackageByName(this.opts.rootPackageName, schema.identifier.package);
|
|
2778
|
+
this.line();
|
|
2779
|
+
this.line("@model_validator(mode='before')");
|
|
2780
|
+
this.line("@classmethod");
|
|
2781
|
+
this.line("def _preprocess_resources(cls, data: Any) -> Any:");
|
|
2782
|
+
this.line(" if isinstance(data, dict):");
|
|
2783
|
+
this.line(` return preprocess_resource_fields(data, "${pyFhirPackage2}")`);
|
|
2784
|
+
this.line(" return data");
|
|
1605
2785
|
}
|
|
1606
2786
|
generateModelConfig() {
|
|
1607
2787
|
const extraMode = this.opts.allowExtraFields ? "allow" : "forbid";
|
|
@@ -1625,12 +2805,12 @@ var Python = class extends Writer {
|
|
|
1625
2805
|
});
|
|
1626
2806
|
this.line(")");
|
|
1627
2807
|
}
|
|
1628
|
-
generateFields(schema
|
|
2808
|
+
generateFields(schema) {
|
|
1629
2809
|
const sortedFields = Object.entries(schema.fields ?? []).sort(([a], [b]) => a.localeCompare(b));
|
|
1630
2810
|
const withExtensions = this.shouldAddPrimitiveExtensions(schema);
|
|
1631
2811
|
for (const [fieldName, field] of sortedFields) {
|
|
1632
2812
|
if ("choices" in field && field.choices) continue;
|
|
1633
|
-
const fieldInfo = this.buildFieldInfo(fieldName, field,
|
|
2813
|
+
const fieldInfo = this.buildFieldInfo(fieldName, field, schema);
|
|
1634
2814
|
this.line(`${fieldInfo.name}: ${fieldInfo.type}${fieldInfo.defaultValue}`);
|
|
1635
2815
|
if (withExtensions && "type" in field && isPrimitiveIdentifier(field.type)) {
|
|
1636
2816
|
this.addPrimitiveExtensionField(fieldName, field.array ?? false);
|
|
@@ -1647,23 +2827,24 @@ var Python = class extends Writer {
|
|
|
1647
2827
|
return false;
|
|
1648
2828
|
}
|
|
1649
2829
|
addPrimitiveExtensionField(fieldName, isArray) {
|
|
1650
|
-
const
|
|
2830
|
+
const pyFieldName2 = this.nameFormatFunction(`${fieldName}Extension`);
|
|
1651
2831
|
const alias = `_${fieldName}`;
|
|
1652
2832
|
const typeExpr = isArray ? "PyList[Element | None] | None" : "Element | None";
|
|
1653
2833
|
const aliasSpec = `alias="${alias}", serialization_alias="${alias}"`;
|
|
1654
|
-
this.line(`${
|
|
2834
|
+
this.line(`${pyFieldName2}: ${typeExpr} = Field(None, ${aliasSpec})`);
|
|
1655
2835
|
}
|
|
1656
|
-
buildFieldInfo(fieldName, field,
|
|
1657
|
-
const
|
|
1658
|
-
const fieldType = this.determineFieldType(field, fieldName,
|
|
2836
|
+
buildFieldInfo(fieldName, field, schema) {
|
|
2837
|
+
const pyFieldName2 = fixReservedWords(this.nameFormatFunction(fieldName));
|
|
2838
|
+
const fieldType = this.determineFieldType(field, fieldName, schema);
|
|
1659
2839
|
const defaultValue = this.getFieldDefaultValue(field, fieldName);
|
|
1660
2840
|
return {
|
|
1661
|
-
name:
|
|
2841
|
+
name: pyFieldName2,
|
|
1662
2842
|
type: fieldType,
|
|
1663
2843
|
defaultValue
|
|
1664
2844
|
};
|
|
1665
2845
|
}
|
|
1666
|
-
determineFieldType(field, fieldName,
|
|
2846
|
+
determineFieldType(field, fieldName, schema) {
|
|
2847
|
+
const schemaName = schema.identifier.name;
|
|
1667
2848
|
let fieldType = field ? this.getBaseFieldType(field) : "";
|
|
1668
2849
|
const rewrite = GENERIC_FIELD_REWRITES[schemaName]?.[fieldName];
|
|
1669
2850
|
if (rewrite) {
|
|
@@ -1672,6 +2853,27 @@ var Python = class extends Writer {
|
|
|
1672
2853
|
if (!field.required) fieldType = `${fieldType} | None`;
|
|
1673
2854
|
return fieldType;
|
|
1674
2855
|
}
|
|
2856
|
+
const params = schema.generic?.params ?? [];
|
|
2857
|
+
const directParam = params.find((p) => leafOf2(p.path) === fieldName);
|
|
2858
|
+
if (directParam) {
|
|
2859
|
+
fieldType = directParam.typeVar;
|
|
2860
|
+
if (field.array) fieldType = `PyList[${fieldType}]`;
|
|
2861
|
+
if (!field.required) fieldType = `${fieldType} | None`;
|
|
2862
|
+
return fieldType;
|
|
2863
|
+
}
|
|
2864
|
+
if ("type" in field && field.type && params.length > 0) {
|
|
2865
|
+
assert4(this.tsIndex !== void 0);
|
|
2866
|
+
const target = this.tsIndex.resolveType(field.type);
|
|
2867
|
+
if (target && (isNestedTypeSchema(target) || isSpecializationTypeSchema(target))) {
|
|
2868
|
+
const nestedParams = target.generic?.params ?? [];
|
|
2869
|
+
if (nestedParams.length > 0) {
|
|
2870
|
+
const args = nestedParams.map(
|
|
2871
|
+
(np) => params.find((p) => leafOf2(p.path) === leafOf2(np.path))?.typeVar ?? np.typeVar
|
|
2872
|
+
);
|
|
2873
|
+
fieldType = `${fieldType}[${args.join(", ")}]`;
|
|
2874
|
+
}
|
|
2875
|
+
}
|
|
2876
|
+
}
|
|
1675
2877
|
if ("enum" in field && field.enum) {
|
|
1676
2878
|
const baseTypeName = "type" in field ? field.type.name : "";
|
|
1677
2879
|
if (baseTypeName in GENERIC_FIELD_REWRITES) {
|
|
@@ -1703,19 +2905,30 @@ var Python = class extends Writer {
|
|
|
1703
2905
|
}
|
|
1704
2906
|
return ` = Field(${aliasSpec})`;
|
|
1705
2907
|
}
|
|
1706
|
-
generateResourceMethods(
|
|
1707
|
-
const className = schema.identifier.name.toString();
|
|
2908
|
+
generateResourceMethods(_schema) {
|
|
1708
2909
|
this.line();
|
|
1709
2910
|
this.line("def model_post_init(self, __context: Any) -> None:");
|
|
1710
|
-
this.line(
|
|
2911
|
+
this.line(` self.__pydantic_fields_set__.add("${this.nameFormatFunction("resourceType")}")`);
|
|
1711
2912
|
this.line();
|
|
1712
2913
|
this.line("def to_json(self, indent: int | None = None) -> str:");
|
|
1713
2914
|
this.line(" return self.model_dump_json(exclude_unset=True, exclude_none=True, indent=indent)");
|
|
1714
2915
|
this.line();
|
|
1715
2916
|
this.line("@classmethod");
|
|
1716
|
-
this.line(
|
|
2917
|
+
this.line("def from_json(cls, json: str) -> Self:");
|
|
1717
2918
|
this.line(" return cls.model_validate_json(json)");
|
|
1718
2919
|
}
|
|
2920
|
+
generateExtensionEqualityMethods() {
|
|
2921
|
+
this.line();
|
|
2922
|
+
this.line("def __eq__(self, other: object) -> bool:");
|
|
2923
|
+
this.line(" if not isinstance(other, Extension):");
|
|
2924
|
+
this.line(" return NotImplemented");
|
|
2925
|
+
this.line(
|
|
2926
|
+
" return self.model_dump(by_alias=True, exclude_none=True) == other.model_dump(by_alias=True, exclude_none=True)"
|
|
2927
|
+
);
|
|
2928
|
+
this.line();
|
|
2929
|
+
this.line("def __hash__(self) -> int:");
|
|
2930
|
+
this.line(" return hash(self.url)");
|
|
2931
|
+
}
|
|
1719
2932
|
generateNestedTypes(schema) {
|
|
1720
2933
|
if (!schema.nested) return;
|
|
1721
2934
|
this.line();
|
|
@@ -1723,17 +2936,20 @@ var Python = class extends Writer {
|
|
|
1723
2936
|
this.generateType(subtype);
|
|
1724
2937
|
}
|
|
1725
2938
|
}
|
|
1726
|
-
generateDefaultImports(includeGenericImports) {
|
|
2939
|
+
generateDefaultImports(includeGenericImports, includeResourceGenericImports = false, includeResourceMethods = false) {
|
|
1727
2940
|
this.pyImportFrom("__future__", "annotations");
|
|
1728
|
-
|
|
2941
|
+
const pydanticImports = ["BaseModel", "ConfigDict", "Field", "PositiveInt"];
|
|
2942
|
+
if (includeResourceGenericImports) pydanticImports.push("model_validator");
|
|
2943
|
+
this.pyImportFrom("pydantic", ...pydanticImports.sort());
|
|
1729
2944
|
const typingImports = ["Any", "List as PyList", "Literal"];
|
|
1730
|
-
if (includeGenericImports) {
|
|
2945
|
+
if (includeGenericImports || includeResourceGenericImports) {
|
|
1731
2946
|
typingImports.push("Generic");
|
|
1732
2947
|
}
|
|
1733
2948
|
this.pyImportFrom("typing", ...typingImports.sort());
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
2949
|
+
const typingExtImports = [];
|
|
2950
|
+
if (includeGenericImports || includeResourceGenericImports) typingExtImports.push("TypeVar");
|
|
2951
|
+
if (includeResourceMethods) typingExtImports.push("Self");
|
|
2952
|
+
if (typingExtImports.length > 0) this.pyImportFrom("typing_extensions", ...typingExtImports.sort());
|
|
1737
2953
|
}
|
|
1738
2954
|
generateDependenciesImports(schema) {
|
|
1739
2955
|
if (!schema.dependencies || schema.dependencies.length === 0) return;
|
|
@@ -1749,134 +2965,58 @@ var Python = class extends Writer {
|
|
|
1749
2965
|
const elementUrl = "http://hl7.org/fhir/StructureDefinition/Element";
|
|
1750
2966
|
const element = this.tsIndex.resolveByUrl(schema.identifier.package, elementUrl);
|
|
1751
2967
|
if (!element) return;
|
|
1752
|
-
const
|
|
1753
|
-
this.pyImportFrom(
|
|
2968
|
+
const pyPkg = pyPackage(this.opts.rootPackageName, element.identifier);
|
|
2969
|
+
this.pyImportFrom(pyPkg, "Element");
|
|
1754
2970
|
}
|
|
1755
2971
|
importComplexTypeDependencies(dependencies) {
|
|
1756
2972
|
const complexTypeDeps = dependencies.filter((dep) => dep.kind === "complex-type");
|
|
1757
2973
|
const depsByPackage = this.groupDependenciesByPackage(complexTypeDeps);
|
|
1758
|
-
for (const [
|
|
1759
|
-
this.pyImportFrom(
|
|
2974
|
+
for (const [pyPackage2, names] of Object.entries(depsByPackage)) {
|
|
2975
|
+
this.pyImportFrom(pyPackage2, ...names.sort());
|
|
1760
2976
|
}
|
|
1761
2977
|
}
|
|
1762
2978
|
importResourceDependencies(dependencies) {
|
|
1763
2979
|
const resourceDeps = dependencies.filter((dep) => dep.kind === "resource");
|
|
1764
2980
|
for (const dep of resourceDeps) {
|
|
1765
2981
|
this.pyImportType(dep);
|
|
1766
|
-
const familyName = `${pascalCase(dep.name)}Family`;
|
|
1767
|
-
const familyPackage = `${this.pyFhirPackage(dep)}.resource_families`;
|
|
1768
|
-
this.pyImportFrom(familyPackage, familyName);
|
|
1769
2982
|
}
|
|
1770
2983
|
}
|
|
1771
2984
|
groupDependenciesByPackage(dependencies) {
|
|
1772
2985
|
const grouped = {};
|
|
1773
2986
|
for (const dep of dependencies) {
|
|
1774
|
-
const
|
|
1775
|
-
if (!grouped[
|
|
1776
|
-
grouped[
|
|
2987
|
+
const pyPkg = pyPackage(this.opts.rootPackageName, dep);
|
|
2988
|
+
if (!grouped[pyPkg]) {
|
|
2989
|
+
grouped[pyPkg] = [];
|
|
1777
2990
|
}
|
|
1778
|
-
grouped[
|
|
2991
|
+
grouped[pyPkg].push(dep.name);
|
|
1779
2992
|
}
|
|
1780
2993
|
return grouped;
|
|
1781
2994
|
}
|
|
1782
|
-
pyImportFrom(
|
|
1783
|
-
const oneLine = `from ${
|
|
2995
|
+
pyImportFrom(pyPackage2, ...entities) {
|
|
2996
|
+
const oneLine = `from ${pyPackage2} import ${entities.join(", ")}`;
|
|
1784
2997
|
if (this.shouldUseSingleLineImport(oneLine, entities)) {
|
|
1785
2998
|
this.line(oneLine);
|
|
1786
2999
|
} else {
|
|
1787
|
-
this.writeMultiLineImport(
|
|
3000
|
+
this.writeMultiLineImport(pyPackage2, entities);
|
|
1788
3001
|
}
|
|
1789
3002
|
}
|
|
1790
3003
|
shouldUseSingleLineImport(oneLine, entities) {
|
|
1791
3004
|
return oneLine.length <= MAX_IMPORT_LINE_LENGTH || entities.length === 1;
|
|
1792
3005
|
}
|
|
1793
|
-
writeMultiLineImport(
|
|
1794
|
-
this.line(`from ${
|
|
3006
|
+
writeMultiLineImport(pyPackage2, entities) {
|
|
3007
|
+
this.line(`from ${pyPackage2} import (`);
|
|
1795
3008
|
this.indentBlock(() => {
|
|
1796
|
-
|
|
1797
|
-
while (
|
|
1798
|
-
const line = this.buildImportLine(
|
|
3009
|
+
let i = 0;
|
|
3010
|
+
while (i < entities.length) {
|
|
3011
|
+
const { line, next } = this.buildImportLine(entities, i, MAX_IMPORT_LINE_LENGTH);
|
|
1799
3012
|
this.line(line);
|
|
3013
|
+
i = next;
|
|
1800
3014
|
}
|
|
1801
3015
|
});
|
|
1802
3016
|
this.line(")");
|
|
1803
3017
|
}
|
|
1804
3018
|
pyImportType(identifier) {
|
|
1805
|
-
this.pyImportFrom(this.
|
|
1806
|
-
}
|
|
1807
|
-
generateResourceFamilies(packageResources) {
|
|
1808
|
-
assert4(this.tsIndex !== void 0);
|
|
1809
|
-
const packages = (
|
|
1810
|
-
//this.helper.getPackages(packageResources, this.opts.rootPackageName);
|
|
1811
|
-
Object.keys(groupByPackages(packageResources)).map(
|
|
1812
|
-
(pkgName) => `${this.opts.rootPackageName}.${pkgName.replaceAll(".", "_")}`
|
|
1813
|
-
)
|
|
1814
|
-
);
|
|
1815
|
-
const families = {};
|
|
1816
|
-
for (const resource of this.tsIndex.collectResources()) {
|
|
1817
|
-
const children = (resource.typeFamily?.resources ?? []).map((c) => c.name);
|
|
1818
|
-
if (children.length > 0) {
|
|
1819
|
-
const familyName = `${resource.identifier.name}Family`;
|
|
1820
|
-
families[familyName] = children;
|
|
1821
|
-
}
|
|
1822
|
-
}
|
|
1823
|
-
const exportList = Object.keys(families);
|
|
1824
|
-
if (exportList.length === 0) return;
|
|
1825
|
-
this.buildResourceFamiliesFile(packages, families, exportList);
|
|
1826
|
-
}
|
|
1827
|
-
buildResourceFamiliesFile(packages, families, exportList) {
|
|
1828
|
-
this.cat("resource_families.py", () => {
|
|
1829
|
-
this.generateDisclaimer();
|
|
1830
|
-
this.includeResourceFamilyValidator();
|
|
1831
|
-
this.line();
|
|
1832
|
-
this.generateFamilyDefinitions(packages, families);
|
|
1833
|
-
this.generateFamilyExports(exportList);
|
|
1834
|
-
});
|
|
1835
|
-
}
|
|
1836
|
-
includeResourceFamilyValidator() {
|
|
1837
|
-
const content = fs__default.readFileSync(resolvePyAssets("resource_family_validator.py"), "utf-8");
|
|
1838
|
-
this.line(content);
|
|
1839
|
-
}
|
|
1840
|
-
generateFamilyDefinitions(packages, families) {
|
|
1841
|
-
this.line(`packages = [${packages.map((p) => `'${p}'`).join(", ")}]`);
|
|
1842
|
-
this.line();
|
|
1843
|
-
for (const [familyName, resources] of Object.entries(families)) {
|
|
1844
|
-
this.generateFamilyDefinition(familyName, resources);
|
|
1845
|
-
}
|
|
1846
|
-
}
|
|
1847
|
-
generateFamilyDefinition(familyName, resources) {
|
|
1848
|
-
const listName = `${familyName}_resources`;
|
|
1849
|
-
this.line(
|
|
1850
|
-
`${listName} = [${resources.map((r) => `'${r}'`).sort().join(", ")}]`
|
|
1851
|
-
);
|
|
1852
|
-
this.line();
|
|
1853
|
-
this.line(`def validate_and_downcast_${familyName}(v: Any) -> Any:`);
|
|
1854
|
-
this.line(` return validate_and_downcast(v, packages, ${listName})`);
|
|
1855
|
-
this.line();
|
|
1856
|
-
this.line(`type ${familyName} = Annotated[Any, BeforeValidator(validate_and_downcast_${familyName})]`);
|
|
1857
|
-
this.line();
|
|
1858
|
-
}
|
|
1859
|
-
generateFamilyExports(exportList) {
|
|
1860
|
-
this.line(`__all__ = [${exportList.map((e) => `'${e}'`).join(", ")}]`);
|
|
1861
|
-
}
|
|
1862
|
-
buildPyPackageName(packageName) {
|
|
1863
|
-
const parts = packageName ? [snakeCase(packageName)] : [""];
|
|
1864
|
-
return parts.join(".");
|
|
1865
|
-
}
|
|
1866
|
-
pyFhirPackage(identifier) {
|
|
1867
|
-
return this.pyFhirPackageByName(identifier.package);
|
|
1868
|
-
}
|
|
1869
|
-
pyFhirPackageByName(name) {
|
|
1870
|
-
return [this.opts.rootPackageName, this.buildPyPackageName(name)].join(".");
|
|
1871
|
-
}
|
|
1872
|
-
pyPackage(identifier) {
|
|
1873
|
-
if (identifier.kind === "complex-type") {
|
|
1874
|
-
return `${this.pyFhirPackage(identifier)}.base`;
|
|
1875
|
-
}
|
|
1876
|
-
if (identifier.kind === "resource") {
|
|
1877
|
-
return [this.pyFhirPackage(identifier), snakeCase(identifier.name)].join(".");
|
|
1878
|
-
}
|
|
1879
|
-
return this.pyFhirPackage(identifier);
|
|
3019
|
+
this.pyImportFrom(pyPackage(this.opts.rootPackageName, identifier), pascalCase(identifier.name));
|
|
1880
3020
|
}
|
|
1881
3021
|
getFieldFormatFunction(format2) {
|
|
1882
3022
|
if (!AVAILABLE_STRING_FORMATS[format2]) {
|
|
@@ -1950,11 +3090,13 @@ function mkIdentifier(fhirSchema) {
|
|
|
1950
3090
|
if (fhirSchema.kind === "logical") return { kind: "logical", ...fields };
|
|
1951
3091
|
return { kind: "resource", ...fields };
|
|
1952
3092
|
}
|
|
3093
|
+
var VALUE_SET_NAME_SPLIT_RE = /[-_]/;
|
|
3094
|
+
var OPAQUE_VALUE_SET_ID_RE = /^[a-zA-Z0-9_-]{20,}$/;
|
|
1953
3095
|
var getValueSetName = (url) => {
|
|
1954
3096
|
const urlParts = url.split("/");
|
|
1955
3097
|
const lastSegment = urlParts[urlParts.length - 1];
|
|
1956
3098
|
if (lastSegment && lastSegment.length > 0) {
|
|
1957
|
-
return lastSegment.split(
|
|
3099
|
+
return lastSegment.split(VALUE_SET_NAME_SPLIT_RE).map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join("");
|
|
1958
3100
|
}
|
|
1959
3101
|
return url;
|
|
1960
3102
|
};
|
|
@@ -1968,7 +3110,7 @@ function mkValueSetIdentifierByUrl(register, pkg, fullValueSetUrl) {
|
|
|
1968
3110
|
},
|
|
1969
3111
|
id: fullValueSetUrl};
|
|
1970
3112
|
const valueSet = register.resolveVs(pkg, valueSetUrl) || valuesSetFallback;
|
|
1971
|
-
const valueSetName = valueSet?.id &&
|
|
3113
|
+
const valueSetName = valueSet?.id && !OPAQUE_VALUE_SET_ID_RE.test(valueSet.id) ? valueSet.id : valueSetNameFallback;
|
|
1972
3114
|
return {
|
|
1973
3115
|
kind: "value-set",
|
|
1974
3116
|
package: valueSet.package_meta.name,
|
|
@@ -2240,6 +3382,7 @@ var isValueSet = (resource) => {
|
|
|
2240
3382
|
};
|
|
2241
3383
|
|
|
2242
3384
|
// src/typeschema/register.ts
|
|
3385
|
+
var BARE_RESOURCE_NAME_RE = /^[a-zA-Z0-9]+$/;
|
|
2243
3386
|
var readPackageDependencies = async (manager, packageMeta2) => {
|
|
2244
3387
|
const packageJSON = await manager.packageJson(packageMeta2.name);
|
|
2245
3388
|
if (!packageJSON) return [];
|
|
@@ -2264,6 +3407,7 @@ var mkPackageAwareResolver = async (manager, pkg, deep, acc, logger) => {
|
|
|
2264
3407
|
logger?.info(`${" ".repeat(deep * 2)}+ ${pkgId}`);
|
|
2265
3408
|
if (acc[pkgId]) return acc[pkgId];
|
|
2266
3409
|
const index = mkEmptyPkgIndex(pkg);
|
|
3410
|
+
acc[pkgId] = index;
|
|
2267
3411
|
for (const resource of await manager.search({ package: pkg })) {
|
|
2268
3412
|
const rawUrl = resource.url;
|
|
2269
3413
|
if (!rawUrl) continue;
|
|
@@ -2284,7 +3428,6 @@ var mkPackageAwareResolver = async (manager, pkg, deep, acc, logger) => {
|
|
|
2284
3428
|
for (const resolutionOptions of Object.values(index.canonicalResolution)) {
|
|
2285
3429
|
resolutionOptions.sort((a, b) => a.deep - b.deep);
|
|
2286
3430
|
}
|
|
2287
|
-
acc[pkgId] = index;
|
|
2288
3431
|
return index;
|
|
2289
3432
|
};
|
|
2290
3433
|
var enrichResolver = (resolver, logger) => {
|
|
@@ -2299,8 +3442,8 @@ var enrichResolver = (resolver, logger) => {
|
|
|
2299
3442
|
const resource = resolition.resource;
|
|
2300
3443
|
const resourcePkg = resolition.pkg;
|
|
2301
3444
|
if (isStructureDefinition(resource)) {
|
|
2302
|
-
const
|
|
2303
|
-
const rfs = enrichFHIRSchema(
|
|
3445
|
+
const fs6 = fhirschema.translate(resource);
|
|
3446
|
+
const rfs = enrichFHIRSchema(fs6, resourcePkg);
|
|
2304
3447
|
counter++;
|
|
2305
3448
|
resolver[pkgId].fhirSchemas[rfs.url] = rfs;
|
|
2306
3449
|
}
|
|
@@ -2333,12 +3476,12 @@ var registerFromManager = async (manager, { logger, focusedPackages }) => {
|
|
|
2333
3476
|
}
|
|
2334
3477
|
}
|
|
2335
3478
|
for (const idx of Object.values(resolver)) {
|
|
2336
|
-
const
|
|
2337
|
-
if (
|
|
3479
|
+
const fs6 = idx.fhirSchemas[canonicalUrl];
|
|
3480
|
+
if (fs6 && fs6.package_meta.name === pkg.name) return fs6;
|
|
2338
3481
|
}
|
|
2339
3482
|
for (const idx of Object.values(resolver)) {
|
|
2340
|
-
const
|
|
2341
|
-
if (
|
|
3483
|
+
const fs6 = idx.fhirSchemas[canonicalUrl];
|
|
3484
|
+
if (fs6) return fs6;
|
|
2342
3485
|
}
|
|
2343
3486
|
return void 0;
|
|
2344
3487
|
};
|
|
@@ -2362,29 +3505,29 @@ var registerFromManager = async (manager, { logger, focusedPackages }) => {
|
|
|
2362
3505
|
};
|
|
2363
3506
|
const ensureSpecializationCanonicalUrl = (name) => {
|
|
2364
3507
|
if (name.includes("|")) name = name.split("|")[0];
|
|
2365
|
-
if (
|
|
3508
|
+
if (BARE_RESOURCE_NAME_RE.test(name)) {
|
|
2366
3509
|
return `http://hl7.org/fhir/StructureDefinition/${name}`;
|
|
2367
3510
|
}
|
|
2368
3511
|
return name;
|
|
2369
3512
|
};
|
|
2370
3513
|
const resolveFsGenealogy = (pkg, canonicalUrl) => {
|
|
2371
|
-
let
|
|
2372
|
-
if (
|
|
2373
|
-
const genealogy = [
|
|
2374
|
-
while (
|
|
2375
|
-
const pkg2 =
|
|
2376
|
-
const baseUrl = ensureSpecializationCanonicalUrl(
|
|
2377
|
-
|
|
2378
|
-
if (
|
|
3514
|
+
let fs6 = resolveFs(pkg, canonicalUrl);
|
|
3515
|
+
if (fs6 === void 0) throw new Error(`Failed to resolve FHIR Schema: '${canonicalUrl}'`);
|
|
3516
|
+
const genealogy = [fs6];
|
|
3517
|
+
while (fs6?.base) {
|
|
3518
|
+
const pkg2 = fs6.package_meta;
|
|
3519
|
+
const baseUrl = ensureSpecializationCanonicalUrl(fs6.base);
|
|
3520
|
+
fs6 = resolveFs(pkg2, baseUrl);
|
|
3521
|
+
if (fs6 === void 0)
|
|
2379
3522
|
throw new Error(
|
|
2380
3523
|
`Failed to resolve FHIR Schema base for '${canonicalUrl}'. Problem: '${baseUrl}' from '${packageMetaToFhir(pkg2)}'`
|
|
2381
3524
|
);
|
|
2382
|
-
genealogy.push(
|
|
3525
|
+
genealogy.push(fs6);
|
|
2383
3526
|
}
|
|
2384
3527
|
return genealogy;
|
|
2385
3528
|
};
|
|
2386
3529
|
const resolveFsSpecializations = (pkg, canonicalUrl) => {
|
|
2387
|
-
return resolveFsGenealogy(pkg, canonicalUrl).filter((
|
|
3530
|
+
return resolveFsGenealogy(pkg, canonicalUrl).filter((fs6) => fs6.derivation === "specialization");
|
|
2388
3531
|
};
|
|
2389
3532
|
const resolveElementSnapshot = (fhirSchema, path) => {
|
|
2390
3533
|
const geneology = resolveFsGenealogy(fhirSchema.package_meta, fhirSchema.url);
|
|
@@ -2479,9 +3622,9 @@ var registerFromPackageMetas = async (packageMetas, conf) => {
|
|
|
2479
3622
|
var resolveFsElementGenealogy = (genealogy, path) => {
|
|
2480
3623
|
const [top, ...rest] = path;
|
|
2481
3624
|
if (top === void 0) return [];
|
|
2482
|
-
return genealogy.map((
|
|
2483
|
-
if (!
|
|
2484
|
-
let elem =
|
|
3625
|
+
return genealogy.map((fs6) => {
|
|
3626
|
+
if (!fs6.elements) return void 0;
|
|
3627
|
+
let elem = fs6.elements?.[top];
|
|
2485
3628
|
for (const k of rest) {
|
|
2486
3629
|
elem = elem?.elements?.[k];
|
|
2487
3630
|
}
|
|
@@ -2504,7 +3647,7 @@ var hasStructuralElements = (register, fhirSchema, path) => {
|
|
|
2504
3647
|
if (elemType) {
|
|
2505
3648
|
const typeUrl = register.ensureSpecializationCanonicalUrl(elemType);
|
|
2506
3649
|
const typeGenealogy = register.resolveFsGenealogy(fhirSchema.package_meta, typeUrl);
|
|
2507
|
-
const keys = typeGenealogy.flatMap((
|
|
3650
|
+
const keys = typeGenealogy.flatMap((fs6) => Object.keys(fs6.elements ?? {}));
|
|
2508
3651
|
if (keys.length > 0) typeKeys = new Set(keys);
|
|
2509
3652
|
}
|
|
2510
3653
|
for (const elem of elemGens) {
|
|
@@ -2519,19 +3662,19 @@ var isNestedElement = (register, fhirSchema, path, snapshot, raw) => {
|
|
|
2519
3662
|
if (!raw?.elements || raw.choiceOf !== void 0) return false;
|
|
2520
3663
|
return hasStructuralElements(register, fhirSchema, path);
|
|
2521
3664
|
};
|
|
2522
|
-
var collectNestedPaths = (
|
|
2523
|
-
if (!
|
|
3665
|
+
var collectNestedPaths = (fs6) => {
|
|
3666
|
+
if (!fs6.elements) return /* @__PURE__ */ new Set();
|
|
2524
3667
|
return new Set(
|
|
2525
|
-
collectNestedElements(
|
|
3668
|
+
collectNestedElements(fs6, [], fs6.elements).filter(([_, el]) => el.elements && Object.keys(el.elements).length > 0).map(([path]) => path.join("."))
|
|
2526
3669
|
);
|
|
2527
3670
|
};
|
|
2528
3671
|
function mkNestedIdentifier(register, fhirSchema, path) {
|
|
2529
3672
|
const nestedTypeOrigins = {};
|
|
2530
3673
|
const genealogy = fhirSchema.derivation === "constraint" ? register.resolveFsSpecializations(fhirSchema.package_meta, fhirSchema.url) : register.resolveFsGenealogy(fhirSchema.package_meta, fhirSchema.url);
|
|
2531
|
-
for (const
|
|
2532
|
-
const paths = collectNestedPaths(
|
|
3674
|
+
for (const fs6 of [...genealogy].reverse()) {
|
|
3675
|
+
const paths = collectNestedPaths(fs6);
|
|
2533
3676
|
for (const p of paths) {
|
|
2534
|
-
nestedTypeOrigins[p] = `${
|
|
3677
|
+
nestedTypeOrigins[p] = `${fs6.url}#${p}`;
|
|
2535
3678
|
}
|
|
2536
3679
|
}
|
|
2537
3680
|
const nestedName = path.join(".");
|
|
@@ -2666,10 +3809,10 @@ function isRequired(register, fhirSchema, path) {
|
|
|
2666
3809
|
const fieldName = path[path.length - 1];
|
|
2667
3810
|
if (!fieldName) throw new Error(`Internal error: fieldName is missing for path ${path.join("/")}`);
|
|
2668
3811
|
const parentPath = path.slice(0, -1);
|
|
2669
|
-
const requires = register.resolveFsGenealogy(fhirSchema.package_meta, fhirSchema.url).flatMap((
|
|
2670
|
-
if (parentPath.length === 0) return
|
|
2671
|
-
if (!
|
|
2672
|
-
let elem =
|
|
3812
|
+
const requires = register.resolveFsGenealogy(fhirSchema.package_meta, fhirSchema.url).flatMap((fs6) => {
|
|
3813
|
+
if (parentPath.length === 0) return fs6.required || [];
|
|
3814
|
+
if (!fs6.elements) return [];
|
|
3815
|
+
let elem = fs6;
|
|
2673
3816
|
for (const k of parentPath) {
|
|
2674
3817
|
elem = elem?.elements?.[k];
|
|
2675
3818
|
}
|
|
@@ -2681,10 +3824,10 @@ function isExcluded(register, fhirSchema, path) {
|
|
|
2681
3824
|
const fieldName = path[path.length - 1];
|
|
2682
3825
|
if (!fieldName) throw new Error(`Internal error: fieldName is missing for path ${path.join("/")}`);
|
|
2683
3826
|
const parentPath = path.slice(0, -1);
|
|
2684
|
-
const requires = register.resolveFsGenealogy(fhirSchema.package_meta, fhirSchema.url).flatMap((
|
|
2685
|
-
if (parentPath.length === 0) return
|
|
2686
|
-
if (!
|
|
2687
|
-
let elem =
|
|
3827
|
+
const requires = register.resolveFsGenealogy(fhirSchema.package_meta, fhirSchema.url).flatMap((fs6) => {
|
|
3828
|
+
if (parentPath.length === 0) return fs6.excluded || [];
|
|
3829
|
+
if (!fs6.elements) return [];
|
|
3830
|
+
let elem = fs6;
|
|
2688
3831
|
for (const k of parentPath) {
|
|
2689
3832
|
elem = elem?.elements?.[k];
|
|
2690
3833
|
}
|
|
@@ -2696,9 +3839,9 @@ var buildReferences = (register, fhirSchema, element) => {
|
|
|
2696
3839
|
if (!element.refers) return void 0;
|
|
2697
3840
|
return element.refers.map((ref) => {
|
|
2698
3841
|
const curl = register.ensureSpecializationCanonicalUrl(ref);
|
|
2699
|
-
const
|
|
2700
|
-
if (!
|
|
2701
|
-
return mkIdentifier(
|
|
3842
|
+
const fs6 = register.resolveFs(fhirSchema.package_meta, curl);
|
|
3843
|
+
if (!fs6) throw new Error(`Failed to resolve fs for ${curl}`);
|
|
3844
|
+
return mkIdentifier(fs6);
|
|
2702
3845
|
});
|
|
2703
3846
|
};
|
|
2704
3847
|
var extractSliceFieldNames = (schema) => {
|
|
@@ -2848,7 +3991,8 @@ function buildFieldType(register, fhirSchema, path, element, logger) {
|
|
|
2848
3991
|
if (element.elementReference) {
|
|
2849
3992
|
const refPath = element.elementReference.slice(1).filter((_, i) => i % 2 === 1);
|
|
2850
3993
|
return mkNestedIdentifier(register, fhirSchema, refPath);
|
|
2851
|
-
}
|
|
3994
|
+
}
|
|
3995
|
+
if (element.type) {
|
|
2852
3996
|
const url = register.ensureSpecializationCanonicalUrl(element.type);
|
|
2853
3997
|
const fieldFs = register.resolveFs(fhirSchema.package_meta, url);
|
|
2854
3998
|
if (!fieldFs) {
|
|
@@ -2864,17 +4008,18 @@ function buildFieldType(register, fhirSchema, path, element, logger) {
|
|
|
2864
4008
|
);
|
|
2865
4009
|
}
|
|
2866
4010
|
return mkIdentifier(fieldFs);
|
|
2867
|
-
}
|
|
2868
|
-
|
|
2869
|
-
} else if (fhirSchema.derivation === "constraint") {
|
|
4011
|
+
}
|
|
4012
|
+
if (element.choices) {
|
|
2870
4013
|
return void 0;
|
|
2871
|
-
}
|
|
2872
|
-
|
|
2873
|
-
"#fieldTypeNotFound",
|
|
2874
|
-
`Can't recognize element type: <${fhirSchema.url}>.${path.join(".")} (pkg: '${packageMetaToFhir(fhirSchema.package_meta)}'): missing type info`
|
|
2875
|
-
);
|
|
4014
|
+
}
|
|
4015
|
+
if (fhirSchema.derivation === "constraint") {
|
|
2876
4016
|
return void 0;
|
|
2877
4017
|
}
|
|
4018
|
+
logger?.dryWarn(
|
|
4019
|
+
"#fieldTypeNotFound",
|
|
4020
|
+
`Can't recognize element type: <${fhirSchema.url}>.${path.join(".")} (pkg: '${packageMetaToFhir(fhirSchema.package_meta)}'): missing type info`
|
|
4021
|
+
);
|
|
4022
|
+
return void 0;
|
|
2878
4023
|
}
|
|
2879
4024
|
var mkField = (register, fhirSchema, path, element, logger, rawElement) => {
|
|
2880
4025
|
let binding;
|
|
@@ -3526,13 +4671,13 @@ var typeSchemaToJson = (ts, pretty) => {
|
|
|
3526
4671
|
genContent: () => JSON.stringify(ts, null, pretty ? 2 : void 0)
|
|
3527
4672
|
};
|
|
3528
4673
|
};
|
|
3529
|
-
var fhirSchemaToJson = (
|
|
3530
|
-
const pkgPath = normalizeFileName(
|
|
3531
|
-
const name = normalizeFileName(`${
|
|
4674
|
+
var fhirSchemaToJson = (fs6, pretty) => {
|
|
4675
|
+
const pkgPath = normalizeFileName(fs6.package_meta.name);
|
|
4676
|
+
const name = normalizeFileName(`${fs6.name}(${extractNameFromCanonical(fs6.url)})`);
|
|
3532
4677
|
const baseName = Path5.join(pkgPath, name);
|
|
3533
4678
|
return {
|
|
3534
4679
|
filename: baseName,
|
|
3535
|
-
genContent: () => JSON.stringify(
|
|
4680
|
+
genContent: () => JSON.stringify(fs6, null, pretty ? 2 : void 0)
|
|
3536
4681
|
};
|
|
3537
4682
|
};
|
|
3538
4683
|
var structureDefinitionToJson = (sd, pretty) => {
|
|
@@ -3554,7 +4699,7 @@ var IntrospectionWriter = class extends FileSystemWriter {
|
|
|
3554
4699
|
}
|
|
3555
4700
|
if (this.opts.typeSchemas) {
|
|
3556
4701
|
if (Path5.extname(this.opts.typeSchemas) === ".ndjson") {
|
|
3557
|
-
this.writeNdjson(tsIndex.schemas, this.opts.typeSchemas, typeSchemaToJson);
|
|
4702
|
+
await this.writeNdjson(tsIndex.schemas, this.opts.typeSchemas, typeSchemaToJson);
|
|
3558
4703
|
} else {
|
|
3559
4704
|
const items = tsIndex.schemas.map((ts) => typeSchemaToJson(ts, true));
|
|
3560
4705
|
const seenFilenames = /* @__PURE__ */ new Set();
|
|
@@ -3602,16 +4747,16 @@ var IntrospectionWriter = class extends FileSystemWriter {
|
|
|
3602
4747
|
const outputPath = this.opts.fhirSchemas;
|
|
3603
4748
|
const allFs = tsIndex.register.allFs();
|
|
3604
4749
|
const seenUrls = /* @__PURE__ */ new Set();
|
|
3605
|
-
const fhirSchemas = allFs.filter((
|
|
3606
|
-
if (seenUrls.has(
|
|
3607
|
-
seenUrls.add(
|
|
4750
|
+
const fhirSchemas = allFs.filter((fs6) => {
|
|
4751
|
+
if (seenUrls.has(fs6.url)) return false;
|
|
4752
|
+
seenUrls.add(fs6.url);
|
|
3608
4753
|
return true;
|
|
3609
4754
|
});
|
|
3610
4755
|
if (Path5.extname(outputPath) === ".ndjson") {
|
|
3611
|
-
this.writeNdjson(fhirSchemas, outputPath, fhirSchemaToJson);
|
|
4756
|
+
await this.writeNdjson(fhirSchemas, outputPath, fhirSchemaToJson);
|
|
3612
4757
|
} else {
|
|
3613
|
-
this.writeJsonFiles(
|
|
3614
|
-
fhirSchemas.map((
|
|
4758
|
+
await this.writeJsonFiles(
|
|
4759
|
+
fhirSchemas.map((fs6) => fhirSchemaToJson(fs6, true)),
|
|
3615
4760
|
outputPath
|
|
3616
4761
|
);
|
|
3617
4762
|
}
|
|
@@ -3627,9 +4772,9 @@ var IntrospectionWriter = class extends FileSystemWriter {
|
|
|
3627
4772
|
return true;
|
|
3628
4773
|
});
|
|
3629
4774
|
if (Path5.extname(outputPath) === ".ndjson") {
|
|
3630
|
-
this.writeNdjson(structureDefinitions, outputPath, structureDefinitionToJson);
|
|
4775
|
+
await this.writeNdjson(structureDefinitions, outputPath, structureDefinitionToJson);
|
|
3631
4776
|
} else {
|
|
3632
|
-
this.writeJsonFiles(
|
|
4777
|
+
await this.writeJsonFiles(
|
|
3633
4778
|
structureDefinitions.map((sd) => structureDefinitionToJson(sd, true)),
|
|
3634
4779
|
outputPath
|
|
3635
4780
|
);
|
|
@@ -3680,7 +4825,7 @@ var IntrospectionWriter = class extends FileSystemWriter {
|
|
|
3680
4825
|
// src/typeschema/ir/report.ts
|
|
3681
4826
|
var generateSkippedPackagesSection = (lines, skippedPackages) => {
|
|
3682
4827
|
lines.push("## Skipped Packages", "");
|
|
3683
|
-
for (const pkg of skippedPackages) {
|
|
4828
|
+
for (const pkg of [...skippedPackages].sort()) {
|
|
3684
4829
|
lines.push(`- ${pkg}`);
|
|
3685
4830
|
}
|
|
3686
4831
|
lines.push("");
|
|
@@ -3741,7 +4886,7 @@ var generateCollisionVersionLines = (versions) => {
|
|
|
3741
4886
|
const sourceList = v.entries.map((e) => {
|
|
3742
4887
|
const name = extractNameFromCanonical(e.sourceCanonical) ?? e.sourceCanonical;
|
|
3743
4888
|
return `${name} (${e.sourcePackage})`;
|
|
3744
|
-
}).join(", ");
|
|
4889
|
+
}).sort().join(", ");
|
|
3745
4890
|
const mark = v.mark ? versionMarkLabel[v.mark] : "";
|
|
3746
4891
|
return ` - Version ${version++}${mark}: ${sourceList}`;
|
|
3747
4892
|
});
|
|
@@ -3843,6 +4988,7 @@ var DebugMixinProvider = class {
|
|
|
3843
4988
|
constructor(mode) {
|
|
3844
4989
|
this.mode = mode;
|
|
3845
4990
|
}
|
|
4991
|
+
mode;
|
|
3846
4992
|
apply(target) {
|
|
3847
4993
|
return this._addDebug(target);
|
|
3848
4994
|
}
|
|
@@ -3880,6 +5026,7 @@ var LambdaMixinProvider = class {
|
|
|
3880
5026
|
upperCase: () => (text, render) => render(text).toUpperCase()
|
|
3881
5027
|
};
|
|
3882
5028
|
}
|
|
5029
|
+
nameGenerator;
|
|
3883
5030
|
lambda;
|
|
3884
5031
|
apply(target) {
|
|
3885
5032
|
return {
|
|
@@ -3897,6 +5044,10 @@ var NameGenerator = class {
|
|
|
3897
5044
|
this.nameTransformations = nameTransformations;
|
|
3898
5045
|
this.unsaveCharacterPattern = unsaveCharacterPattern;
|
|
3899
5046
|
}
|
|
5047
|
+
keywords;
|
|
5048
|
+
typeMap;
|
|
5049
|
+
nameTransformations;
|
|
5050
|
+
unsaveCharacterPattern;
|
|
3900
5051
|
_replaceUnsaveChars(name) {
|
|
3901
5052
|
const pattern = this.unsaveCharacterPattern instanceof RegExp ? this.unsaveCharacterPattern : new RegExp(this.unsaveCharacterPattern, "g");
|
|
3902
5053
|
return name.replace(pattern, "_");
|
|
@@ -4066,6 +5217,9 @@ var ViewModelFactory = class {
|
|
|
4066
5217
|
this.nameGenerator = nameGenerator;
|
|
4067
5218
|
this.filterPred = filterPred;
|
|
4068
5219
|
}
|
|
5220
|
+
tsIndex;
|
|
5221
|
+
nameGenerator;
|
|
5222
|
+
filterPred;
|
|
4069
5223
|
arrayMixinProvider = new ListElementInformationMixinProvider();
|
|
4070
5224
|
createUtility() {
|
|
4071
5225
|
return this._createForRoot();
|
|
@@ -4329,7 +5483,7 @@ function loadMustacheGeneratorConfig(templatePath, logger) {
|
|
|
4329
5483
|
if (parsed && typeof parsed === "object") {
|
|
4330
5484
|
return parsed;
|
|
4331
5485
|
}
|
|
4332
|
-
} catch
|
|
5486
|
+
} catch {
|
|
4333
5487
|
}
|
|
4334
5488
|
return {};
|
|
4335
5489
|
}
|
|
@@ -4638,8 +5792,9 @@ var extractValueField = (elements) => {
|
|
|
4638
5792
|
if (!elements) return void 0;
|
|
4639
5793
|
return elements.find((e) => e.startsWith("value") && e !== "value");
|
|
4640
5794
|
};
|
|
5795
|
+
var VALUE_PREFIX_RE = /^value/;
|
|
4641
5796
|
var valueFieldToTsType = (valueField) => {
|
|
4642
|
-
const fhirName = valueField.replace(
|
|
5797
|
+
const fhirName = valueField.replace(VALUE_PREFIX_RE, "");
|
|
4643
5798
|
const primitives = {
|
|
4644
5799
|
String: "string",
|
|
4645
5800
|
Boolean: "boolean",
|
|
@@ -4684,7 +5839,7 @@ var collectSubExtensionSlices = (extProfile) => {
|
|
|
4684
5839
|
}
|
|
4685
5840
|
return result;
|
|
4686
5841
|
};
|
|
4687
|
-
var
|
|
5842
|
+
var resolveExtensionProfile2 = (tsIndex, pkgName, url) => {
|
|
4688
5843
|
const schema = tsIndex.resolveByUrl(pkgName, url);
|
|
4689
5844
|
if (!schema || !isProfileTypeSchema(schema)) return void 0;
|
|
4690
5845
|
if (schema.identifier.package !== pkgName) return void 0;
|
|
@@ -4757,7 +5912,7 @@ var generateExtensionGetterOverloads = (w, ext, targetPath, methodName, inputTyp
|
|
|
4757
5912
|
}
|
|
4758
5913
|
);
|
|
4759
5914
|
};
|
|
4760
|
-
var
|
|
5915
|
+
var generateComplexExtensionSetter2 = (w, info) => {
|
|
4761
5916
|
const { ext, snapshot, setMethodName, targetPath, extProfileInfo } = info;
|
|
4762
5917
|
const tsProfileName = tsResourceName(snapshot.identifier);
|
|
4763
5918
|
const inputTypeName = tsExtensionFlatTypeName(tsProfileName, ext.name);
|
|
@@ -4820,7 +5975,7 @@ var generateComplexExtensionSetter = (w, info) => {
|
|
|
4820
5975
|
});
|
|
4821
5976
|
}
|
|
4822
5977
|
};
|
|
4823
|
-
var
|
|
5978
|
+
var generateComplexExtensionGetter2 = (w, info) => {
|
|
4824
5979
|
const { ext, snapshot, getMethodName, targetPath, extProfileInfo } = info;
|
|
4825
5980
|
const tsProfileName = tsResourceName(snapshot.identifier);
|
|
4826
5981
|
const inputTypeName = tsExtensionFlatTypeName(tsProfileName, ext.name);
|
|
@@ -4836,7 +5991,7 @@ var generateComplexExtensionGetter = (w, info) => {
|
|
|
4836
5991
|
w.line(`return extractComplexExtension<${inputType}>(ext, config)`);
|
|
4837
5992
|
});
|
|
4838
5993
|
};
|
|
4839
|
-
var
|
|
5994
|
+
var generateSingleValueExtensionSetter2 = (w, tsIndex, info) => {
|
|
4840
5995
|
const { ext, setMethodName, targetPath, extProfileInfo } = info;
|
|
4841
5996
|
const firstValueType = ext.valueFieldTypes?.[0];
|
|
4842
5997
|
if (!firstValueType) return;
|
|
@@ -4844,7 +5999,7 @@ var generateSingleValueExtensionSetter = (w, tsIndex, info) => {
|
|
|
4844
5999
|
const valueField = tsValueFieldName(firstValueType);
|
|
4845
6000
|
const useUpsert = ext.max === "1";
|
|
4846
6001
|
if (extProfileInfo) {
|
|
4847
|
-
const extFactoryInfo =
|
|
6002
|
+
const extFactoryInfo = collectProfileFactoryInfo2(tsIndex, extProfileInfo.snapshot);
|
|
4848
6003
|
const extValueParam = extFactoryInfo.params.find((p) => p.name === valueField);
|
|
4849
6004
|
const resolvedValueType = extValueParam?.tsType ?? valueType;
|
|
4850
6005
|
const paramType = `${extProfileInfo.className} | Extension | ${resolvedValueType}`;
|
|
@@ -4873,7 +6028,7 @@ var generateSingleValueExtensionSetter = (w, tsIndex, info) => {
|
|
|
4873
6028
|
});
|
|
4874
6029
|
}
|
|
4875
6030
|
};
|
|
4876
|
-
var
|
|
6031
|
+
var generateSingleValueExtensionGetter2 = (w, info) => {
|
|
4877
6032
|
const { ext, getMethodName, targetPath, extProfileInfo } = info;
|
|
4878
6033
|
const firstValueType = ext.valueFieldTypes?.[0];
|
|
4879
6034
|
if (!firstValueType) return;
|
|
@@ -4883,7 +6038,7 @@ var generateSingleValueExtensionGetter = (w, info) => {
|
|
|
4883
6038
|
w.line(`return getExtensionValue<${valueType}>(ext, "${valueField}")`);
|
|
4884
6039
|
});
|
|
4885
6040
|
};
|
|
4886
|
-
var
|
|
6041
|
+
var generateGenericExtensionSetter2 = (w, info) => {
|
|
4887
6042
|
const { ext, setMethodName, targetPath } = info;
|
|
4888
6043
|
const useUpsert = ext.max === "1";
|
|
4889
6044
|
w.curlyBlock(["public", setMethodName, `(value: Omit<Extension, "url"> | Extension): this`], () => {
|
|
@@ -4899,7 +6054,7 @@ var generateGenericExtensionSetter = (w, info) => {
|
|
|
4899
6054
|
w.line("return this");
|
|
4900
6055
|
});
|
|
4901
6056
|
};
|
|
4902
|
-
var
|
|
6057
|
+
var generateGenericExtensionGetter2 = (w, info) => {
|
|
4903
6058
|
const { ext, getMethodName, targetPath } = info;
|
|
4904
6059
|
w.curlyBlock(["public", getMethodName, "(): Extension | undefined"], () => {
|
|
4905
6060
|
if (targetPath.length === 0) {
|
|
@@ -4912,12 +6067,12 @@ var generateGenericExtensionGetter = (w, info) => {
|
|
|
4912
6067
|
}
|
|
4913
6068
|
});
|
|
4914
6069
|
};
|
|
4915
|
-
var
|
|
6070
|
+
var generateExtensionMethods2 = (w, tsIndex, snapshot) => {
|
|
4916
6071
|
for (const ext of snapshot.extensions ?? []) {
|
|
4917
6072
|
if (!ext.url) continue;
|
|
4918
6073
|
const baseName = ext.nameCandidates.recommended;
|
|
4919
6074
|
const targetPath = ext.path.split(".").filter((segment) => segment !== "extension");
|
|
4920
|
-
const extProfileInfo =
|
|
6075
|
+
const extProfileInfo = resolveExtensionProfile2(tsIndex, snapshot.identifier.package, ext.url);
|
|
4921
6076
|
const info = {
|
|
4922
6077
|
ext,
|
|
4923
6078
|
snapshot,
|
|
@@ -4927,17 +6082,17 @@ var generateExtensionMethods = (w, tsIndex, snapshot) => {
|
|
|
4927
6082
|
extProfileInfo
|
|
4928
6083
|
};
|
|
4929
6084
|
if (ext.isComplex && ext.subExtensions) {
|
|
4930
|
-
|
|
6085
|
+
generateComplexExtensionSetter2(w, info);
|
|
4931
6086
|
w.line();
|
|
4932
|
-
|
|
6087
|
+
generateComplexExtensionGetter2(w, info);
|
|
4933
6088
|
} else if (ext.valueFieldTypes?.length === 1 && ext.valueFieldTypes[0]) {
|
|
4934
|
-
|
|
6089
|
+
generateSingleValueExtensionSetter2(w, tsIndex, info);
|
|
4935
6090
|
w.line();
|
|
4936
|
-
|
|
6091
|
+
generateSingleValueExtensionGetter2(w, info);
|
|
4937
6092
|
} else {
|
|
4938
|
-
|
|
6093
|
+
generateGenericExtensionSetter2(w, info);
|
|
4939
6094
|
w.line();
|
|
4940
|
-
|
|
6095
|
+
generateGenericExtensionGetter2(w, info);
|
|
4941
6096
|
}
|
|
4942
6097
|
w.line();
|
|
4943
6098
|
}
|
|
@@ -5026,7 +6181,7 @@ var collectTypesFromSlices = (tsIndex, snapshot, addType) => {
|
|
|
5026
6181
|
}
|
|
5027
6182
|
}
|
|
5028
6183
|
};
|
|
5029
|
-
var
|
|
6184
|
+
var collectRequiredSliceNames2 = (field) => {
|
|
5030
6185
|
if (!field.array || !field.slicing?.slices) return void 0;
|
|
5031
6186
|
const isTypeDisc = field.slicing.discriminator?.some((d) => d.type === "type") ?? false;
|
|
5032
6187
|
if (isTypeDisc) return void 0;
|
|
@@ -5038,7 +6193,7 @@ var collectRequiredSliceNames = (field) => {
|
|
|
5038
6193
|
}).map(([name]) => name);
|
|
5039
6194
|
return names.length > 0 ? names : void 0;
|
|
5040
6195
|
};
|
|
5041
|
-
var
|
|
6196
|
+
var collectSliceDefs2 = (tsIndex, snapshot) => Object.entries(snapshot.fields).filter(([_, field]) => isNotChoiceDeclarationField(field) && field.slicing?.slices).flatMap(([fieldName, field]) => {
|
|
5042
6197
|
if (!isNotChoiceDeclarationField(field) || !field.slicing?.slices || !field.type) return [];
|
|
5043
6198
|
const baseType = tsTypeFromIdentifier(field.type);
|
|
5044
6199
|
const pkgName = snapshot.identifier.package;
|
|
@@ -5069,7 +6224,7 @@ var collectSliceDefs = (tsIndex, snapshot) => Object.entries(snapshot.fields).fi
|
|
|
5069
6224
|
};
|
|
5070
6225
|
});
|
|
5071
6226
|
});
|
|
5072
|
-
var
|
|
6227
|
+
var generateSliceSetters2 = (w, sliceDefs, snapshot) => {
|
|
5073
6228
|
const profileClassName = tsProfileClassName(snapshot);
|
|
5074
6229
|
const tsProfileName = tsResourceName(snapshot.identifier);
|
|
5075
6230
|
for (const sliceDef of sliceDefs) {
|
|
@@ -5133,7 +6288,7 @@ var generateSliceSetters = (w, sliceDefs, snapshot) => {
|
|
|
5133
6288
|
w.line();
|
|
5134
6289
|
}
|
|
5135
6290
|
};
|
|
5136
|
-
var
|
|
6291
|
+
var generateSliceGetters2 = (w, sliceDefs, snapshot) => {
|
|
5137
6292
|
const profileClassName = tsProfileClassName(snapshot);
|
|
5138
6293
|
const tsProfileName = tsResourceName(snapshot.identifier);
|
|
5139
6294
|
const defaultMode = w.opts.sliceGetterDefault ?? "flat";
|
|
@@ -5266,7 +6421,7 @@ var collectRegularFieldValidation = (errors, warnings, name, field, resolveRef,
|
|
|
5266
6421
|
}
|
|
5267
6422
|
}
|
|
5268
6423
|
};
|
|
5269
|
-
var
|
|
6424
|
+
var generateValidateMethod2 = (w, tsIndex, snapshot) => {
|
|
5270
6425
|
const fields = snapshot.fields;
|
|
5271
6426
|
const profileName = snapshot.identifier.name;
|
|
5272
6427
|
const canonicalUrl = snapshot.identifier.url;
|
|
@@ -5343,7 +6498,7 @@ var collectChoiceAccessors = (snapshot, promotedChoices) => {
|
|
|
5343
6498
|
}));
|
|
5344
6499
|
return { accessors, choiceClearMethods };
|
|
5345
6500
|
};
|
|
5346
|
-
var
|
|
6501
|
+
var tryPromoteChoice2 = (field, fields, params, promotedChoices, resolveRef, isFamilyType) => {
|
|
5347
6502
|
if (!isChoiceDeclarationField(field) || !field.required || field.choices.length !== 1) return;
|
|
5348
6503
|
const choiceName = field.choices[0];
|
|
5349
6504
|
if (!choiceName) return;
|
|
@@ -5358,7 +6513,7 @@ var mkIsFamilyType = (tsIndex) => (ref) => {
|
|
|
5358
6513
|
if (!schema || !("typeFamily" in schema)) return false;
|
|
5359
6514
|
return (schema.typeFamily?.resources?.length ?? 0) > 0;
|
|
5360
6515
|
};
|
|
5361
|
-
var
|
|
6516
|
+
var collectProfileFactoryInfo2 = (tsIndex, snapshot) => {
|
|
5362
6517
|
const autoFields = [];
|
|
5363
6518
|
const sliceAutoFields = [];
|
|
5364
6519
|
const params = [];
|
|
@@ -5375,7 +6530,7 @@ var collectProfileFactoryInfo = (tsIndex, snapshot) => {
|
|
|
5375
6530
|
if (field.excluded) continue;
|
|
5376
6531
|
if (isChoiceInstanceField(field)) continue;
|
|
5377
6532
|
if (isChoiceDeclarationField(field)) {
|
|
5378
|
-
|
|
6533
|
+
tryPromoteChoice2(field, fields, params, promotedChoices, resolveRef, isFamilyType);
|
|
5379
6534
|
continue;
|
|
5380
6535
|
}
|
|
5381
6536
|
if (field.valueConstraint) {
|
|
@@ -5389,7 +6544,7 @@ var collectProfileFactoryInfo = (tsIndex, snapshot) => {
|
|
|
5389
6544
|
continue;
|
|
5390
6545
|
}
|
|
5391
6546
|
if (isNotChoiceDeclarationField(field)) {
|
|
5392
|
-
const sliceNames =
|
|
6547
|
+
const sliceNames = collectRequiredSliceNames2(field);
|
|
5393
6548
|
if (sliceNames) {
|
|
5394
6549
|
if (field.type) {
|
|
5395
6550
|
const tsType = fieldTsType(field, resolveRef, isFamilyType);
|
|
@@ -5409,7 +6564,7 @@ var collectProfileFactoryInfo = (tsIndex, snapshot) => {
|
|
|
5409
6564
|
params.push({ name, tsType, typeId: field.type });
|
|
5410
6565
|
}
|
|
5411
6566
|
}
|
|
5412
|
-
|
|
6567
|
+
collectBaseRequiredParams2(
|
|
5413
6568
|
tsIndex,
|
|
5414
6569
|
snapshot,
|
|
5415
6570
|
resolveRef,
|
|
@@ -5426,7 +6581,7 @@ var collectProfileFactoryInfo = (tsIndex, snapshot) => {
|
|
|
5426
6581
|
const accessors = [...autoAccessors, ...choiceAccessors];
|
|
5427
6582
|
return { autoFields, sliceAutoFields, params, accessors, choiceClearMethods, fixedFields };
|
|
5428
6583
|
};
|
|
5429
|
-
var
|
|
6584
|
+
var collectBaseRequiredParams2 = (tsIndex, snapshot, resolveRef, params, coveredNames, isFamilyType) => {
|
|
5430
6585
|
const covered = new Set(coveredNames);
|
|
5431
6586
|
const baseSchema = tsIndex.resolveType(snapshot.base);
|
|
5432
6587
|
if (!baseSchema || !("fields" in baseSchema) || !baseSchema.fields) return;
|
|
@@ -5516,7 +6671,7 @@ var generateProfileImports = (w, tsIndex, snapshot) => {
|
|
|
5516
6671
|
collectTypesFromSlices(tsIndex, snapshot, addType);
|
|
5517
6672
|
const needsExtensionType = collectTypesFromExtensions(tsIndex, snapshot, addType);
|
|
5518
6673
|
collectTypesFromFlatInput(tsIndex, snapshot, addType);
|
|
5519
|
-
const factoryInfo =
|
|
6674
|
+
const factoryInfo = collectProfileFactoryInfo2(tsIndex, snapshot);
|
|
5520
6675
|
for (const param of factoryInfo.params) addType(param.typeId);
|
|
5521
6676
|
for (const f of factoryInfo.sliceAutoFields) addType(f.typeId);
|
|
5522
6677
|
for (const accessor of factoryInfo.accessors) addType(accessor.typeId);
|
|
@@ -5542,7 +6697,7 @@ var generateProfileImports = (w, tsIndex, snapshot) => {
|
|
|
5542
6697
|
const extProfileImports = /* @__PURE__ */ new Map();
|
|
5543
6698
|
for (const ext of snapshot.extensions ?? []) {
|
|
5544
6699
|
if (!ext.url) continue;
|
|
5545
|
-
const info =
|
|
6700
|
+
const info = resolveExtensionProfile2(tsIndex, snapshot.identifier.package, ext.url);
|
|
5546
6701
|
if (!info) continue;
|
|
5547
6702
|
if (!extProfileImports.has(info.className)) {
|
|
5548
6703
|
const hasFlatInput = collectSubExtensionSlices(info.snapshot).length > 0;
|
|
@@ -5557,7 +6712,7 @@ var generateProfileImports = (w, tsIndex, snapshot) => {
|
|
|
5557
6712
|
}
|
|
5558
6713
|
if (extProfileImports.size > 0) w.line();
|
|
5559
6714
|
};
|
|
5560
|
-
var
|
|
6715
|
+
var generateStaticSliceFields2 = (w, sliceDefs) => {
|
|
5561
6716
|
for (const sliceDef of sliceDefs) {
|
|
5562
6717
|
const staticName = `${tsSliceStaticName(sliceDef.sliceName)}SliceMatch`;
|
|
5563
6718
|
const json = JSON.stringify(sliceDef.match);
|
|
@@ -5779,7 +6934,7 @@ var generateFactoryMethods = (w, tsIndex, snapshot, factoryInfo) => {
|
|
|
5779
6934
|
});
|
|
5780
6935
|
w.line();
|
|
5781
6936
|
};
|
|
5782
|
-
var
|
|
6937
|
+
var generateFieldAccessors2 = (w, factoryInfo) => {
|
|
5783
6938
|
w.line("// Field accessors");
|
|
5784
6939
|
for (const p of factoryInfo.params) {
|
|
5785
6940
|
const methodBaseName = uppercaseFirstLetter(p.name);
|
|
@@ -5821,7 +6976,7 @@ var generateInlineExtensionInputTypes = (w, tsIndex, snapshot) => {
|
|
|
5821
6976
|
const complexExtensions = (snapshot.extensions ?? []).filter((ext) => ext.isComplex && ext.subExtensions);
|
|
5822
6977
|
for (const ext of complexExtensions) {
|
|
5823
6978
|
if (!ext.url) continue;
|
|
5824
|
-
const extProfileInfo =
|
|
6979
|
+
const extProfileInfo = resolveExtensionProfile2(tsIndex, snapshot.identifier.package, ext.url);
|
|
5825
6980
|
const hasFlatInput = extProfileInfo ? collectSubExtensionSlices(extProfileInfo.snapshot).length > 0 : false;
|
|
5826
6981
|
if (hasFlatInput) continue;
|
|
5827
6982
|
const typeName = tsExtensionFlatTypeName(tsProfileName, ext.name);
|
|
@@ -5927,8 +7082,8 @@ var generateFlatInputType = (w, snapshot) => {
|
|
|
5927
7082
|
var generateProfileClass = (w, tsIndex, snapshot) => {
|
|
5928
7083
|
const tsBaseResourceName = tsTypeFromIdentifier(snapshot.base);
|
|
5929
7084
|
const profileClassName = tsProfileClassName(snapshot);
|
|
5930
|
-
const sliceDefs =
|
|
5931
|
-
const factoryInfo =
|
|
7085
|
+
const sliceDefs = collectSliceDefs2(tsIndex, snapshot);
|
|
7086
|
+
const factoryInfo = collectProfileFactoryInfo2(tsIndex, snapshot);
|
|
5932
7087
|
generateInlineExtensionInputTypes(w, tsIndex, snapshot);
|
|
5933
7088
|
generateSliceInputTypes(w, snapshot, sliceDefs);
|
|
5934
7089
|
generateProfileHelpersImport(w, tsIndex, snapshot, sliceDefs, factoryInfo);
|
|
@@ -5939,18 +7094,18 @@ var generateProfileClass = (w, tsIndex, snapshot) => {
|
|
|
5939
7094
|
w.curlyBlock(["export", "class", profileClassName], () => {
|
|
5940
7095
|
w.lineSM(`static readonly canonicalUrl = ${JSON.stringify(canonicalUrl)}`);
|
|
5941
7096
|
w.line();
|
|
5942
|
-
|
|
7097
|
+
generateStaticSliceFields2(w, sliceDefs);
|
|
5943
7098
|
w.lineSM(`private resource: ${tsBaseResourceName}`);
|
|
5944
7099
|
w.line();
|
|
5945
7100
|
generateFactoryMethods(w, tsIndex, snapshot, factoryInfo);
|
|
5946
|
-
|
|
7101
|
+
generateFieldAccessors2(w, factoryInfo);
|
|
5947
7102
|
w.line("// Extensions");
|
|
5948
|
-
|
|
7103
|
+
generateExtensionMethods2(w, tsIndex, snapshot);
|
|
5949
7104
|
w.line("// Slices");
|
|
5950
|
-
|
|
5951
|
-
|
|
7105
|
+
generateSliceSetters2(w, sliceDefs, snapshot);
|
|
7106
|
+
generateSliceGetters2(w, sliceDefs, snapshot);
|
|
5952
7107
|
w.line("// Validation");
|
|
5953
|
-
|
|
7108
|
+
generateValidateMethod2(w, tsIndex, snapshot);
|
|
5954
7109
|
});
|
|
5955
7110
|
w.line();
|
|
5956
7111
|
};
|
|
@@ -5964,7 +7119,7 @@ var resolveTsAssets = (fn) => {
|
|
|
5964
7119
|
}
|
|
5965
7120
|
return Path5.resolve(__dirname, "../../../..", "assets", "api", "writer-generator", "typescript", fn);
|
|
5966
7121
|
};
|
|
5967
|
-
var
|
|
7122
|
+
var leafOf3 = (path) => path[path.length - 1] ?? "";
|
|
5968
7123
|
var TS_HARDCODED_GENERIC_NAMES = /* @__PURE__ */ new Set(["Reference", "Coding", "CodeableConcept"]);
|
|
5969
7124
|
var TypeScript = class extends Writer {
|
|
5970
7125
|
constructor(options) {
|
|
@@ -6107,11 +7262,11 @@ var TypeScript = class extends Writer {
|
|
|
6107
7262
|
const targetParams = isNestedTypeSchema(target) || isSpecializationTypeSchema(target) ? target.generic?.params : void 0;
|
|
6108
7263
|
if (targetParams?.length) {
|
|
6109
7264
|
const args = targetParams.map(
|
|
6110
|
-
(tp) => params.find((q) =>
|
|
7265
|
+
(tp) => params.find((q) => leafOf3(q.path) === leafOf3(tp.path))?.typeVar ?? tp.typeVar
|
|
6111
7266
|
);
|
|
6112
7267
|
nestedArgsByField[tsName] = `<${args.join(", ")}>`;
|
|
6113
7268
|
} else if (isSpecializationTypeSchema(target) && (target.typeFamily?.resources?.length ?? 0) > 0) {
|
|
6114
|
-
const p = params.find((q) =>
|
|
7269
|
+
const p = params.find((q) => leafOf3(q.path) === fieldName);
|
|
6115
7270
|
if (p) fieldMap[tsName] = p.typeVar;
|
|
6116
7271
|
}
|
|
6117
7272
|
}
|
|
@@ -6432,7 +7587,7 @@ var APIBuilder = class {
|
|
|
6432
7587
|
const defaultPyOpts = {
|
|
6433
7588
|
...defaultWriterOpts,
|
|
6434
7589
|
rootPackageName: "fhir_types",
|
|
6435
|
-
fieldFormat: "
|
|
7590
|
+
fieldFormat: "camelCase",
|
|
6436
7591
|
primitiveTypeExtension: false
|
|
6437
7592
|
};
|
|
6438
7593
|
const opts = {
|
|
@@ -6550,7 +7705,7 @@ var APIBuilder = class {
|
|
|
6550
7705
|
};
|
|
6551
7706
|
this.logger.debug(`Starting generation with ${this.generators.length} generators`);
|
|
6552
7707
|
try {
|
|
6553
|
-
if (this.options.cleanOutput) cleanup(this.options, this.logger);
|
|
7708
|
+
if (this.options.cleanOutput) await cleanup(this.options, this.logger);
|
|
6554
7709
|
let register;
|
|
6555
7710
|
if (this.prebuiltRegister) {
|
|
6556
7711
|
this.logger.info("Using prebuilt register");
|