@lunora/seed 1.0.0-alpha.3 → 1.0.0-alpha.30
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/LICENSE.md +6 -0
- package/dist/index.d.mts +28 -28
- package/dist/index.d.ts +28 -28
- package/dist/index.mjs +2 -2
- package/dist/packem_shared/{createSeedClient-CI9LRLTi.mjs → createSeedClient-CZuGqXLm.mjs} +11 -2
- package/dist/packem_shared/{plan-DirmkctQ.mjs → plan-DybYaa3F.mjs} +35 -6
- package/dist/packem_shared/plan.d-SaRxbrrI.d.mts +78 -0
- package/dist/packem_shared/plan.d-SaRxbrrI.d.ts +78 -0
- package/dist/packem_shared/seedPlan-DbOn48o6.mjs +2 -0
- package/dist/testing.d.mts +1 -1
- package/dist/testing.d.ts +1 -1
- package/dist/testing.mjs +1 -1
- package/package.json +6 -5
- package/dist/packem_shared/plan.d-BQ6LiWIk.d.mts +0 -78
- package/dist/packem_shared/plan.d-BQ6LiWIk.d.ts +0 -78
- package/dist/packem_shared/seedPlan-CDSmSgjY.mjs +0 -1
package/LICENSE.md
CHANGED
|
@@ -103,3 +103,9 @@ Unless required by applicable law or agreed to in writing, software distributed
|
|
|
103
103
|
under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
|
104
104
|
CONDITIONS OF ANY KIND, either express or implied. See the License for the
|
|
105
105
|
specific language governing permissions and limitations under the License.
|
|
106
|
+
|
|
107
|
+
<!-- DEPENDENCIES -->
|
|
108
|
+
<!-- /DEPENDENCIES -->
|
|
109
|
+
|
|
110
|
+
<!-- TYPE_DEPENDENCIES -->
|
|
111
|
+
<!-- /TYPE_DEPENDENCIES -->
|
package/dist/index.d.mts
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
import { Schema } from '@lunora/server';
|
|
2
|
-
import { O as OverrideContext } from "./packem_shared/plan.d-
|
|
3
|
-
export { type S as SeedCounts, type a as SeedOptions, type b as SeedOverrides, type T as TablePlan, s as seedPlan } from "./packem_shared/plan.d-
|
|
2
|
+
import { O as OverrideContext } from "./packem_shared/plan.d-SaRxbrrI.mjs";
|
|
3
|
+
export { type S as SeedCounts, type a as SeedOptions, type b as SeedOverrides, type T as TablePlan, s as seedPlan } from "./packem_shared/plan.d-SaRxbrrI.mjs";
|
|
4
4
|
/**
|
|
5
|
-
* A typed, schema-aware seed client — the ergonomic DX layer over {@link seedPlan}.
|
|
6
|
-
*
|
|
7
|
-
* Where `seedPlan` generates every table at once, the client lets you author one
|
|
8
|
-
* table at a time, with autocomplete on each table's columns and live foreign-key
|
|
9
|
-
* connection to whatever was seeded earlier this run. Pass your generated
|
|
10
|
-
* `InsertModel` type as the type argument — or let `lunora/_generated/seed.ts`
|
|
11
|
-
* do that for you — for full type inference on each table's columns.
|
|
12
|
-
*
|
|
13
|
-
* The client is deterministic (same `seed` ⇒ same rows and ids) and pure unless a
|
|
14
|
-
* `persist` hook is supplied, in which case each generated batch is also written
|
|
15
|
-
* through it (the test harness, an admin import, …). Because every row carries an
|
|
16
|
-
* explicit `_id`, the returned ids are known whether or not anything is persisted.
|
|
17
|
-
*/
|
|
5
|
+
* A typed, schema-aware seed client — the ergonomic DX layer over {@link seedPlan}.
|
|
6
|
+
*
|
|
7
|
+
* Where `seedPlan` generates every table at once, the client lets you author one
|
|
8
|
+
* table at a time, with autocomplete on each table's columns and live foreign-key
|
|
9
|
+
* connection to whatever was seeded earlier this run. Pass your generated
|
|
10
|
+
* `InsertModel` type as the type argument — or let `lunora/_generated/seed.ts`
|
|
11
|
+
* do that for you — for full type inference on each table's columns.
|
|
12
|
+
*
|
|
13
|
+
* The client is deterministic (same `seed` ⇒ same rows and ids) and pure unless a
|
|
14
|
+
* `persist` hook is supplied, in which case each generated batch is also written
|
|
15
|
+
* through it (the test harness, an admin import, …). Because every row carries an
|
|
16
|
+
* explicit `_id`, the returned ids are known whether or not anything is persisted.
|
|
17
|
+
*/
|
|
18
18
|
/** Picks a concrete count: a fixed number, or a deterministic value within `[min, max]`. */
|
|
19
19
|
type CountHelper = (countOrRange: number | readonly [number, number]) => number;
|
|
20
20
|
/** The per-call population spec: a count, an inclusive range, explicit partial rows, or a count callback. */
|
|
@@ -23,10 +23,10 @@ type SeedSpec<Row> = number | ReadonlyArray<Partial<Row>> | readonly [number, nu
|
|
|
23
23
|
type FieldOverride<Value> = ((context: OverrideContext) => Value) | Value;
|
|
24
24
|
/** Options for a single table call. `overrides` win over any explicit partial rows. */
|
|
25
25
|
interface SeedCallOptions<Row> {
|
|
26
|
-
overrides?: { [K in keyof Row]?: FieldOverride<Row[K]
|
|
26
|
+
overrides?: { [K in keyof Row]?: FieldOverride<Row[K]>; };
|
|
27
27
|
}
|
|
28
28
|
/** The id columns the client emits, keyed by table — the result of a single table call. */
|
|
29
|
-
type SeedCallResult<InsertModel, Table extends keyof InsertModel> = { [K in Table]: string[] };
|
|
29
|
+
type SeedCallResult<InsertModel, Table extends keyof InsertModel> = { [K in Table]: string[]; };
|
|
30
30
|
/** The seeder function exposed for one table. */
|
|
31
31
|
type TableSeeder<InsertModel, Table extends keyof InsertModel> = (spec?: SeedSpec<InsertModel[Table]>, options?: SeedCallOptions<InsertModel[Table]>) => Promise<SeedCallResult<InsertModel, Table>>;
|
|
32
32
|
/** The `$`-prefixed run state shared by every client. */
|
|
@@ -39,7 +39,7 @@ interface SeedClientState {
|
|
|
39
39
|
readonly $store: Readonly<Record<string, ReadonlyArray<Record<string, unknown>>>>;
|
|
40
40
|
}
|
|
41
41
|
/** The per-table seeder methods plus the `$`-prefixed run state. */
|
|
42
|
-
type SeedClient<InsertModel> = SeedClientState & { [Table in keyof InsertModel]: TableSeeder<InsertModel, Table
|
|
42
|
+
type SeedClient<InsertModel> = SeedClientState & { [Table in keyof InsertModel]: TableSeeder<InsertModel, Table>; };
|
|
43
43
|
/** Options for {@link createSeedClient}. */
|
|
44
44
|
interface SeedClientOptions {
|
|
45
45
|
/** Default row count when a table call passes no spec (default `10`). */
|
|
@@ -50,15 +50,15 @@ interface SeedClientOptions {
|
|
|
50
50
|
seed?: number;
|
|
51
51
|
}
|
|
52
52
|
/**
|
|
53
|
-
* Create a typed seed client for `schema`. Each table is a method; call it with a
|
|
54
|
-
* count, a range, or explicit partial rows, and its foreign keys connect to rows
|
|
55
|
-
* seeded earlier this run. State accumulates on `$store`/`$ids` and clears with
|
|
56
|
-
* `$reset()`.
|
|
57
|
-
* @example
|
|
58
|
-
* const seed = createSeedClient<InsertModel>(schema, { seed: 1 });
|
|
59
|
-
* const { users } = await seed.users(5);
|
|
60
|
-
* const { posts } = await seed.posts((x) => x([10, 20]));
|
|
61
|
-
* // posts.authorId values are drawn from `users`.
|
|
62
|
-
*/
|
|
53
|
+
* Create a typed seed client for `schema`. Each table is a method; call it with a
|
|
54
|
+
* count, a range, or explicit partial rows, and its foreign keys connect to rows
|
|
55
|
+
* seeded earlier this run. State accumulates on `$store`/`$ids` and clears with
|
|
56
|
+
* `$reset()`.
|
|
57
|
+
* @example
|
|
58
|
+
* const seed = createSeedClient<InsertModel>(schema, { seed: 1 });
|
|
59
|
+
* const { users } = await seed.users(5);
|
|
60
|
+
* const { posts } = await seed.posts((x) => x([10, 20]));
|
|
61
|
+
* // posts.authorId values are drawn from `users`.
|
|
62
|
+
*/
|
|
63
63
|
declare const createSeedClient: <InsertModel = Record<string, Record<string, unknown>>>(schema: Schema, options?: SeedClientOptions) => SeedClient<InsertModel>;
|
|
64
64
|
export { type CountHelper, type OverrideContext, type SeedCallOptions, type SeedCallResult, type SeedClient, type SeedClientOptions, type SeedSpec, createSeedClient };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
import { Schema } from '@lunora/server';
|
|
2
|
-
import { O as OverrideContext } from "./packem_shared/plan.d-
|
|
3
|
-
export { type S as SeedCounts, type a as SeedOptions, type b as SeedOverrides, type T as TablePlan, s as seedPlan } from "./packem_shared/plan.d-
|
|
2
|
+
import { O as OverrideContext } from "./packem_shared/plan.d-SaRxbrrI.js";
|
|
3
|
+
export { type S as SeedCounts, type a as SeedOptions, type b as SeedOverrides, type T as TablePlan, s as seedPlan } from "./packem_shared/plan.d-SaRxbrrI.js";
|
|
4
4
|
/**
|
|
5
|
-
* A typed, schema-aware seed client — the ergonomic DX layer over {@link seedPlan}.
|
|
6
|
-
*
|
|
7
|
-
* Where `seedPlan` generates every table at once, the client lets you author one
|
|
8
|
-
* table at a time, with autocomplete on each table's columns and live foreign-key
|
|
9
|
-
* connection to whatever was seeded earlier this run. Pass your generated
|
|
10
|
-
* `InsertModel` type as the type argument — or let `lunora/_generated/seed.ts`
|
|
11
|
-
* do that for you — for full type inference on each table's columns.
|
|
12
|
-
*
|
|
13
|
-
* The client is deterministic (same `seed` ⇒ same rows and ids) and pure unless a
|
|
14
|
-
* `persist` hook is supplied, in which case each generated batch is also written
|
|
15
|
-
* through it (the test harness, an admin import, …). Because every row carries an
|
|
16
|
-
* explicit `_id`, the returned ids are known whether or not anything is persisted.
|
|
17
|
-
*/
|
|
5
|
+
* A typed, schema-aware seed client — the ergonomic DX layer over {@link seedPlan}.
|
|
6
|
+
*
|
|
7
|
+
* Where `seedPlan` generates every table at once, the client lets you author one
|
|
8
|
+
* table at a time, with autocomplete on each table's columns and live foreign-key
|
|
9
|
+
* connection to whatever was seeded earlier this run. Pass your generated
|
|
10
|
+
* `InsertModel` type as the type argument — or let `lunora/_generated/seed.ts`
|
|
11
|
+
* do that for you — for full type inference on each table's columns.
|
|
12
|
+
*
|
|
13
|
+
* The client is deterministic (same `seed` ⇒ same rows and ids) and pure unless a
|
|
14
|
+
* `persist` hook is supplied, in which case each generated batch is also written
|
|
15
|
+
* through it (the test harness, an admin import, …). Because every row carries an
|
|
16
|
+
* explicit `_id`, the returned ids are known whether or not anything is persisted.
|
|
17
|
+
*/
|
|
18
18
|
/** Picks a concrete count: a fixed number, or a deterministic value within `[min, max]`. */
|
|
19
19
|
type CountHelper = (countOrRange: number | readonly [number, number]) => number;
|
|
20
20
|
/** The per-call population spec: a count, an inclusive range, explicit partial rows, or a count callback. */
|
|
@@ -23,10 +23,10 @@ type SeedSpec<Row> = number | ReadonlyArray<Partial<Row>> | readonly [number, nu
|
|
|
23
23
|
type FieldOverride<Value> = ((context: OverrideContext) => Value) | Value;
|
|
24
24
|
/** Options for a single table call. `overrides` win over any explicit partial rows. */
|
|
25
25
|
interface SeedCallOptions<Row> {
|
|
26
|
-
overrides?: { [K in keyof Row]?: FieldOverride<Row[K]
|
|
26
|
+
overrides?: { [K in keyof Row]?: FieldOverride<Row[K]>; };
|
|
27
27
|
}
|
|
28
28
|
/** The id columns the client emits, keyed by table — the result of a single table call. */
|
|
29
|
-
type SeedCallResult<InsertModel, Table extends keyof InsertModel> = { [K in Table]: string[] };
|
|
29
|
+
type SeedCallResult<InsertModel, Table extends keyof InsertModel> = { [K in Table]: string[]; };
|
|
30
30
|
/** The seeder function exposed for one table. */
|
|
31
31
|
type TableSeeder<InsertModel, Table extends keyof InsertModel> = (spec?: SeedSpec<InsertModel[Table]>, options?: SeedCallOptions<InsertModel[Table]>) => Promise<SeedCallResult<InsertModel, Table>>;
|
|
32
32
|
/** The `$`-prefixed run state shared by every client. */
|
|
@@ -39,7 +39,7 @@ interface SeedClientState {
|
|
|
39
39
|
readonly $store: Readonly<Record<string, ReadonlyArray<Record<string, unknown>>>>;
|
|
40
40
|
}
|
|
41
41
|
/** The per-table seeder methods plus the `$`-prefixed run state. */
|
|
42
|
-
type SeedClient<InsertModel> = SeedClientState & { [Table in keyof InsertModel]: TableSeeder<InsertModel, Table
|
|
42
|
+
type SeedClient<InsertModel> = SeedClientState & { [Table in keyof InsertModel]: TableSeeder<InsertModel, Table>; };
|
|
43
43
|
/** Options for {@link createSeedClient}. */
|
|
44
44
|
interface SeedClientOptions {
|
|
45
45
|
/** Default row count when a table call passes no spec (default `10`). */
|
|
@@ -50,15 +50,15 @@ interface SeedClientOptions {
|
|
|
50
50
|
seed?: number;
|
|
51
51
|
}
|
|
52
52
|
/**
|
|
53
|
-
* Create a typed seed client for `schema`. Each table is a method; call it with a
|
|
54
|
-
* count, a range, or explicit partial rows, and its foreign keys connect to rows
|
|
55
|
-
* seeded earlier this run. State accumulates on `$store`/`$ids` and clears with
|
|
56
|
-
* `$reset()`.
|
|
57
|
-
* @example
|
|
58
|
-
* const seed = createSeedClient<InsertModel>(schema, { seed: 1 });
|
|
59
|
-
* const { users } = await seed.users(5);
|
|
60
|
-
* const { posts } = await seed.posts((x) => x([10, 20]));
|
|
61
|
-
* // posts.authorId values are drawn from `users`.
|
|
62
|
-
*/
|
|
53
|
+
* Create a typed seed client for `schema`. Each table is a method; call it with a
|
|
54
|
+
* count, a range, or explicit partial rows, and its foreign keys connect to rows
|
|
55
|
+
* seeded earlier this run. State accumulates on `$store`/`$ids` and clears with
|
|
56
|
+
* `$reset()`.
|
|
57
|
+
* @example
|
|
58
|
+
* const seed = createSeedClient<InsertModel>(schema, { seed: 1 });
|
|
59
|
+
* const { users } = await seed.users(5);
|
|
60
|
+
* const { posts } = await seed.posts((x) => x([10, 20]));
|
|
61
|
+
* // posts.authorId values are drawn from `users`.
|
|
62
|
+
*/
|
|
63
63
|
declare const createSeedClient: <InsertModel = Record<string, Record<string, unknown>>>(schema: Schema, options?: SeedClientOptions) => SeedClient<InsertModel>;
|
|
64
64
|
export { type CountHelper, type OverrideContext, type SeedCallOptions, type SeedCallResult, type SeedClient, type SeedClientOptions, type SeedSpec, createSeedClient };
|
package/dist/index.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export { createSeedClient } from './packem_shared/createSeedClient-
|
|
2
|
-
export { s as seedPlan } from './packem_shared/plan-
|
|
1
|
+
export { createSeedClient } from './packem_shared/createSeedClient-CZuGqXLm.mjs';
|
|
2
|
+
export { s as seedPlan } from './packem_shared/plan-DybYaa3F.mjs';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as setHashKey, s as seedPlan, c as copycat } from './plan-
|
|
1
|
+
import { a as setHashKey, s as seedPlan, c as copycat } from './plan-DybYaa3F.mjs';
|
|
2
2
|
|
|
3
3
|
const isRange = (spec) => Array.isArray(spec) && spec.length === 2 && typeof spec[0] === "number" && typeof spec[1] === "number";
|
|
4
4
|
const resolveSpec = (spec, table, seed, defaultCount) => {
|
|
@@ -35,7 +35,7 @@ const createSeedClient = (schema, options = {}) => {
|
|
|
35
35
|
const store = {};
|
|
36
36
|
const idsByTable = {};
|
|
37
37
|
const createdCount = {};
|
|
38
|
-
const
|
|
38
|
+
const runSeedTable = async (table, spec, callOptions) => {
|
|
39
39
|
setHashKey(seed);
|
|
40
40
|
const { count, partials } = resolveSpec(spec, table, seed, defaultCount);
|
|
41
41
|
const offset = createdCount[table] ?? 0;
|
|
@@ -64,6 +64,15 @@ const createSeedClient = (schema, options = {}) => {
|
|
|
64
64
|
}
|
|
65
65
|
return { [table]: created };
|
|
66
66
|
};
|
|
67
|
+
let queue = Promise.resolve();
|
|
68
|
+
const seedTable = (table, spec, callOptions) => {
|
|
69
|
+
const result = queue.then(() => runSeedTable(table, spec, callOptions));
|
|
70
|
+
queue = result.then(
|
|
71
|
+
() => void 0,
|
|
72
|
+
() => void 0
|
|
73
|
+
);
|
|
74
|
+
return result;
|
|
75
|
+
};
|
|
67
76
|
const state = {
|
|
68
77
|
$ids: idsByTable,
|
|
69
78
|
$reset: () => {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { LunoraError } from '@lunora/errors';
|
|
1
2
|
import { faker } from '@faker-js/faker';
|
|
2
3
|
import { optionalInner } from '@lunora/values';
|
|
3
4
|
|
|
@@ -288,7 +289,7 @@ const orderTables = (specs, selected) => {
|
|
|
288
289
|
}
|
|
289
290
|
return ordered;
|
|
290
291
|
};
|
|
291
|
-
const fkParentClosure = (specs, roots) => {
|
|
292
|
+
const fkParentClosure = (specs, roots, stopAt = /* @__PURE__ */ new Set()) => {
|
|
292
293
|
const byName = new Map(specs.map((spec) => [spec.name, spec]));
|
|
293
294
|
const result = new Set(roots);
|
|
294
295
|
const stack = [...result];
|
|
@@ -301,6 +302,9 @@ const fkParentClosure = (specs, roots) => {
|
|
|
301
302
|
if (spec === void 0) {
|
|
302
303
|
continue;
|
|
303
304
|
}
|
|
305
|
+
if (stopAt.has(name)) {
|
|
306
|
+
continue;
|
|
307
|
+
}
|
|
304
308
|
for (const field of spec.fields) {
|
|
305
309
|
if (field.fkTable !== void 0 && field.fkTable !== name && byName.has(field.fkTable) && !result.has(field.fkTable)) {
|
|
306
310
|
result.add(field.fkTable);
|
|
@@ -334,7 +338,8 @@ const generateString = (fieldName, input, constraints) => {
|
|
|
334
338
|
const value = rule === void 0 ? copycat.word(input) : rule.generate(input);
|
|
335
339
|
const { maxLength, minLength } = constraints;
|
|
336
340
|
if (maxLength !== void 0 && minLength !== void 0 && minLength > maxLength) {
|
|
337
|
-
throw new
|
|
341
|
+
throw new LunoraError(
|
|
342
|
+
"INTERNAL",
|
|
338
343
|
`Seed constraint error for field "${fieldName}": minLength (${String(minLength)}) > maxLength (${String(maxLength)}). Adjust the schema constraints.`
|
|
339
344
|
);
|
|
340
345
|
}
|
|
@@ -384,16 +389,28 @@ const generateValue = (validator, fieldName, input) => {
|
|
|
384
389
|
return null;
|
|
385
390
|
}
|
|
386
391
|
case "number": {
|
|
387
|
-
|
|
392
|
+
const { maximum, minimum } = constraints;
|
|
393
|
+
if (maximum !== void 0 && minimum !== void 0 && minimum > maximum) {
|
|
394
|
+
throw new LunoraError(
|
|
395
|
+
"INTERNAL",
|
|
396
|
+
`Seed constraint error for field "${fieldName}": minimum (${String(minimum)}) > maximum (${String(maximum)}). Adjust the schema constraints.`
|
|
397
|
+
);
|
|
398
|
+
}
|
|
399
|
+
const min = minimum ?? 0;
|
|
400
|
+
const max = maximum ?? 1e3;
|
|
401
|
+
if (!Number.isInteger(min) || !Number.isInteger(max)) {
|
|
402
|
+
return copycat.float(input, { max, min });
|
|
403
|
+
}
|
|
404
|
+
return copycat.int(input, { max, min });
|
|
388
405
|
}
|
|
389
406
|
case "object": {
|
|
390
407
|
const shape = metaOf(inner).shape ?? {};
|
|
391
408
|
return Object.fromEntries(Object.entries(shape).map(([key, child]) => [key, generateValue(child, key, [input, key])]));
|
|
392
409
|
}
|
|
393
410
|
case "record": {
|
|
394
|
-
const { valueValidator } = metaOf(inner);
|
|
411
|
+
const { keyValidator, valueValidator } = metaOf(inner);
|
|
395
412
|
const entries = copycat.times(input, [1, 3], (itemInput) => {
|
|
396
|
-
const key = copycat.word(["k", itemInput]);
|
|
413
|
+
const key = keyValidator === void 0 ? copycat.word(["k", itemInput]) : String(generateValue(keyValidator, fieldName, ["k", itemInput]));
|
|
397
414
|
const value = valueValidator === void 0 ? copycat.word(["v", itemInput]) : generateValue(valueValidator, fieldName, ["v", itemInput]);
|
|
398
415
|
return [key, value];
|
|
399
416
|
});
|
|
@@ -447,8 +464,20 @@ const seedPlan = (schema, options = {}) => {
|
|
|
447
464
|
const { counts = {}, defaultCount = 10, existingIds = {}, indexOffset = {}, only, overrides = {}, seed = 0 } = options;
|
|
448
465
|
setHashKey(seed);
|
|
449
466
|
const specs = introspectSchema(schema);
|
|
467
|
+
if (only !== void 0) {
|
|
468
|
+
const known = new Set(specs.map((spec) => spec.name));
|
|
469
|
+
for (const name of only) {
|
|
470
|
+
if (!known.has(name)) {
|
|
471
|
+
const available = specs.map((spec) => spec.name).join(", ");
|
|
472
|
+
throw new LunoraError("BAD_REQUEST", `unknown table "${name}" in seed \`only\` — schema defines: ${available || "(no tables)"}`);
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
}
|
|
450
476
|
const requested = new Set(only ?? specs.map((spec) => spec.name));
|
|
451
|
-
const
|
|
477
|
+
const covered = new Set(Object.keys(existingIds).filter((table) => !requested.has(table) && (existingIds[table] ?? []).length > 0));
|
|
478
|
+
const selected = new Set(
|
|
479
|
+
[...fkParentClosure(specs, requested, covered)].filter((table) => requested.has(table) || (existingIds[table] ?? []).length === 0)
|
|
480
|
+
);
|
|
452
481
|
const order = orderTables(specs, selected);
|
|
453
482
|
const specByName = new Map(specs.map((spec) => [spec.name, spec]));
|
|
454
483
|
const idsByTable = /* @__PURE__ */ new Map();
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { Schema } from '@lunora/server';
|
|
2
|
+
/**
|
|
3
|
+
* The pure, I/O-free core of seeding. {@link seedPlan} introspects a schema,
|
|
4
|
+
* generates rows table-by-table in foreign-key order, and resolves every
|
|
5
|
+
* `v.id("parent")` column to a real id of an already-generated parent row. The
|
|
6
|
+
* result feeds every adapter (test harness, CLI, studio) — none of them
|
|
7
|
+
* re-implement generation.
|
|
8
|
+
*
|
|
9
|
+
* Determinism: a `seed` value selects the global copycat mapping, and each value
|
|
10
|
+
* is hashed from `[seed, table, index, field]`, so the same `(schema, options)`
|
|
11
|
+
* always yields byte-identical rows.
|
|
12
|
+
*/
|
|
13
|
+
/** A row context handed to an override function. */
|
|
14
|
+
interface OverrideContext {
|
|
15
|
+
field: string;
|
|
16
|
+
/** The row's absolute index (the `indexOffset` base plus its position in this batch). */
|
|
17
|
+
index: number;
|
|
18
|
+
/** The row built so far (system `_id` first, then earlier fields). */
|
|
19
|
+
row: Record<string, unknown>;
|
|
20
|
+
/**
|
|
21
|
+
* A live, read-only view of every table's rows generated so far this run,
|
|
22
|
+
* keyed by table name. Lets an override correlate across tables (e.g. copy a
|
|
23
|
+
* field from the parent row a foreign key points at). Rows for the current
|
|
24
|
+
* table accumulate as they are built, so only earlier rows are visible.
|
|
25
|
+
*/
|
|
26
|
+
store: Readonly<Record<string, ReadonlyArray<Record<string, unknown>>>>;
|
|
27
|
+
table: string;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Per-table, per-field overrides. Each override is a static value or a function
|
|
31
|
+
* of the row context; field `_id` overrides the generated primary key.
|
|
32
|
+
*/
|
|
33
|
+
type SeedOverrides = Record<string, Record<string, unknown>>;
|
|
34
|
+
/** Per-table row counts. */
|
|
35
|
+
type SeedCounts = Record<string, number>;
|
|
36
|
+
interface SeedOptions {
|
|
37
|
+
/** Rows per table; falls back to {@link SeedOptions.defaultCount} when a table is absent. */
|
|
38
|
+
counts?: SeedCounts;
|
|
39
|
+
/** Count used for any selected table not present in `counts` (default `10`). */
|
|
40
|
+
defaultCount?: number;
|
|
41
|
+
/**
|
|
42
|
+
* Ids of rows that already exist in the target store, keyed by table. Foreign
|
|
43
|
+
* keys may resolve to these in addition to freshly-seeded parents, and a
|
|
44
|
+
* parent table fully covered here is not re-seeded when it is only pulled in
|
|
45
|
+
* as an FK dependency (it is when named explicitly in `only`).
|
|
46
|
+
*/
|
|
47
|
+
existingIds?: Readonly<Record<string, ReadonlyArray<string>>>;
|
|
48
|
+
/**
|
|
49
|
+
* Per-table absolute index base for generation. Defaults to `0`. A client
|
|
50
|
+
* seeding the same table across several calls passes the running total so
|
|
51
|
+
* each batch hashes from fresh indices and never collides ids with an
|
|
52
|
+
* earlier batch.
|
|
53
|
+
*/
|
|
54
|
+
indexOffset?: Readonly<Record<string, number>>;
|
|
55
|
+
/**
|
|
56
|
+
* Restrict seeding to these tables. Transitive `v.id(...)` parents are added
|
|
57
|
+
* automatically (unless already covered by `existingIds`) so child foreign
|
|
58
|
+
* keys resolve to real rows. The result is still ordered by FK dependency.
|
|
59
|
+
* Default: all tables.
|
|
60
|
+
*/
|
|
61
|
+
only?: ReadonlyArray<string>;
|
|
62
|
+
/** Static values or functions overriding generated columns. */
|
|
63
|
+
overrides?: SeedOverrides;
|
|
64
|
+
/** Deterministic mapping selector — same seed ⇒ same rows. Default `0`. */
|
|
65
|
+
seed?: number;
|
|
66
|
+
}
|
|
67
|
+
/** One table's generated rows, in insert order. Each row carries an explicit `_id`. */
|
|
68
|
+
interface TablePlan {
|
|
69
|
+
rows: ReadonlyArray<Record<string, unknown>>;
|
|
70
|
+
table: string;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Build a deterministic, FK-consistent set of rows for `schema`.
|
|
74
|
+
* @returns one {@link TablePlan} per seeded table, ordered so a table's FK
|
|
75
|
+
* parents come before it.
|
|
76
|
+
*/
|
|
77
|
+
declare const seedPlan: (schema: Schema, options?: SeedOptions) => ReadonlyArray<TablePlan>;
|
|
78
|
+
export { OverrideContext as O, SeedCounts as S, TablePlan as T, SeedOptions as a, SeedOverrides as b, seedPlan as s };
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { Schema } from '@lunora/server';
|
|
2
|
+
/**
|
|
3
|
+
* The pure, I/O-free core of seeding. {@link seedPlan} introspects a schema,
|
|
4
|
+
* generates rows table-by-table in foreign-key order, and resolves every
|
|
5
|
+
* `v.id("parent")` column to a real id of an already-generated parent row. The
|
|
6
|
+
* result feeds every adapter (test harness, CLI, studio) — none of them
|
|
7
|
+
* re-implement generation.
|
|
8
|
+
*
|
|
9
|
+
* Determinism: a `seed` value selects the global copycat mapping, and each value
|
|
10
|
+
* is hashed from `[seed, table, index, field]`, so the same `(schema, options)`
|
|
11
|
+
* always yields byte-identical rows.
|
|
12
|
+
*/
|
|
13
|
+
/** A row context handed to an override function. */
|
|
14
|
+
interface OverrideContext {
|
|
15
|
+
field: string;
|
|
16
|
+
/** The row's absolute index (the `indexOffset` base plus its position in this batch). */
|
|
17
|
+
index: number;
|
|
18
|
+
/** The row built so far (system `_id` first, then earlier fields). */
|
|
19
|
+
row: Record<string, unknown>;
|
|
20
|
+
/**
|
|
21
|
+
* A live, read-only view of every table's rows generated so far this run,
|
|
22
|
+
* keyed by table name. Lets an override correlate across tables (e.g. copy a
|
|
23
|
+
* field from the parent row a foreign key points at). Rows for the current
|
|
24
|
+
* table accumulate as they are built, so only earlier rows are visible.
|
|
25
|
+
*/
|
|
26
|
+
store: Readonly<Record<string, ReadonlyArray<Record<string, unknown>>>>;
|
|
27
|
+
table: string;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Per-table, per-field overrides. Each override is a static value or a function
|
|
31
|
+
* of the row context; field `_id` overrides the generated primary key.
|
|
32
|
+
*/
|
|
33
|
+
type SeedOverrides = Record<string, Record<string, unknown>>;
|
|
34
|
+
/** Per-table row counts. */
|
|
35
|
+
type SeedCounts = Record<string, number>;
|
|
36
|
+
interface SeedOptions {
|
|
37
|
+
/** Rows per table; falls back to {@link SeedOptions.defaultCount} when a table is absent. */
|
|
38
|
+
counts?: SeedCounts;
|
|
39
|
+
/** Count used for any selected table not present in `counts` (default `10`). */
|
|
40
|
+
defaultCount?: number;
|
|
41
|
+
/**
|
|
42
|
+
* Ids of rows that already exist in the target store, keyed by table. Foreign
|
|
43
|
+
* keys may resolve to these in addition to freshly-seeded parents, and a
|
|
44
|
+
* parent table fully covered here is not re-seeded when it is only pulled in
|
|
45
|
+
* as an FK dependency (it is when named explicitly in `only`).
|
|
46
|
+
*/
|
|
47
|
+
existingIds?: Readonly<Record<string, ReadonlyArray<string>>>;
|
|
48
|
+
/**
|
|
49
|
+
* Per-table absolute index base for generation. Defaults to `0`. A client
|
|
50
|
+
* seeding the same table across several calls passes the running total so
|
|
51
|
+
* each batch hashes from fresh indices and never collides ids with an
|
|
52
|
+
* earlier batch.
|
|
53
|
+
*/
|
|
54
|
+
indexOffset?: Readonly<Record<string, number>>;
|
|
55
|
+
/**
|
|
56
|
+
* Restrict seeding to these tables. Transitive `v.id(...)` parents are added
|
|
57
|
+
* automatically (unless already covered by `existingIds`) so child foreign
|
|
58
|
+
* keys resolve to real rows. The result is still ordered by FK dependency.
|
|
59
|
+
* Default: all tables.
|
|
60
|
+
*/
|
|
61
|
+
only?: ReadonlyArray<string>;
|
|
62
|
+
/** Static values or functions overriding generated columns. */
|
|
63
|
+
overrides?: SeedOverrides;
|
|
64
|
+
/** Deterministic mapping selector — same seed ⇒ same rows. Default `0`. */
|
|
65
|
+
seed?: number;
|
|
66
|
+
}
|
|
67
|
+
/** One table's generated rows, in insert order. Each row carries an explicit `_id`. */
|
|
68
|
+
interface TablePlan {
|
|
69
|
+
rows: ReadonlyArray<Record<string, unknown>>;
|
|
70
|
+
table: string;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Build a deterministic, FK-consistent set of rows for `schema`.
|
|
74
|
+
* @returns one {@link TablePlan} per seeded table, ordered so a table's FK
|
|
75
|
+
* parents come before it.
|
|
76
|
+
*/
|
|
77
|
+
declare const seedPlan: (schema: Schema, options?: SeedOptions) => ReadonlyArray<TablePlan>;
|
|
78
|
+
export { OverrideContext as O, SeedCounts as S, TablePlan as T, SeedOptions as a, SeedOverrides as b, seedPlan as s };
|
package/dist/testing.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Schema } from '@lunora/server';
|
|
2
2
|
import { TestHarness } from '@lunora/testing';
|
|
3
|
-
import { a as SeedOptions } from "./packem_shared/plan.d-
|
|
3
|
+
import { a as SeedOptions } from "./packem_shared/plan.d-SaRxbrrI.mjs";
|
|
4
4
|
declare const seed: (harness: TestHarness, schema: Schema, options?: SeedOptions) => Promise<Record<string, string[]>>;
|
|
5
5
|
export { seed };
|
package/dist/testing.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Schema } from '@lunora/server';
|
|
2
2
|
import { TestHarness } from '@lunora/testing';
|
|
3
|
-
import { a as SeedOptions } from "./packem_shared/plan.d-
|
|
3
|
+
import { a as SeedOptions } from "./packem_shared/plan.d-SaRxbrrI.js";
|
|
4
4
|
declare const seed: (harness: TestHarness, schema: Schema, options?: SeedOptions) => Promise<Record<string, string[]>>;
|
|
5
5
|
export { seed };
|
package/dist/testing.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { s as seedPlan, i as introspectSchema } from './packem_shared/plan-
|
|
1
|
+
import { s as seedPlan, i as introspectSchema } from './packem_shared/plan-DybYaa3F.mjs';
|
|
2
2
|
|
|
3
3
|
const reviveRow = (row, bigintFields, bytesFields) => {
|
|
4
4
|
if (bigintFields.size === 0 && bytesFields.size === 0) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lunora/seed",
|
|
3
|
-
"version": "1.0.0-alpha.
|
|
3
|
+
"version": "1.0.0-alpha.30",
|
|
4
4
|
"description": "Schema-driven, deterministic database seeding for Lunora: realistic fake data from defineSchema",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"cloudflare",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"directory": "packages/seed"
|
|
23
23
|
},
|
|
24
24
|
"files": [
|
|
25
|
-
"dist",
|
|
25
|
+
"./dist",
|
|
26
26
|
"README.md",
|
|
27
27
|
"LICENSE.md",
|
|
28
28
|
"__assets__"
|
|
@@ -47,11 +47,12 @@
|
|
|
47
47
|
"access": "public"
|
|
48
48
|
},
|
|
49
49
|
"dependencies": {
|
|
50
|
-
"@faker-js/faker": "^10.5.0"
|
|
50
|
+
"@faker-js/faker": "^10.5.0",
|
|
51
|
+
"@lunora/errors": "1.0.0-alpha.8"
|
|
51
52
|
},
|
|
52
53
|
"peerDependencies": {
|
|
53
|
-
"@lunora/server": "1.0.0-alpha.
|
|
54
|
-
"@lunora/values": "1.0.0-alpha.2"
|
|
54
|
+
"@lunora/server": ">=1.0.0-alpha.24 <2.0.0-0",
|
|
55
|
+
"@lunora/values": ">=1.0.0-alpha.7 <2.0.0-0"
|
|
55
56
|
},
|
|
56
57
|
"engines": {
|
|
57
58
|
"node": "^22.15.0 || >=24.11.0"
|
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
import { Schema } from '@lunora/server';
|
|
2
|
-
/**
|
|
3
|
-
* The pure, I/O-free core of seeding. {@link seedPlan} introspects a schema,
|
|
4
|
-
* generates rows table-by-table in foreign-key order, and resolves every
|
|
5
|
-
* `v.id("parent")` column to a real id of an already-generated parent row. The
|
|
6
|
-
* result feeds every adapter (test harness, CLI, studio) — none of them
|
|
7
|
-
* re-implement generation.
|
|
8
|
-
*
|
|
9
|
-
* Determinism: a `seed` value selects the global copycat mapping, and each value
|
|
10
|
-
* is hashed from `[seed, table, index, field]`, so the same `(schema, options)`
|
|
11
|
-
* always yields byte-identical rows.
|
|
12
|
-
*/
|
|
13
|
-
/** A row context handed to an override function. */
|
|
14
|
-
interface OverrideContext {
|
|
15
|
-
field: string;
|
|
16
|
-
/** The row's absolute index (the `indexOffset` base plus its position in this batch). */
|
|
17
|
-
index: number;
|
|
18
|
-
/** The row built so far (system `_id` first, then earlier fields). */
|
|
19
|
-
row: Record<string, unknown>;
|
|
20
|
-
/**
|
|
21
|
-
* A live, read-only view of every table's rows generated so far this run,
|
|
22
|
-
* keyed by table name. Lets an override correlate across tables (e.g. copy a
|
|
23
|
-
* field from the parent row a foreign key points at). Rows for the current
|
|
24
|
-
* table accumulate as they are built, so only earlier rows are visible.
|
|
25
|
-
*/
|
|
26
|
-
store: Readonly<Record<string, ReadonlyArray<Record<string, unknown>>>>;
|
|
27
|
-
table: string;
|
|
28
|
-
}
|
|
29
|
-
/**
|
|
30
|
-
* Per-table, per-field overrides. Each override is a static value or a function
|
|
31
|
-
* of the row context; field `_id` overrides the generated primary key.
|
|
32
|
-
*/
|
|
33
|
-
type SeedOverrides = Record<string, Record<string, unknown>>;
|
|
34
|
-
/** Per-table row counts. */
|
|
35
|
-
type SeedCounts = Record<string, number>;
|
|
36
|
-
interface SeedOptions {
|
|
37
|
-
/** Rows per table; falls back to {@link SeedOptions.defaultCount} when a table is absent. */
|
|
38
|
-
counts?: SeedCounts;
|
|
39
|
-
/** Count used for any selected table not present in `counts` (default `10`). */
|
|
40
|
-
defaultCount?: number;
|
|
41
|
-
/**
|
|
42
|
-
* Ids of rows that already exist in the target store, keyed by table. Foreign
|
|
43
|
-
* keys may resolve to these in addition to freshly-seeded parents, and a
|
|
44
|
-
* parent table fully covered here is not re-seeded when it is only pulled in
|
|
45
|
-
* as an FK dependency (it is when named explicitly in `only`).
|
|
46
|
-
*/
|
|
47
|
-
existingIds?: Readonly<Record<string, ReadonlyArray<string>>>;
|
|
48
|
-
/**
|
|
49
|
-
* Per-table absolute index base for generation. Defaults to `0`. A client
|
|
50
|
-
* seeding the same table across several calls passes the running total so
|
|
51
|
-
* each batch hashes from fresh indices and never collides ids with an
|
|
52
|
-
* earlier batch.
|
|
53
|
-
*/
|
|
54
|
-
indexOffset?: Readonly<Record<string, number>>;
|
|
55
|
-
/**
|
|
56
|
-
* Restrict seeding to these tables. Transitive `v.id(...)` parents are added
|
|
57
|
-
* automatically (unless already covered by `existingIds`) so child foreign
|
|
58
|
-
* keys resolve to real rows. The result is still ordered by FK dependency.
|
|
59
|
-
* Default: all tables.
|
|
60
|
-
*/
|
|
61
|
-
only?: ReadonlyArray<string>;
|
|
62
|
-
/** Static values or functions overriding generated columns. */
|
|
63
|
-
overrides?: SeedOverrides;
|
|
64
|
-
/** Deterministic mapping selector — same seed ⇒ same rows. Default `0`. */
|
|
65
|
-
seed?: number;
|
|
66
|
-
}
|
|
67
|
-
/** One table's generated rows, in insert order. Each row carries an explicit `_id`. */
|
|
68
|
-
interface TablePlan {
|
|
69
|
-
rows: ReadonlyArray<Record<string, unknown>>;
|
|
70
|
-
table: string;
|
|
71
|
-
}
|
|
72
|
-
/**
|
|
73
|
-
* Build a deterministic, FK-consistent set of rows for `schema`.
|
|
74
|
-
* @returns one {@link TablePlan} per seeded table, ordered so a table's FK
|
|
75
|
-
* parents come before it.
|
|
76
|
-
*/
|
|
77
|
-
declare const seedPlan: (schema: Schema, options?: SeedOptions) => ReadonlyArray<TablePlan>;
|
|
78
|
-
export { OverrideContext as O, SeedCounts as S, TablePlan as T, SeedOptions as a, SeedOverrides as b, seedPlan as s };
|
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
import { Schema } from '@lunora/server';
|
|
2
|
-
/**
|
|
3
|
-
* The pure, I/O-free core of seeding. {@link seedPlan} introspects a schema,
|
|
4
|
-
* generates rows table-by-table in foreign-key order, and resolves every
|
|
5
|
-
* `v.id("parent")` column to a real id of an already-generated parent row. The
|
|
6
|
-
* result feeds every adapter (test harness, CLI, studio) — none of them
|
|
7
|
-
* re-implement generation.
|
|
8
|
-
*
|
|
9
|
-
* Determinism: a `seed` value selects the global copycat mapping, and each value
|
|
10
|
-
* is hashed from `[seed, table, index, field]`, so the same `(schema, options)`
|
|
11
|
-
* always yields byte-identical rows.
|
|
12
|
-
*/
|
|
13
|
-
/** A row context handed to an override function. */
|
|
14
|
-
interface OverrideContext {
|
|
15
|
-
field: string;
|
|
16
|
-
/** The row's absolute index (the `indexOffset` base plus its position in this batch). */
|
|
17
|
-
index: number;
|
|
18
|
-
/** The row built so far (system `_id` first, then earlier fields). */
|
|
19
|
-
row: Record<string, unknown>;
|
|
20
|
-
/**
|
|
21
|
-
* A live, read-only view of every table's rows generated so far this run,
|
|
22
|
-
* keyed by table name. Lets an override correlate across tables (e.g. copy a
|
|
23
|
-
* field from the parent row a foreign key points at). Rows for the current
|
|
24
|
-
* table accumulate as they are built, so only earlier rows are visible.
|
|
25
|
-
*/
|
|
26
|
-
store: Readonly<Record<string, ReadonlyArray<Record<string, unknown>>>>;
|
|
27
|
-
table: string;
|
|
28
|
-
}
|
|
29
|
-
/**
|
|
30
|
-
* Per-table, per-field overrides. Each override is a static value or a function
|
|
31
|
-
* of the row context; field `_id` overrides the generated primary key.
|
|
32
|
-
*/
|
|
33
|
-
type SeedOverrides = Record<string, Record<string, unknown>>;
|
|
34
|
-
/** Per-table row counts. */
|
|
35
|
-
type SeedCounts = Record<string, number>;
|
|
36
|
-
interface SeedOptions {
|
|
37
|
-
/** Rows per table; falls back to {@link SeedOptions.defaultCount} when a table is absent. */
|
|
38
|
-
counts?: SeedCounts;
|
|
39
|
-
/** Count used for any selected table not present in `counts` (default `10`). */
|
|
40
|
-
defaultCount?: number;
|
|
41
|
-
/**
|
|
42
|
-
* Ids of rows that already exist in the target store, keyed by table. Foreign
|
|
43
|
-
* keys may resolve to these in addition to freshly-seeded parents, and a
|
|
44
|
-
* parent table fully covered here is not re-seeded when it is only pulled in
|
|
45
|
-
* as an FK dependency (it is when named explicitly in `only`).
|
|
46
|
-
*/
|
|
47
|
-
existingIds?: Readonly<Record<string, ReadonlyArray<string>>>;
|
|
48
|
-
/**
|
|
49
|
-
* Per-table absolute index base for generation. Defaults to `0`. A client
|
|
50
|
-
* seeding the same table across several calls passes the running total so
|
|
51
|
-
* each batch hashes from fresh indices and never collides ids with an
|
|
52
|
-
* earlier batch.
|
|
53
|
-
*/
|
|
54
|
-
indexOffset?: Readonly<Record<string, number>>;
|
|
55
|
-
/**
|
|
56
|
-
* Restrict seeding to these tables. Transitive `v.id(...)` parents are added
|
|
57
|
-
* automatically (unless already covered by `existingIds`) so child foreign
|
|
58
|
-
* keys resolve to real rows. The result is still ordered by FK dependency.
|
|
59
|
-
* Default: all tables.
|
|
60
|
-
*/
|
|
61
|
-
only?: ReadonlyArray<string>;
|
|
62
|
-
/** Static values or functions overriding generated columns. */
|
|
63
|
-
overrides?: SeedOverrides;
|
|
64
|
-
/** Deterministic mapping selector — same seed ⇒ same rows. Default `0`. */
|
|
65
|
-
seed?: number;
|
|
66
|
-
}
|
|
67
|
-
/** One table's generated rows, in insert order. Each row carries an explicit `_id`. */
|
|
68
|
-
interface TablePlan {
|
|
69
|
-
rows: ReadonlyArray<Record<string, unknown>>;
|
|
70
|
-
table: string;
|
|
71
|
-
}
|
|
72
|
-
/**
|
|
73
|
-
* Build a deterministic, FK-consistent set of rows for `schema`.
|
|
74
|
-
* @returns one {@link TablePlan} per seeded table, ordered so a table's FK
|
|
75
|
-
* parents come before it.
|
|
76
|
-
*/
|
|
77
|
-
declare const seedPlan: (schema: Schema, options?: SeedOptions) => ReadonlyArray<TablePlan>;
|
|
78
|
-
export { OverrideContext as O, SeedCounts as S, TablePlan as T, SeedOptions as a, SeedOverrides as b, seedPlan as s };
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { s as seedPlan } from './plan-DirmkctQ.mjs';
|