@osdk/seed-compiler 0.11.0 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +11 -0
- package/README.md +52 -37
- package/build/browser/cli/main.js +3 -5
- package/build/browser/cli/main.js.map +1 -1
- package/build/browser/compileSeedData.js +42 -259
- package/build/browser/compileSeedData.js.map +1 -1
- package/build/browser/index.js +1 -2
- package/build/browser/index.js.map +1 -1
- package/build/cjs/index.cjs +33 -185
- package/build/cjs/index.cjs.map +1 -1
- package/build/cjs/index.d.cts +8 -82
- package/build/esm/cli/main.js +3 -5
- package/build/esm/cli/main.js.map +1 -1
- package/build/esm/compileSeedData.js +42 -259
- package/build/esm/compileSeedData.js.map +1 -1
- package/build/esm/index.js +1 -2
- package/build/esm/index.js.map +1 -1
- package/build/types/cli/main.d.ts.map +1 -1
- package/build/types/compileSeedData.d.ts +8 -58
- package/build/types/compileSeedData.d.ts.map +1 -1
- package/build/types/index.d.ts +1 -3
- package/build/types/index.d.ts.map +1 -1
- package/package.json +8 -2
- package/build/browser/schema.js +0 -51
- package/build/browser/schema.js.map +0 -1
- package/build/esm/schema.js +0 -51
- package/build/esm/schema.js.map +0 -1
- package/build/types/schema.d.ts +0 -24
- package/build/types/schema.d.ts.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
# @osdk/seed-compiler
|
|
2
2
|
|
|
3
|
+
## 0.12.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- fb5b752: Reduce `@osdk/seed-compiler` to merging. Seed files are fed through a single `SeedBuilder` from `@osdk/seed-helpers`, which already validates objects, rejects duplicate primary keys, and deduplicates links — so the compiler's own wire-type tables and validator are gone, along with the `mergeSeedOutputs`, `validateSeedOutput`, and `schemaFromMetadata` exports. `compileSeedData` is the only remaining export and now takes `OntologyFullMetadata` in place of a `SchemaMap`. Sharing one builder lets a link reference objects from another seed file, and primary-key conflicts name the file that introduced them. Seed files may default-export either the `createSeed(...)` result (`{ output, context }`) or its `.output`; anything else is rejected with a message naming what was found instead. `SeedBuilder` now names the object type in its not-in-metadata error.
|
|
8
|
+
|
|
9
|
+
### Patch Changes
|
|
10
|
+
|
|
11
|
+
- Updated dependencies [fb5b752]
|
|
12
|
+
- @osdk/seed-helpers@0.27.0
|
|
13
|
+
|
|
3
14
|
## 0.11.0
|
|
4
15
|
|
|
5
16
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# @osdk/seed-compiler
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
Merges seed data files (`.mts`) into a single JSON output for the local ontology
|
|
4
|
+
server to load into SQLite on startup.
|
|
5
5
|
|
|
6
6
|
## Usage
|
|
7
7
|
|
|
@@ -14,67 +14,82 @@ seed-compiler \
|
|
|
14
14
|
--output path/to/seed-data.json
|
|
15
15
|
```
|
|
16
16
|
|
|
17
|
-
| Flag | Description
|
|
18
|
-
| ------------ |
|
|
19
|
-
| `--metadata` | Path to the `ontology-metadata.json` file written by the SDK generator.
|
|
20
|
-
| `--seed-dir` | Directory containing seed `.mts` files. All top-level `.mts` files are loaded, sorted by filename for deterministic output, and merged.
|
|
21
|
-
| `--output` | Path where the merged seed JSON is written.
|
|
17
|
+
| Flag | Description |
|
|
18
|
+
| ------------ | --------------------------------------------------------------------------------------------------------------------------------------- |
|
|
19
|
+
| `--metadata` | Path to the `ontology-metadata.json` file written by the SDK generator. Backs the `SeedBuilder` the files are merged through. |
|
|
20
|
+
| `--seed-dir` | Directory containing seed `.mts` files. All top-level `.mts` files are loaded, sorted by filename for deterministic output, and merged. |
|
|
21
|
+
| `--output` | Path where the merged seed JSON is written. |
|
|
22
22
|
|
|
23
23
|
## Authoring seed files
|
|
24
24
|
|
|
25
|
-
Seed files use
|
|
25
|
+
Seed files use a metadata-bound `createSeed` — `createSeedWithMetadata()` from
|
|
26
|
+
`@osdk/seed-helpers` partially applied to your ontology's metadata — together with
|
|
27
|
+
the user's generated `@ontology/sdk`. Each file must **default-export either the
|
|
28
|
+
`createSeed(...)` result** (the `{ output, context }` object) **or its `.output`**
|
|
29
|
+
(a bare `SeedOutput`); the compiler tells the two apart structurally and treats
|
|
30
|
+
them identically. Exporting the result itself is the recommended form, since
|
|
31
|
+
`context` stays available to anything else importing the file:
|
|
26
32
|
|
|
27
33
|
```ts
|
|
28
34
|
import { Product, Seller } from "@ontology/sdk";
|
|
29
|
-
|
|
35
|
+
|
|
36
|
+
import { createSeed } from "./createSeed.js";
|
|
30
37
|
|
|
31
38
|
export default createSeed((seed) => {
|
|
32
|
-
const widget = seed.
|
|
33
|
-
|
|
39
|
+
const widget = seed.create(Product, {
|
|
40
|
+
productId: "prod-001",
|
|
34
41
|
title: "Widget",
|
|
35
42
|
price: 100,
|
|
36
43
|
});
|
|
37
|
-
const alice = seed.
|
|
38
|
-
|
|
44
|
+
const alice = seed.create(Seller, {
|
|
45
|
+
sellerId: "seller-001",
|
|
39
46
|
name: "Alice",
|
|
40
47
|
});
|
|
41
48
|
|
|
42
49
|
// Link by reference — full compile-time validation on link names and target types.
|
|
43
|
-
seed.link(
|
|
50
|
+
seed.link(widget, "sellers", alice);
|
|
44
51
|
});
|
|
45
52
|
```
|
|
46
53
|
|
|
47
|
-
|
|
48
|
-
keeping refs in scope is awkward:
|
|
54
|
+
`seed.link()` also accepts an array of targets:
|
|
49
55
|
|
|
50
56
|
```ts
|
|
51
|
-
seed.
|
|
52
|
-
seed.add(Seller, { pk: "seller-001", name: "Alice" });
|
|
53
|
-
|
|
54
|
-
seed.link(
|
|
55
|
-
"widget-seller",
|
|
56
|
-
Product,
|
|
57
|
-
"prod-001",
|
|
58
|
-
"sellers",
|
|
59
|
-
Seller,
|
|
60
|
-
"seller-001",
|
|
61
|
-
"products",
|
|
62
|
-
);
|
|
57
|
+
seed.link(widget, "sellers", [alice, bob]);
|
|
63
58
|
```
|
|
64
59
|
|
|
65
|
-
|
|
60
|
+
where `createSeed` is defined once per project as:
|
|
61
|
+
|
|
62
|
+
```ts
|
|
63
|
+
import metadata from "@ontology/sdk/UNSTABLE_DO_NOT_USE/ontology-metadata";
|
|
64
|
+
import {
|
|
65
|
+
createSeedWithMetadata,
|
|
66
|
+
type SeedFunction,
|
|
67
|
+
type SeedOutput,
|
|
68
|
+
} from "@osdk/seed-helpers";
|
|
69
|
+
|
|
70
|
+
export const createSeed = <T>(
|
|
71
|
+
fn: SeedFunction<T>,
|
|
72
|
+
): { output: SeedOutput; context: T } => createSeedWithMetadata(metadata, fn);
|
|
73
|
+
```
|
|
66
74
|
|
|
67
75
|
## Validation
|
|
68
76
|
|
|
69
|
-
|
|
77
|
+
Validation is not this package's job. `@osdk/seed-helpers` validates every object as
|
|
78
|
+
`seed.create()` / `seed.update()` inserts it, and again when the compiler feeds each
|
|
79
|
+
file's output into its shared `SeedBuilder`:
|
|
70
80
|
|
|
71
|
-
- **Object types**
|
|
72
|
-
- **
|
|
81
|
+
- **Object types** must be defined in the ontology (via the metadata file).
|
|
82
|
+
- **Property names** must exist on the object type, and values must be non-null and
|
|
83
|
+
match the wire type's expected JS type.
|
|
73
84
|
- **String-encoded property values** must match the regex format for their wire type
|
|
74
85
|
(`timestamp`, `date`, `datetime`, `long`, `decimal`).
|
|
75
|
-
- **
|
|
76
|
-
|
|
77
|
-
|
|
86
|
+
- **Primary keys** must be unique within an object type.
|
|
87
|
+
|
|
88
|
+
Because all files are merged through one `SeedBuilder`, primary-key uniqueness holds
|
|
89
|
+
across the whole directory, not just within a file. The compiler's only addition is
|
|
90
|
+
the seed file's name in the error message, so a conflict points at the file that
|
|
91
|
+
introduced it.
|
|
78
92
|
|
|
79
|
-
|
|
80
|
-
|
|
93
|
+
Links are deduplicated by their identity (source, link type, target). Link entries
|
|
94
|
+
may reference objects created in any seed file in the directory, and a link's `name`
|
|
95
|
+
is derived from that identity rather than taken from the input.
|
|
@@ -21,9 +21,8 @@ import invariant from "tiny-invariant";
|
|
|
21
21
|
import yargs from "yargs";
|
|
22
22
|
import { hideBin } from "yargs/helpers";
|
|
23
23
|
import { compileSeedData } from "../compileSeedData.js";
|
|
24
|
-
import { schemaFromMetadata } from "../schema.js";
|
|
25
24
|
export default async function main(args = process.argv) {
|
|
26
|
-
const opts = await yargs(hideBin(args)).version("0.
|
|
25
|
+
const opts = await yargs(hideBin(args)).version("0.12.0" ?? "").wrap(Math.min(120, yargs().terminalWidth())).strict().help().options({
|
|
27
26
|
metadata: {
|
|
28
27
|
describe: "Path to the ontology-metadata.json file written by the SDK generator. " + "Provides primary-key field names and property wire types.",
|
|
29
28
|
type: "string",
|
|
@@ -49,12 +48,11 @@ export default async function main(args = process.argv) {
|
|
|
49
48
|
const metadata = JSON.parse(fs.readFileSync(opts.metadata, "utf-8"));
|
|
50
49
|
const seedDirStat = fs.statSync(opts.seedDir);
|
|
51
50
|
!seedDirStat.isDirectory() ? process.env.NODE_ENV !== "production" ? invariant(false, `--seed-dir '${opts.seedDir}' is not a directory`) : invariant(false) : void 0;
|
|
52
|
-
const seedFiles = fs.readdirSync(opts.seedDir).filter(f => f.endsWith(".mts")).sort().map(f => path.join(opts.seedDir, f));
|
|
51
|
+
const seedFiles = fs.readdirSync(opts.seedDir).filter(f => f.endsWith(".mts") && !f.startsWith("$")).sort().map(f => path.join(opts.seedDir, f));
|
|
53
52
|
if (seedFiles.length === 0) {
|
|
54
53
|
consola.warn(`No .mts seed files found in ${opts.seedDir}`);
|
|
55
54
|
return;
|
|
56
55
|
}
|
|
57
|
-
|
|
58
|
-
await compileSeedData(seedFiles, opts.output, schema);
|
|
56
|
+
await compileSeedData(seedFiles, opts.output, metadata);
|
|
59
57
|
}
|
|
60
58
|
//# sourceMappingURL=main.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"main.js","names":["fs","path","consola","invariant","yargs","hideBin","compileSeedData","
|
|
1
|
+
{"version":3,"file":"main.js","names":["fs","path","consola","invariant","yargs","hideBin","compileSeedData","main","args","process","argv","opts","version","wrap","Math","min","terminalWidth","strict","help","options","metadata","describe","type","demandOption","coerce","resolve","seedDir","output","alias","parseAsync","metadataStat","statSync","isFile","env","NODE_ENV","JSON","parse","readFileSync","seedDirStat","isDirectory","seedFiles","readdirSync","filter","f","endsWith","startsWith","sort","map","join","length","warn"],"sources":["main.ts"],"sourcesContent":["/*\n * Copyright 2025 Palantir Technologies, Inc. All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\n\nimport type { OntologyFullMetadata } from \"@osdk/foundry.ontologies\";\nimport { consola } from \"consola\";\nimport invariant from \"tiny-invariant\";\nimport yargs from \"yargs\";\nimport { hideBin } from \"yargs/helpers\";\n\nimport { compileSeedData } from \"../compileSeedData.js\";\n\nexport default async function main(\n args: string[] = process.argv,\n): Promise<void> {\n const opts: {\n metadata: string;\n seedDir: string;\n output: string;\n } = await yargs(hideBin(args))\n .version(process.env.PACKAGE_VERSION ?? \"\")\n .wrap(Math.min(120, yargs().terminalWidth()))\n .strict()\n .help()\n .options({\n metadata: {\n describe:\n \"Path to the ontology-metadata.json file written by the SDK generator. \" +\n \"Provides primary-key field names and property wire types.\",\n type: \"string\" as const,\n demandOption: true,\n coerce: path.resolve,\n },\n seedDir: {\n describe:\n \"Directory containing seed data .mts files. All top-level .mts \" +\n \"files are compiled (sorted by filename for deterministic output).\",\n type: \"string\" as const,\n demandOption: true,\n coerce: path.resolve,\n },\n output: {\n alias: \"o\",\n describe: \"Output path for the compiled seed data JSON.\",\n type: \"string\" as const,\n demandOption: true,\n coerce: path.resolve,\n },\n })\n .parseAsync();\n\n const metadataStat = fs.statSync(opts.metadata);\n invariant(\n metadataStat.isFile(),\n `--metadata '${opts.metadata}' is not a file`,\n );\n const metadata = JSON.parse(\n fs.readFileSync(opts.metadata, \"utf-8\"),\n ) as OntologyFullMetadata;\n\n const seedDirStat = fs.statSync(opts.seedDir);\n invariant(\n seedDirStat.isDirectory(),\n `--seed-dir '${opts.seedDir}' is not a directory`,\n );\n const seedFiles = fs\n .readdirSync(opts.seedDir)\n .filter((f) => f.endsWith(\".mts\") && !f.startsWith(\"$\"))\n .sort()\n .map((f) => path.join(opts.seedDir, f));\n\n if (seedFiles.length === 0) {\n consola.warn(`No .mts seed files found in ${opts.seedDir}`);\n return;\n }\n\n await compileSeedData(seedFiles, opts.output, metadata);\n}\n"],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA,OAAO,KAAKA,EAAE,MAAM,SAAS;AAC7B,OAAO,KAAKC,IAAI,MAAM,WAAW;AAGjC,SAASC,OAAO,QAAQ,SAAS;AACjC,OAAOC,SAAS,MAAM,gBAAgB;AACtC,OAAOC,KAAK,MAAM,OAAO;AACzB,SAASC,OAAO,QAAQ,eAAe;AAEvC,SAASC,eAAe,QAAQ,uBAAuB;AAEvD,eAAe,eAAeC,IAAIA,CAChCC,IAAc,GAAGC,OAAO,CAACC,IAAI,EACd;EACf,MAAMC,IAIL,GAAG,MAAMP,KAAK,CAACC,OAAO,CAACG,IAAI,CAAC,CAAC,CAC3BI,OAAO,CAAC,YAA+B,EAAE,CAAC,CAC1CC,IAAI,CAACC,IAAI,CAACC,GAAG,CAAC,GAAG,EAAEX,KAAK,CAAC,CAAC,CAACY,aAAa,CAAC,CAAC,CAAC,CAAC,CAC5CC,MAAM,CAAC,CAAC,CACRC,IAAI,CAAC,CAAC,CACNC,OAAO,CAAC;IACPC,QAAQ,EAAE;MACRC,QAAQ,EACN,wEAAwE,GACxE,2DAA2D;MAC7DC,IAAI,EAAE,QAAiB;MACvBC,YAAY,EAAE,IAAI;MAClBC,MAAM,EAAEvB,IAAI,CAACwB;IACf,CAAC;IACDC,OAAO,EAAE;MACPL,QAAQ,EACN,gEAAgE,GAChE,mEAAmE;MACrEC,IAAI,EAAE,QAAiB;MACvBC,YAAY,EAAE,IAAI;MAClBC,MAAM,EAAEvB,IAAI,CAACwB;IACf,CAAC;IACDE,MAAM,EAAE;MACNC,KAAK,EAAE,GAAG;MACVP,QAAQ,EAAE,8CAA8C;MACxDC,IAAI,EAAE,QAAiB;MACvBC,YAAY,EAAE,IAAI;MAClBC,MAAM,EAAEvB,IAAI,CAACwB;IACf;EACF,CAAC,CAAC,CACDI,UAAU,CAAC,CAAC;EAEf,MAAMC,YAAY,GAAG9B,EAAE,CAAC+B,QAAQ,CAACpB,IAAI,CAACS,QAAQ,CAAC;EAC/C,CACEU,YAAY,CAACE,MAAM,CAAC,CAAC,GAAAvB,OAAA,CAAAwB,GAAA,CAAAC,QAAA,oBADvB/B,SAAS,QAEP,eAAeQ,IAAI,CAACS,QAAQ,iBAAiB,IAF/CjB,SAAS;EAIT,MAAMiB,QAAQ,GAAGe,IAAI,CAACC,KAAK,CACzBpC,EAAE,CAACqC,YAAY,CAAC1B,IAAI,CAACS,QAAQ,EAAE,OAAO,CACxC,CAAyB;EAEzB,MAAMkB,WAAW,GAAGtC,EAAE,CAAC+B,QAAQ,CAACpB,IAAI,CAACe,OAAO,CAAC;EAC7C,CACEY,WAAW,CAACC,WAAW,CAAC,CAAC,GAAA9B,OAAA,CAAAwB,GAAA,CAAAC,QAAA,oBAD3B/B,SAAS,QAEP,eAAeQ,IAAI,CAACe,OAAO,sBAAsB,IAFnDvB,SAAS;EAIT,MAAMqC,SAAS,GAAGxC,EAAE,CACjByC,WAAW,CAAC9B,IAAI,CAACe,OAAO,CAAC,CACzBgB,MAAM,CAAEC,CAAC,IAAKA,CAAC,CAACC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAACD,CAAC,CAACE,UAAU,CAAC,GAAG,CAAC,CAAC,CACvDC,IAAI,CAAC,CAAC,CACNC,GAAG,CAAEJ,CAAC,IAAK1C,IAAI,CAAC+C,IAAI,CAACrC,IAAI,CAACe,OAAO,EAAEiB,CAAC,CAAC,CAAC;EAEzC,IAAIH,SAAS,CAACS,MAAM,KAAK,CAAC,EAAE;IAC1B/C,OAAO,CAACgD,IAAI,CAAC,+BAA+BvC,IAAI,CAACe,OAAO,EAAE,CAAC;IAC3D;EACF;EAEA,MAAMpB,eAAe,CAACkC,SAAS,EAAE7B,IAAI,CAACgB,MAAM,EAAEP,QAAQ,CAAC;AACzD","ignoreList":[]}
|
|
@@ -16,109 +16,35 @@
|
|
|
16
16
|
|
|
17
17
|
import * as fs from "node:fs";
|
|
18
18
|
import * as path from "node:path";
|
|
19
|
+
import { SeedBuilder } from "@osdk/seed-helpers";
|
|
19
20
|
import { consola } from "consola";
|
|
20
21
|
import { createJiti } from "jiti";
|
|
21
22
|
|
|
22
23
|
/**
|
|
23
|
-
*
|
|
24
|
-
* match the wire format regex). Format failures are collected across the
|
|
25
|
-
* whole output and reported together at the end so users can fix many
|
|
26
|
-
* content mistakes in one pass. Structural failures (unknown object type,
|
|
27
|
-
* unknown property name, null value, wrong JS type) throw immediately
|
|
28
|
-
* instead — they're not aggregated, so they don't need this struct.
|
|
29
|
-
*/
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Expected runtime JS type for each cataloged wire type.
|
|
33
|
-
*
|
|
34
|
-
* Used to fail fast on `as any` callers that pass a value of the wrong shape
|
|
35
|
-
* (e.g., `age: "30"` when `age` is an integer). Wire types not listed here
|
|
36
|
-
* are not strictly typed by this validator — currently `attachment`,
|
|
37
|
-
* `mediaReference`, `geopoint`, `geoshape`, `vector`, `array`, `struct`,
|
|
38
|
-
* which have non-primitive runtime shapes that need bespoke validation.
|
|
39
|
-
*/
|
|
40
|
-
const EXPECTED_JS_TYPE = {
|
|
41
|
-
// string-encoded primitives
|
|
42
|
-
string: "string",
|
|
43
|
-
marking: "string",
|
|
44
|
-
timestamp: "string",
|
|
45
|
-
date: "string",
|
|
46
|
-
datetime: "string",
|
|
47
|
-
long: "string",
|
|
48
|
-
decimal: "string",
|
|
49
|
-
ipAddress: "string",
|
|
50
|
-
cipherText: "string",
|
|
51
|
-
// numeric primitives
|
|
52
|
-
integer: "number",
|
|
53
|
-
byte: "number",
|
|
54
|
-
short: "number",
|
|
55
|
-
double: "number",
|
|
56
|
-
float: "number",
|
|
57
|
-
// boolean
|
|
58
|
-
boolean: "boolean"
|
|
59
|
-
};
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* Regex patterns for string-encoded wire types that TypeScript cannot validate.
|
|
24
|
+
* Merges one or more seed data files into a single JSON output.
|
|
63
25
|
*
|
|
64
|
-
*
|
|
65
|
-
* - timestamp: RFC 3339 parse on the Rust side — requires timezone, rejects trailing garbage
|
|
66
|
-
* - date: stored as raw string, but only YYYY-MM-DD works in SQLite queries
|
|
67
|
-
* - datetime: stored as raw string, same YYYY-MM-DD requirement for query correctness
|
|
68
|
-
* - long: strict decimal integer parse on the Rust side — no scientific notation
|
|
69
|
-
* - decimal: Rust stores any string (no validation), but we enforce numeric format
|
|
70
|
-
* to prevent obviously invalid values from silently passing through
|
|
71
|
-
*/
|
|
72
|
-
const WIRE_TYPE_FORMAT = {
|
|
73
|
-
timestamp: {
|
|
74
|
-
pattern: /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?(Z|[+-]\d{2}:\d{2})$/u,
|
|
75
|
-
example: "2025-01-01T00:00:00Z"
|
|
76
|
-
},
|
|
77
|
-
date: {
|
|
78
|
-
pattern: /^\d{4}-\d{2}-\d{2}$/u,
|
|
79
|
-
example: "2025-01-01"
|
|
80
|
-
},
|
|
81
|
-
datetime: {
|
|
82
|
-
pattern: /^\d{4}-\d{2}-\d{2}(T\d{2}:\d{2}:\d{2}(\.\d+)?(Z|[+-]\d{2}:\d{2})?)?$/u,
|
|
83
|
-
example: "2025-01-01T12:00:00Z"
|
|
84
|
-
},
|
|
85
|
-
long: {
|
|
86
|
-
// Strict decimal integer — matches Rust's str::parse::<i64>().
|
|
87
|
-
// No scientific notation, no decimal point, no whitespace.
|
|
88
|
-
pattern: /^-?\d+$/u,
|
|
89
|
-
example: "9007199254740993"
|
|
90
|
-
},
|
|
91
|
-
decimal: {
|
|
92
|
-
// Numeric string with optional decimal point. Anchored.
|
|
93
|
-
// Rust stores any string (no validation), but we reject obviously invalid values.
|
|
94
|
-
pattern: /^-?\d+(\.\d+)?$/u,
|
|
95
|
-
example: "123.45"
|
|
96
|
-
}
|
|
97
|
-
};
|
|
98
|
-
|
|
99
|
-
/**
|
|
100
|
-
* Compiles one or more seed data files into a single merged JSON output.
|
|
101
|
-
*
|
|
102
|
-
* Pipeline: load each file → merge → validate against schema → write JSON.
|
|
26
|
+
* Pipeline: load each file -> feed into one {@link SeedBuilder} -> write JSON.
|
|
103
27
|
*
|
|
104
28
|
* @param seedFiles - Absolute paths to seed `.mts` / `.ts` files.
|
|
105
29
|
* @param outputPath - Where to write the merged seed JSON.
|
|
106
|
-
* @param
|
|
107
|
-
*
|
|
108
|
-
* @throws if any seed file fails to compile or has an invalid export,
|
|
109
|
-
*
|
|
110
|
-
* primary key is duplicated across files, if any property value is
|
|
111
|
-
* null/undefined or has the wrong JS type, or if any string-encoded
|
|
112
|
-
* value has an invalid format.
|
|
30
|
+
* @param metadata - Ontology metadata, typically parsed from the
|
|
31
|
+
* `ontology-metadata.json` written by the SDK generator.
|
|
32
|
+
* @throws if any seed file fails to compile or has an invalid default export,
|
|
33
|
+
* or if the builder rejects any object or link it is given.
|
|
113
34
|
*/
|
|
114
|
-
export async function compileSeedData(seedFiles, outputPath,
|
|
35
|
+
export async function compileSeedData(seedFiles, outputPath, metadata) {
|
|
115
36
|
consola.info(`Compiling seed data from ${seedFiles.length} file(s)...`);
|
|
116
|
-
const
|
|
37
|
+
const builder = new SeedBuilder(metadata);
|
|
117
38
|
for (const seedFile of seedFiles) {
|
|
118
|
-
|
|
39
|
+
const output = await loadSeedFile(seedFile);
|
|
40
|
+
try {
|
|
41
|
+
builder.addAll(output);
|
|
42
|
+
} catch (e) {
|
|
43
|
+
const message = e instanceof Error ? e.message : String(e);
|
|
44
|
+
throw new Error(`Seed file '${path.basename(seedFile)}': ${message}`);
|
|
45
|
+
}
|
|
119
46
|
}
|
|
120
|
-
const merged =
|
|
121
|
-
validateSeedOutput(merged, schema);
|
|
47
|
+
const merged = builder.build();
|
|
122
48
|
const totalObjects = Object.values(merged.objects).reduce((sum, arr) => sum + arr.length, 0);
|
|
123
49
|
const outputDir = path.dirname(outputPath);
|
|
124
50
|
await fs.promises.mkdir(outputDir, {
|
|
@@ -127,172 +53,23 @@ export async function compileSeedData(seedFiles, outputPath, schema) {
|
|
|
127
53
|
await fs.promises.writeFile(outputPath, JSON.stringify(merged, null, 2));
|
|
128
54
|
consola.success(`Seed data compiled successfully (${totalObjects} objects, ${merged.links.length} links)`);
|
|
129
55
|
}
|
|
56
|
+
const isRecord = value => typeof value === "object" && value != null && !Array.isArray(value);
|
|
57
|
+
const isSeedOutput = value => isRecord(value) && isRecord(value.objects);
|
|
58
|
+
const isSeedResult = value => isRecord(value) && isSeedOutput(value.output);
|
|
130
59
|
|
|
131
60
|
/**
|
|
132
|
-
*
|
|
133
|
-
*
|
|
134
|
-
* - Objects for the same type combine additively.
|
|
135
|
-
* - Duplicate primary keys across files cause an error (checked by the actual
|
|
136
|
-
* PK field from the schema, not by comparing the full serialized object).
|
|
137
|
-
* - Duplicate links (same source, target, and link type) are deduplicated
|
|
138
|
-
* with a warning logged to the console.
|
|
139
|
-
*
|
|
140
|
-
* @param outputs - The individual seed outputs to merge.
|
|
141
|
-
* @param schemaMap - Used to resolve the primary key field name per object type.
|
|
142
|
-
* @returns A single merged SeedOutput ready for validation and writing.
|
|
143
|
-
* @throws if any primary key appears in more than one file for the same type.
|
|
144
|
-
*/
|
|
145
|
-
export function mergeSeedOutputs(outputs, schemaMap) {
|
|
146
|
-
const merged = {
|
|
147
|
-
objects: {},
|
|
148
|
-
links: []
|
|
149
|
-
};
|
|
150
|
-
const seenPks = new Map();
|
|
151
|
-
const seenLinks = new Set();
|
|
152
|
-
for (const output of outputs) {
|
|
153
|
-
mergeObjectsInto(merged, output.objects, schemaMap, seenPks);
|
|
154
|
-
mergeLinksInto(merged, output.links, seenLinks);
|
|
155
|
-
}
|
|
156
|
-
return merged;
|
|
157
|
-
}
|
|
158
|
-
function mergeObjectsInto(merged, source, schemaMap, seenPks) {
|
|
159
|
-
for (const [apiName, objects] of Object.entries(source)) {
|
|
160
|
-
const schema = schemaMap.get(apiName);
|
|
161
|
-
if (!schema) {
|
|
162
|
-
throw new Error(`Object type '${apiName}' in seed data is not defined in the ontology`);
|
|
163
|
-
}
|
|
164
|
-
const bucket = merged.objects[apiName] ??= [];
|
|
165
|
-
const pkSet = getOrInit(seenPks, apiName, () => new Set());
|
|
166
|
-
for (const obj of objects) {
|
|
167
|
-
const pk = String(obj[schema.primaryKeyApiName] ?? "");
|
|
168
|
-
if (pkSet.has(pk)) {
|
|
169
|
-
throw new Error(`Duplicate primary key '${pk}' for '${apiName}' across seed files`);
|
|
170
|
-
}
|
|
171
|
-
pkSet.add(pk);
|
|
172
|
-
bucket.push(obj);
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
function mergeLinksInto(merged, source, seenLinks) {
|
|
177
|
-
for (const link of source) {
|
|
178
|
-
const key = linkKey(link);
|
|
179
|
-
if (seenLinks.has(key)) {
|
|
180
|
-
consola.warn(`Duplicate link deduplicated: ${link.linkType}` + ` from ${link.sourceObjectType}:${link.sourceKey}` + ` to ${link.targetObjectType}:${link.targetKey}`);
|
|
181
|
-
continue;
|
|
182
|
-
}
|
|
183
|
-
seenLinks.add(key);
|
|
184
|
-
merged.links.push(link);
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
function linkKey(link) {
|
|
188
|
-
return `${link.sourceObjectType}:${link.sourceKey}` + `:${link.linkType}` + `:${link.targetObjectType}:${link.targetKey}`;
|
|
189
|
-
}
|
|
190
|
-
function getOrInit(map, key, init) {
|
|
191
|
-
let value = map.get(key);
|
|
192
|
-
if (value === undefined) {
|
|
193
|
-
value = init();
|
|
194
|
-
map.set(key, value);
|
|
195
|
-
}
|
|
196
|
-
return value;
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
/**
|
|
200
|
-
* Validates the seed output against the ontology schema.
|
|
201
|
-
*
|
|
202
|
-
* Hard errors (thrown immediately) for any of:
|
|
203
|
-
*
|
|
204
|
-
* - Object types in the seed output not defined in the ontology.
|
|
205
|
-
* - Property names on a seed object not defined on that type.
|
|
206
|
-
* - `null`/`undefined` property values. Typed callers can't reach this
|
|
207
|
-
* (the builder's `SeedProps<Q>` rejects `null`); any null at runtime is
|
|
208
|
-
* a sign of `as any` or a hand-rolled output.
|
|
209
|
-
* - JS type mismatches against the cataloged wire-type-to-JS-type map
|
|
210
|
-
* (e.g., `age: "30"` when `age` is an integer, or `score: 30` when
|
|
211
|
-
* `score` is a long).
|
|
212
|
-
*
|
|
213
|
-
* For wire types whose runtime shape this validator doesn't catalog
|
|
214
|
-
* (`attachment`, `mediaReference`, `geopoint`, `geoshape`, `vector`,
|
|
215
|
-
* `array`, `struct`), JS-type checking is skipped — they have non-primitive
|
|
216
|
-
* shapes that would need bespoke validation. They still go through the
|
|
217
|
-
* earlier object-type / property-name / null checks.
|
|
61
|
+
* Loads a single seed file via jiti and extracts the {@link SeedOutput} from
|
|
62
|
+
* its default export.
|
|
218
63
|
*
|
|
219
|
-
*
|
|
220
|
-
*
|
|
221
|
-
*
|
|
222
|
-
* `string` value matches its wire format (e.g., `"not-a-date"` vs
|
|
223
|
-
* `"2025-01-01T00:00:00Z"` are both valid `string` to the type system);
|
|
224
|
-
* format validation fills that gap.
|
|
225
|
-
*
|
|
226
|
-
* @throws Error on any structural violation listed above, or listing all
|
|
227
|
-
* format failures grouped by object type.
|
|
228
|
-
*/
|
|
229
|
-
export function validateSeedOutput(output, schemaMap) {
|
|
230
|
-
const errors = validateAndCollectFormatErrors(output, schemaMap);
|
|
231
|
-
if (errors.length > 0) {
|
|
232
|
-
throw new Error(formatValidationErrors(errors));
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
function validateAndCollectFormatErrors(output, schemaMap) {
|
|
236
|
-
const errors = [];
|
|
237
|
-
for (const [apiName, objects] of Object.entries(output.objects)) {
|
|
238
|
-
const schema = schemaMap.get(apiName);
|
|
239
|
-
if (!schema) {
|
|
240
|
-
throw new Error(`Object type '${apiName}' in seed data is not defined in the ontology`);
|
|
241
|
-
}
|
|
242
|
-
for (const [i, obj] of objects.entries()) {
|
|
243
|
-
for (const [key, value] of Object.entries(obj)) {
|
|
244
|
-
const wireType = schema.properties.get(key);
|
|
245
|
-
if (wireType === undefined) {
|
|
246
|
-
throw new Error(`Property '${key}' on '${apiName}' object` + ` (index ${i}) is not defined in the ontology`);
|
|
247
|
-
}
|
|
248
|
-
if (value == null) {
|
|
249
|
-
throw new Error(`Property '${key}' on '${apiName}' object` + ` (index ${i}) is null or undefined`);
|
|
250
|
-
}
|
|
251
|
-
const expectedJsType = EXPECTED_JS_TYPE[wireType];
|
|
252
|
-
if (expectedJsType !== undefined && typeof value !== expectedJsType) {
|
|
253
|
-
throw new Error(`Property '${key}' on '${apiName}' object` + ` (index ${i}) expects ${wireType} (a ${expectedJsType})` + ` but got ${typeof value}`);
|
|
254
|
-
}
|
|
255
|
-
const format = WIRE_TYPE_FORMAT[wireType];
|
|
256
|
-
if (!format) continue;
|
|
257
|
-
|
|
258
|
-
// Format regex only applies to string-encoded wire types, all of
|
|
259
|
-
// which have EXPECTED_JS_TYPE === "string"; the cast is safe here
|
|
260
|
-
// because the JS-type check above would have thrown otherwise.
|
|
261
|
-
if (format.pattern.test(value)) continue;
|
|
262
|
-
errors.push({
|
|
263
|
-
objectType: apiName,
|
|
264
|
-
objectIndex: i,
|
|
265
|
-
field: key,
|
|
266
|
-
message: `property '${key}' has invalid ${wireType}` + ` format: '${String(value)}'. Expected format like '${format.example}'`
|
|
267
|
-
});
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
return errors;
|
|
272
|
-
}
|
|
273
|
-
function formatValidationErrors(errors) {
|
|
274
|
-
const grouped = new Map();
|
|
275
|
-
for (const err of errors) {
|
|
276
|
-
const messages = getOrInit(grouped, err.objectType, () => []);
|
|
277
|
-
messages.push(` object[${err.objectIndex}]: ${err.message}`);
|
|
278
|
-
}
|
|
279
|
-
const body = [...grouped.entries()].map(([type, msgs]) => `${type}:\n${msgs.join("\n")}`).join("\n\n");
|
|
280
|
-
const errorWord = errors.length === 1 ? "error" : "errors";
|
|
281
|
-
const typeWord = grouped.size === 1 ? "object type" : "object types";
|
|
282
|
-
return `Seed data validation failed ` + `(${errors.length} ${errorWord} across ${grouped.size} ${typeWord}` + `):\n\n${body}`;
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
/**
|
|
286
|
-
* Loads a single seed file via jiti and extracts its default export.
|
|
287
|
-
*
|
|
288
|
-
* jiti.import() fully executes the module — the `createSeed()` builder
|
|
289
|
-
* function runs during import and the default export is the resulting
|
|
290
|
-
* SeedOutput.
|
|
64
|
+
* Both shapes a seed author naturally reaches for are accepted: the
|
|
65
|
+
* `createSeed(...)` result (`{ output, context }`) and its `.output` (a bare
|
|
66
|
+
* `SeedOutput`).
|
|
291
67
|
*
|
|
292
68
|
* @throws with a contextual message wrapping the original error and filename.
|
|
293
69
|
*/
|
|
294
70
|
async function loadSeedFile(seedFile) {
|
|
295
71
|
consola.info(`Loading seed file: ${seedFile}`);
|
|
72
|
+
const name = path.basename(seedFile);
|
|
296
73
|
let seedModule;
|
|
297
74
|
try {
|
|
298
75
|
const jiti = createJiti(seedFile, {
|
|
@@ -302,20 +79,26 @@ async function loadSeedFile(seedFile) {
|
|
|
302
79
|
seedModule = await jiti.import(seedFile);
|
|
303
80
|
} catch (e) {
|
|
304
81
|
const message = e instanceof Error ? e.message : String(e);
|
|
305
|
-
throw new Error(`Seed file '${
|
|
82
|
+
throw new Error(`Seed file '${name}' failed to compile:\n ${message}`);
|
|
306
83
|
}
|
|
307
|
-
if (!seedModule
|
|
308
|
-
throw new Error(`Seed file '${
|
|
84
|
+
if (!isRecord(seedModule) || !Object.hasOwn(seedModule, "default")) {
|
|
85
|
+
throw new Error(`Seed file '${name}' must have a default export. Export the result of ` + `createSeed(), which wraps createSeedWithMetadata() from ` + `@osdk/seed-helpers.`);
|
|
309
86
|
}
|
|
310
|
-
const
|
|
311
|
-
|
|
312
|
-
|
|
87
|
+
const defaultExport = seedModule.default;
|
|
88
|
+
const output = isSeedResult(defaultExport) ? defaultExport.output : isSeedOutput(defaultExport) ? defaultExport : undefined;
|
|
89
|
+
if (!output) {
|
|
90
|
+
throw new Error(`Seed file '${name}' default export is not a createSeed() result.\n` + `Export either createSeed(...) — an object with an 'output' property\n ` + `or createSeed(...).output — an object with an 'objects' property`);
|
|
91
|
+
}
|
|
92
|
+
for (const [apiName, objects] of Object.entries(output.objects)) {
|
|
93
|
+
if (!Array.isArray(objects)) {
|
|
94
|
+
throw new TypeError(`Seed file '${name}' has a non-array entry for object type ` + `'${apiName}': expected an array of objects`);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
if (output.links !== undefined && !Array.isArray(output.links)) {
|
|
98
|
+
throw new TypeError(`Seed file '${name}' has a non-array 'links': expected an array of link entries`);
|
|
313
99
|
}
|
|
314
|
-
|
|
315
|
-
// Normalize: links are optional in the export but required in SeedOutput.
|
|
316
|
-
// Spread to avoid mutating the module's exported object.
|
|
317
100
|
return {
|
|
318
|
-
|
|
101
|
+
objects: output.objects,
|
|
319
102
|
links: output.links ?? []
|
|
320
103
|
};
|
|
321
104
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"compileSeedData.js","names":["fs","path","consola","createJiti","EXPECTED_JS_TYPE","string","marking","timestamp","date","datetime","long","decimal","ipAddress","cipherText","integer","byte","short","double","float","boolean","WIRE_TYPE_FORMAT","pattern","example","compileSeedData","seedFiles","outputPath","schema","info","length","outputs","seedFile","push","loadSeedFile","merged","mergeSeedOutputs","validateSeedOutput","totalObjects","Object","values","objects","reduce","sum","arr","outputDir","dirname","promises","mkdir","recursive","writeFile","JSON","stringify","success","links","schemaMap","seenPks","Map","seenLinks","Set","output","mergeObjectsInto","mergeLinksInto","source","apiName","entries","get","Error","bucket","pkSet","getOrInit","obj","pk","String","primaryKeyApiName","has","add","link","key","linkKey","warn","linkType","sourceObjectType","sourceKey","targetObjectType","targetKey","map","init","value","undefined","set","errors","validateAndCollectFormatErrors","formatValidationErrors","i","wireType","properties","expectedJsType","format","test","objectType","objectIndex","field","message","grouped","err","messages","body","type","msgs","join","errorWord","typeWord","size","seedModule","jiti","moduleCache","debug","import","e","basename","default"],"sources":["compileSeedData.ts"],"sourcesContent":["/*\n * Copyright 2025 Palantir Technologies, Inc. All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\n\nimport type { SeedLinkEntry, SeedOutput } from \"@osdk/seed-helpers\";\nimport { consola } from \"consola\";\nimport { createJiti } from \"jiti\";\n\nimport type { SchemaMap } from \"./schema.js\";\n\n/**\n * One string-format validation failure (e.g., a timestamp value that doesn't\n * match the wire format regex). Format failures are collected across the\n * whole output and reported together at the end so users can fix many\n * content mistakes in one pass. Structural failures (unknown object type,\n * unknown property name, null value, wrong JS type) throw immediately\n * instead — they're not aggregated, so they don't need this struct.\n */\ninterface FormatError {\n objectType: string;\n objectIndex: number;\n field: string;\n message: string;\n}\n\n/**\n * Expected runtime JS type for each cataloged wire type.\n *\n * Used to fail fast on `as any` callers that pass a value of the wrong shape\n * (e.g., `age: \"30\"` when `age` is an integer). Wire types not listed here\n * are not strictly typed by this validator — currently `attachment`,\n * `mediaReference`, `geopoint`, `geoshape`, `vector`, `array`, `struct`,\n * which have non-primitive runtime shapes that need bespoke validation.\n */\nconst EXPECTED_JS_TYPE: Record<string, \"string\" | \"number\" | \"boolean\"> = {\n // string-encoded primitives\n string: \"string\",\n marking: \"string\",\n timestamp: \"string\",\n date: \"string\",\n datetime: \"string\",\n long: \"string\",\n decimal: \"string\",\n ipAddress: \"string\",\n cipherText: \"string\",\n // numeric primitives\n integer: \"number\",\n byte: \"number\",\n short: \"number\",\n double: \"number\",\n float: \"number\",\n // boolean\n boolean: \"boolean\",\n};\n\n/**\n * Regex patterns for string-encoded wire types that TypeScript cannot validate.\n *\n * These patterns are aligned with the Rust backend's actual parsing behavior:\n * - timestamp: RFC 3339 parse on the Rust side — requires timezone, rejects trailing garbage\n * - date: stored as raw string, but only YYYY-MM-DD works in SQLite queries\n * - datetime: stored as raw string, same YYYY-MM-DD requirement for query correctness\n * - long: strict decimal integer parse on the Rust side — no scientific notation\n * - decimal: Rust stores any string (no validation), but we enforce numeric format\n * to prevent obviously invalid values from silently passing through\n */\nconst WIRE_TYPE_FORMAT: Record<string, { pattern: RegExp; example: string }> = {\n timestamp: {\n pattern:\n /^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?(Z|[+-]\\d{2}:\\d{2})$/u,\n example: \"2025-01-01T00:00:00Z\",\n },\n date: {\n pattern: /^\\d{4}-\\d{2}-\\d{2}$/u,\n example: \"2025-01-01\",\n },\n datetime: {\n pattern:\n /^\\d{4}-\\d{2}-\\d{2}(T\\d{2}:\\d{2}:\\d{2}(\\.\\d+)?(Z|[+-]\\d{2}:\\d{2})?)?$/u,\n example: \"2025-01-01T12:00:00Z\",\n },\n long: {\n // Strict decimal integer — matches Rust's str::parse::<i64>().\n // No scientific notation, no decimal point, no whitespace.\n pattern: /^-?\\d+$/u,\n example: \"9007199254740993\",\n },\n decimal: {\n // Numeric string with optional decimal point. Anchored.\n // Rust stores any string (no validation), but we reject obviously invalid values.\n pattern: /^-?\\d+(\\.\\d+)?$/u,\n example: \"123.45\",\n },\n};\n\n/**\n * Compiles one or more seed data files into a single merged JSON output.\n *\n * Pipeline: load each file → merge → validate against schema → write JSON.\n *\n * @param seedFiles - Absolute paths to seed `.mts` / `.ts` files.\n * @param outputPath - Where to write the merged seed JSON.\n * @param schema - Per-object-type schema, typically built from\n * {@link import(\"./schema.js\").schemaFromMetadata}.\n * @throws if any seed file fails to compile or has an invalid export, if any\n * object type or property name is unknown to the schema, if any\n * primary key is duplicated across files, if any property value is\n * null/undefined or has the wrong JS type, or if any string-encoded\n * value has an invalid format.\n */\nexport async function compileSeedData(\n seedFiles: string[],\n outputPath: string,\n schema: SchemaMap,\n): Promise<void> {\n consola.info(`Compiling seed data from ${seedFiles.length} file(s)...`);\n\n const outputs: SeedOutput[] = [];\n for (const seedFile of seedFiles) {\n outputs.push(await loadSeedFile(seedFile));\n }\n\n const merged = mergeSeedOutputs(outputs, schema);\n validateSeedOutput(merged, schema);\n\n const totalObjects = Object.values(merged.objects).reduce(\n (sum, arr) => sum + arr.length,\n 0,\n );\n\n const outputDir = path.dirname(outputPath);\n await fs.promises.mkdir(outputDir, { recursive: true });\n await fs.promises.writeFile(outputPath, JSON.stringify(merged, null, 2));\n\n consola.success(\n `Seed data compiled successfully (${totalObjects} objects, ${merged.links.length} links)`,\n );\n}\n\n/**\n * Merges multiple {@link SeedOutput}s into one.\n *\n * - Objects for the same type combine additively.\n * - Duplicate primary keys across files cause an error (checked by the actual\n * PK field from the schema, not by comparing the full serialized object).\n * - Duplicate links (same source, target, and link type) are deduplicated\n * with a warning logged to the console.\n *\n * @param outputs - The individual seed outputs to merge.\n * @param schemaMap - Used to resolve the primary key field name per object type.\n * @returns A single merged SeedOutput ready for validation and writing.\n * @throws if any primary key appears in more than one file for the same type.\n */\nexport function mergeSeedOutputs(\n outputs: SeedOutput[],\n schemaMap: SchemaMap,\n): SeedOutput {\n const merged: SeedOutput = { objects: {}, links: [] };\n const seenPks = new Map<string, Set<string>>();\n const seenLinks = new Set<string>();\n\n for (const output of outputs) {\n mergeObjectsInto(merged, output.objects, schemaMap, seenPks);\n mergeLinksInto(merged, output.links, seenLinks);\n }\n return merged;\n}\n\nfunction mergeObjectsInto(\n merged: SeedOutput,\n source: SeedOutput[\"objects\"],\n schemaMap: SchemaMap,\n seenPks: Map<string, Set<string>>,\n): void {\n for (const [apiName, objects] of Object.entries(source)) {\n const schema = schemaMap.get(apiName);\n if (!schema) {\n throw new Error(\n `Object type '${apiName}' in seed data is not defined in the ontology`,\n );\n }\n\n const bucket = (merged.objects[apiName] ??= []);\n const pkSet = getOrInit(seenPks, apiName, () => new Set<string>());\n\n for (const obj of objects) {\n const pk = String(obj[schema.primaryKeyApiName] ?? \"\");\n if (pkSet.has(pk)) {\n throw new Error(\n `Duplicate primary key '${pk}' for '${apiName}' across seed files`,\n );\n }\n pkSet.add(pk);\n bucket.push(obj);\n }\n }\n}\n\nfunction mergeLinksInto(\n merged: SeedOutput,\n source: SeedOutput[\"links\"],\n seenLinks: Set<string>,\n): void {\n for (const link of source) {\n const key = linkKey(link);\n if (seenLinks.has(key)) {\n consola.warn(\n `Duplicate link deduplicated: ${link.linkType}` +\n ` from ${link.sourceObjectType}:${link.sourceKey}` +\n ` to ${link.targetObjectType}:${link.targetKey}`,\n );\n continue;\n }\n seenLinks.add(key);\n merged.links.push(link);\n }\n}\n\nfunction linkKey(link: SeedLinkEntry): string {\n return (\n `${link.sourceObjectType}:${link.sourceKey}` +\n `:${link.linkType}` +\n `:${link.targetObjectType}:${link.targetKey}`\n );\n}\n\nfunction getOrInit<K, V>(map: Map<K, V>, key: K, init: () => V): V {\n let value = map.get(key);\n if (value === undefined) {\n value = init();\n map.set(key, value);\n }\n return value;\n}\n\n/**\n * Validates the seed output against the ontology schema.\n *\n * Hard errors (thrown immediately) for any of:\n *\n * - Object types in the seed output not defined in the ontology.\n * - Property names on a seed object not defined on that type.\n * - `null`/`undefined` property values. Typed callers can't reach this\n * (the builder's `SeedProps<Q>` rejects `null`); any null at runtime is\n * a sign of `as any` or a hand-rolled output.\n * - JS type mismatches against the cataloged wire-type-to-JS-type map\n * (e.g., `age: \"30\"` when `age` is an integer, or `score: 30` when\n * `score` is a long).\n *\n * For wire types whose runtime shape this validator doesn't catalog\n * (`attachment`, `mediaReference`, `geopoint`, `geoshape`, `vector`,\n * `array`, `struct`), JS-type checking is skipped — they have non-primitive\n * shapes that would need bespoke validation. They still go through the\n * earlier object-type / property-name / null checks.\n *\n * After JS-type validation, string values are checked against the format\n * regex for their wire type (timestamp, date, datetime, long, decimal).\n * The one thing TypeScript cannot distinguish on its own is whether a\n * `string` value matches its wire format (e.g., `\"not-a-date\"` vs\n * `\"2025-01-01T00:00:00Z\"` are both valid `string` to the type system);\n * format validation fills that gap.\n *\n * @throws Error on any structural violation listed above, or listing all\n * format failures grouped by object type.\n */\nexport function validateSeedOutput(\n output: SeedOutput,\n schemaMap: SchemaMap,\n): void {\n const errors = validateAndCollectFormatErrors(output, schemaMap);\n if (errors.length > 0) {\n throw new Error(formatValidationErrors(errors));\n }\n}\n\nfunction validateAndCollectFormatErrors(\n output: SeedOutput,\n schemaMap: SchemaMap,\n): FormatError[] {\n const errors: FormatError[] = [];\n\n for (const [apiName, objects] of Object.entries(output.objects)) {\n const schema = schemaMap.get(apiName);\n if (!schema) {\n throw new Error(\n `Object type '${apiName}' in seed data is not defined in the ontology`,\n );\n }\n\n for (const [i, obj] of objects.entries()) {\n for (const [key, value] of Object.entries(obj)) {\n const wireType = schema.properties.get(key);\n if (wireType === undefined) {\n throw new Error(\n `Property '${key}' on '${apiName}' object` +\n ` (index ${i}) is not defined in the ontology`,\n );\n }\n\n if (value == null) {\n throw new Error(\n `Property '${key}' on '${apiName}' object` +\n ` (index ${i}) is null or undefined`,\n );\n }\n\n const expectedJsType = EXPECTED_JS_TYPE[wireType];\n if (expectedJsType !== undefined && typeof value !== expectedJsType) {\n throw new Error(\n `Property '${key}' on '${apiName}' object` +\n ` (index ${i}) expects ${wireType} (a ${expectedJsType})` +\n ` but got ${typeof value}`,\n );\n }\n\n const format = WIRE_TYPE_FORMAT[wireType];\n if (!format) continue;\n\n // Format regex only applies to string-encoded wire types, all of\n // which have EXPECTED_JS_TYPE === \"string\"; the cast is safe here\n // because the JS-type check above would have thrown otherwise.\n if (format.pattern.test(value as string)) continue;\n\n errors.push({\n objectType: apiName,\n objectIndex: i,\n field: key,\n message:\n `property '${key}' has invalid ${wireType}` +\n ` format: '${String(\n value,\n )}'. Expected format like '${format.example}'`,\n });\n }\n }\n }\n\n return errors;\n}\n\nfunction formatValidationErrors(errors: FormatError[]): string {\n const grouped = new Map<string, string[]>();\n for (const err of errors) {\n const messages = getOrInit(grouped, err.objectType, () => []);\n messages.push(` object[${err.objectIndex}]: ${err.message}`);\n }\n\n const body = [...grouped.entries()]\n .map(([type, msgs]) => `${type}:\\n${msgs.join(\"\\n\")}`)\n .join(\"\\n\\n\");\n\n const errorWord = errors.length === 1 ? \"error\" : \"errors\";\n const typeWord = grouped.size === 1 ? \"object type\" : \"object types\";\n\n return (\n `Seed data validation failed ` +\n `(${errors.length} ${errorWord} across ${grouped.size} ${typeWord}` +\n `):\\n\\n${body}`\n );\n}\n\n/**\n * Loads a single seed file via jiti and extracts its default export.\n *\n * jiti.import() fully executes the module — the `createSeed()` builder\n * function runs during import and the default export is the resulting\n * SeedOutput.\n *\n * @throws with a contextual message wrapping the original error and filename.\n */\nasync function loadSeedFile(seedFile: string): Promise<SeedOutput> {\n consola.info(`Loading seed file: ${seedFile}`);\n\n let seedModule: { default: SeedOutput };\n try {\n const jiti = createJiti(seedFile, {\n moduleCache: false,\n debug: false,\n });\n seedModule = (await jiti.import(seedFile)) as { default: SeedOutput };\n } catch (e: unknown) {\n const message = e instanceof Error ? e.message : String(e);\n throw new Error(\n `Seed file '${path.basename(seedFile)}' failed to compile:\\n ${message}`,\n );\n }\n\n if (!seedModule.default || typeof seedModule.default !== \"object\") {\n throw new Error(\n `Seed file '${path.basename(seedFile)}' must have a default export. ` +\n `Use createSeed() from @osdk/seed-helpers.`,\n );\n }\n\n const output = seedModule.default;\n\n if (!output.objects || typeof output.objects !== \"object\") {\n throw new Error(\n `Seed file '${path.basename(seedFile)}' default export is not a valid` +\n ` SeedOutput. Use createSeed() from @osdk/seed-helpers.`,\n );\n }\n\n // Normalize: links are optional in the export but required in SeedOutput.\n // Spread to avoid mutating the module's exported object.\n return { ...output, links: output.links ?? [] };\n}\n"],"mappings":"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","ignoreList":[]}
|
|
1
|
+
{"version":3,"file":"compileSeedData.js","names":["fs","path","SeedBuilder","consola","createJiti","compileSeedData","seedFiles","outputPath","metadata","info","length","builder","seedFile","output","loadSeedFile","addAll","e","message","Error","String","basename","merged","build","totalObjects","Object","values","objects","reduce","sum","arr","outputDir","dirname","promises","mkdir","recursive","writeFile","JSON","stringify","success","links","isRecord","value","Array","isArray","isSeedOutput","isSeedResult","name","seedModule","jiti","moduleCache","debug","import","hasOwn","defaultExport","default","undefined","apiName","entries","TypeError"],"sources":["compileSeedData.ts"],"sourcesContent":["/*\n * Copyright 2025 Palantir Technologies, Inc. All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport * as fs from \"node:fs\";\nimport * as path from \"node:path\";\n\nimport type { OntologyFullMetadata } from \"@osdk/foundry.ontologies\";\nimport { SeedBuilder, type SeedOutput } from \"@osdk/seed-helpers\";\nimport { consola } from \"consola\";\nimport { createJiti } from \"jiti\";\n\n/**\n * Merges one or more seed data files into a single JSON output.\n *\n * Pipeline: load each file -> feed into one {@link SeedBuilder} -> write JSON.\n *\n * @param seedFiles - Absolute paths to seed `.mts` / `.ts` files.\n * @param outputPath - Where to write the merged seed JSON.\n * @param metadata - Ontology metadata, typically parsed from the\n * `ontology-metadata.json` written by the SDK generator.\n * @throws if any seed file fails to compile or has an invalid default export,\n * or if the builder rejects any object or link it is given.\n */\nexport async function compileSeedData(\n seedFiles: string[],\n outputPath: string,\n metadata: OntologyFullMetadata,\n): Promise<void> {\n consola.info(`Compiling seed data from ${seedFiles.length} file(s)...`);\n const builder = new SeedBuilder(metadata);\n for (const seedFile of seedFiles) {\n const output = await loadSeedFile(seedFile);\n try {\n builder.addAll(output);\n } catch (e: unknown) {\n const message = e instanceof Error ? e.message : String(e);\n throw new Error(`Seed file '${path.basename(seedFile)}': ${message}`);\n }\n }\n const merged = builder.build();\n const totalObjects = Object.values(merged.objects).reduce(\n (sum, arr) => sum + arr.length,\n 0,\n );\n\n const outputDir = path.dirname(outputPath);\n await fs.promises.mkdir(outputDir, { recursive: true });\n await fs.promises.writeFile(outputPath, JSON.stringify(merged, null, 2));\n\n consola.success(\n `Seed data compiled successfully (${totalObjects} objects, ${merged.links.length} links)`,\n );\n}\n\ntype LoadedSeedOutput = Pick<SeedOutput, \"objects\"> &\n Partial<Pick<SeedOutput, \"links\">>;\n\nconst isRecord = (value: unknown): value is Record<string, unknown> =>\n typeof value === \"object\" && value != null && !Array.isArray(value);\n\nconst isSeedOutput = (value: unknown): value is LoadedSeedOutput =>\n isRecord(value) && isRecord(value.objects);\n\nconst isSeedResult = (value: unknown): value is { output: LoadedSeedOutput } =>\n isRecord(value) && isSeedOutput(value.output);\n\n/**\n * Loads a single seed file via jiti and extracts the {@link SeedOutput} from\n * its default export.\n *\n * Both shapes a seed author naturally reaches for are accepted: the\n * `createSeed(...)` result (`{ output, context }`) and its `.output` (a bare\n * `SeedOutput`).\n *\n * @throws with a contextual message wrapping the original error and filename.\n */\nasync function loadSeedFile(seedFile: string): Promise<SeedOutput> {\n consola.info(`Loading seed file: ${seedFile}`);\n const name = path.basename(seedFile);\n let seedModule: Record<string, unknown>;\n\n try {\n const jiti = createJiti(seedFile, {\n moduleCache: false,\n debug: false,\n });\n seedModule = (await jiti.import(seedFile)) as Record<string, unknown>;\n } catch (e: unknown) {\n const message = e instanceof Error ? e.message : String(e);\n throw new Error(`Seed file '${name}' failed to compile:\\n ${message}`);\n }\n\n if (!isRecord(seedModule) || !Object.hasOwn(seedModule, \"default\")) {\n throw new Error(\n `Seed file '${name}' must have a default export. Export the result of ` +\n `createSeed(), which wraps createSeedWithMetadata() from ` +\n `@osdk/seed-helpers.`,\n );\n }\n\n const defaultExport = seedModule.default;\n const output = isSeedResult(defaultExport)\n ? defaultExport.output\n : isSeedOutput(defaultExport)\n ? defaultExport\n : undefined;\n\n if (!output) {\n throw new Error(\n `Seed file '${name}' default export is not a createSeed() result.\\n` +\n `Export either createSeed(...) — an object with an 'output' property\\n ` +\n `or createSeed(...).output — an object with an 'objects' property`,\n );\n }\n\n for (const [apiName, objects] of Object.entries(output.objects)) {\n if (!Array.isArray(objects)) {\n throw new TypeError(\n `Seed file '${name}' has a non-array entry for object type ` +\n `'${apiName}': expected an array of objects`,\n );\n }\n }\n\n if (output.links !== undefined && !Array.isArray(output.links)) {\n throw new TypeError(\n `Seed file '${name}' has a non-array 'links': expected an array of link entries`,\n );\n }\n\n return { objects: output.objects, links: output.links ?? [] };\n}\n"],"mappings":"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","ignoreList":[]}
|