@rljson/edge 0.0.0 → 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.de.md +181 -0
- package/README.md +165 -1
- package/dist/README.de.md +181 -0
- package/dist/README.md +165 -1
- package/dist/car-world/bom-builder.d.ts +57 -0
- package/dist/car-world/brands.d.ts +17 -0
- package/dist/car-world/car-world.d.ts +135 -0
- package/dist/car-world/catalog-generator.d.ts +99 -0
- package/dist/car-world/generate-car-world.d.ts +16 -0
- package/dist/car-world/plan.d.ts +130 -0
- package/dist/car-world/prices.d.ts +32 -0
- package/dist/car-world/scene-builder.d.ts +40 -0
- package/dist/car-world/table-cfgs.d.ts +22 -0
- package/dist/car-world/workshops.d.ts +39 -0
- package/dist/config/config.d.ts +177 -0
- package/dist/config/presets.d.ts +13 -0
- package/dist/core/emitter.d.ts +71 -0
- package/dist/core/estimate.d.ts +33 -0
- package/dist/core/pick.d.ts +69 -0
- package/dist/core/sink.d.ts +49 -0
- package/dist/dictionaries/cad.d.ts +13 -0
- package/dist/dictionaries/manufacturers.d.ts +21 -0
- package/dist/dictionaries/parts.d.ts +29 -0
- package/dist/dictionaries/people.d.ts +4 -0
- package/dist/dictionaries/places.d.ts +27 -0
- package/dist/dictionaries/vehicles.d.ts +22 -0
- package/dist/edge.d.ts +59 -1
- package/dist/edge.js +11322 -3
- package/dist/edge.js.map +1 -1
- package/dist/edge_version.d.ts +2 -0
- package/dist/example.d.ts +7 -1
- package/dist/index.d.ts +23 -1
- package/dist/src/example.ts +57 -15
- package/package.json +4 -4
package/README.de.md
ADDED
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
@license
|
|
3
|
+
Copyright (c) 2025 Rljson
|
|
4
|
+
|
|
5
|
+
Use of this source code is governed by terms that can be
|
|
6
|
+
found in the LICENSE file in the root of this package.
|
|
7
|
+
-->
|
|
8
|
+
|
|
9
|
+
# @rljson/edge - Der Rljson-Beispieldatengenerator
|
|
10
|
+
|
|
11
|
+
Edge erzeugt Beispieldaten für Rljson-Anwendungen: eine Autowelt mit
|
|
12
|
+
Herstellern, ihren Fahrzeugkatalogen und für jedes Fahrzeug einem Preis,
|
|
13
|
+
einer Marke, einer Werkstatt, einer Stückliste und einer CAD-Szene. Eine
|
|
14
|
+
erzeugte Welt nutzt jeden Rljson-Datentyp, von hundert Zeilen für einen
|
|
15
|
+
Unit-Test bis zu 25 Millionen Zeilen für einen Lasttest.
|
|
16
|
+
|
|
17
|
+
## Ziele
|
|
18
|
+
|
|
19
|
+
- Dem Uikit, `db`, den `io`-Backends und den Validatoren realistische
|
|
20
|
+
Testdaten in jedem Rljson-Datentyp liefern
|
|
21
|
+
- Bei jedem Lauf und auf jedem Rechner dieselben Zeilen und Hashes erzeugen,
|
|
22
|
+
ohne Zufallszahlen
|
|
23
|
+
- Vom Fixture eines Unit-Tests bis zum Lasttest mit 20 Millionen Objekten
|
|
24
|
+
skalieren
|
|
25
|
+
- In Node.js, im Browser und in Web Workern laufen, ohne Abhängigkeit zu
|
|
26
|
+
Dateisystem oder Datenbank
|
|
27
|
+
- Anschauliche Daten liefern: echte Marken und Modelle, plausible Teile,
|
|
28
|
+
Werkstätten mit Adresse und Inhaber
|
|
29
|
+
|
|
30
|
+
## Stand
|
|
31
|
+
|
|
32
|
+
[](https://github.com/rljson/edge/actions/workflows/quick_check.yaml)
|
|
33
|
+
|
|
34
|
+
## Installation
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
pnpm add @rljson/edge @rljson/rljson
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Dokumentation
|
|
41
|
+
|
|
42
|
+
- [Beispieldatengenerator](https://github.com/rljson/rljson-pm/blob/main/doc/2026-Q4/concepts/topics/example-data-generator.md):
|
|
43
|
+
Entwurf, Größen und Ideen, im Projektmanagement-Repo
|
|
44
|
+
- [Entscheidungen edge-001 bis edge-004](https://github.com/rljson/rljson-pm/blob/main/doc/2026-Q4/concepts/decisions/000-index.md):
|
|
45
|
+
keine Buffets, kein Zufall, Callback-Sinks, abgeleitete Modelljahre
|
|
46
|
+
- Die Sektion »Generate Rljson« auf [rljson.github.io](https://rljson.github.io):
|
|
47
|
+
Schritt-für-Schritt-Tutorials
|
|
48
|
+
- [Guides](doc/guides): wie in diesem Repo entwickelt, getestet und
|
|
49
|
+
reviewt wird
|
|
50
|
+
|
|
51
|
+
## Code-Beispiele
|
|
52
|
+
|
|
53
|
+
[src/example.ts](src/example.ts) führt diese Beispiele in den Tests aus.
|
|
54
|
+
|
|
55
|
+
Eine Welt im Speicher erzeugen:
|
|
56
|
+
|
|
57
|
+
```ts
|
|
58
|
+
import { Edge } from '@rljson/edge';
|
|
59
|
+
|
|
60
|
+
// The tiny preset: one manufacturer with one catalog of five cars
|
|
61
|
+
const { world, stats } = await Edge.preset('tiny').generate();
|
|
62
|
+
|
|
63
|
+
console.log(`${stats.cars} cars, ${stats.rowsTotal} rows`);
|
|
64
|
+
console.log(world.manufacturers._data[0].name);
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Eine Welt konfigurieren und vorher ihre Größe schätzen:
|
|
68
|
+
|
|
69
|
+
```ts
|
|
70
|
+
import { Edge } from '@rljson/edge';
|
|
71
|
+
|
|
72
|
+
const edge = new Edge({
|
|
73
|
+
manufacturers: { count: 2, catalogsPerManufacturer: 2 },
|
|
74
|
+
catalogs: { carsPerCatalog: 20, revisions: { count: 1 } },
|
|
75
|
+
layers: {
|
|
76
|
+
prices: { currencies: ['EUR', 'CHF'], range: { min: 20000, max: 80000 } },
|
|
77
|
+
brands: { modelsPerManufacturer: 3, popularity: 'zipf' },
|
|
78
|
+
workshops: { perCatalog: 4 },
|
|
79
|
+
parts: { depth: 3, fanOut: 2 },
|
|
80
|
+
cad: { depth: 2, fanOut: 3 },
|
|
81
|
+
},
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
console.log(`estimated rows: ${edge.estimate().rowsTotal}`);
|
|
85
|
+
const { stats } = await edge.generate();
|
|
86
|
+
console.log(`generated rows: ${stats.rowsTotal}`);
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Die Zeilen einer großen Welt über einen Callback weiterreichen, statt sie
|
|
90
|
+
zu behalten:
|
|
91
|
+
|
|
92
|
+
```ts
|
|
93
|
+
import { Edge } from '@rljson/edge';
|
|
94
|
+
|
|
95
|
+
const rowsPerTable: Record<string, number> = {};
|
|
96
|
+
await Edge.preset('small').run({
|
|
97
|
+
onRow: (table) => {
|
|
98
|
+
rowsPerTable[table] = (rowsPerTable[table] ?? 0) + 1;
|
|
99
|
+
},
|
|
100
|
+
});
|
|
101
|
+
console.log(`prices: ${rowsPerTable.prices}, parts: ${rowsPerTable.parts}`);
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## Funktionsweise
|
|
105
|
+
|
|
106
|
+
### Die Welt
|
|
107
|
+
|
|
108
|
+
```text
|
|
109
|
+
Manufacturers components manufacturers (der Einstieg)
|
|
110
|
+
└─ Manufacturer
|
|
111
|
+
├─ Headquarters components addresses
|
|
112
|
+
└─ Catalogs jsonArray manufacturers.catalogsRef
|
|
113
|
+
└─ Catalog cakes catalogs (Slices sind Fahrzeuge)
|
|
114
|
+
├─ Slice ids sliceIds carIds
|
|
115
|
+
├─ Prices layers carPrices → components prices
|
|
116
|
+
├─ Brands layers carBrands → components brands
|
|
117
|
+
├─ Workshops layers carWorkshops → components workshops
|
|
118
|
+
│ ├─ addressRef → addresses
|
|
119
|
+
│ └─ ownerRef → persons
|
|
120
|
+
├─ Parts layers carParts → components parts
|
|
121
|
+
│ └─ subPartRefs → parts
|
|
122
|
+
├─ CAD layers carCad → trees cadScenes
|
|
123
|
+
│ └─ meta.partRef → parts
|
|
124
|
+
└─ Revisions revisions revisions (verknüpfen die Modelljahre)
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
- Jedes Modelljahr eines Katalogs ist ein eigener Cake. Seine Slice-Ids und
|
|
128
|
+
Layer bauen mit `base`, `add` und `remove` auf dem Vorjahr auf, und eine
|
|
129
|
+
Revision verknüpft die beiden Cakes.
|
|
130
|
+
- Eine Stückliste reicht bis zu vier Ebenen unter das Fahrzeug. Die Ebenen
|
|
131
|
+
1 bis 3 gehören zu einer Variante, Ebene 4 ist ein Pool von Normteilen,
|
|
132
|
+
den alle Stücklisten teilen. Eine CAD-Szene ist ein Baum aus Gruppen und
|
|
133
|
+
Meshes, und ein Mesh verweist auf ein Teil.
|
|
134
|
+
- Listen sind Hash-Arrays in einer `jsonArray`-Spalte, die die
|
|
135
|
+
Tabellenkonfiguration als Referenz deklariert. Jede Tabelle hat eine
|
|
136
|
+
`TableCfg`, so prüft `BaseValidator` aus `@rljson/rljson` Typen und
|
|
137
|
+
Referenzen.
|
|
138
|
+
|
|
139
|
+
### Kein Zufall
|
|
140
|
+
|
|
141
|
+
Jeder Wert leitet sich aus einem Index in die Wörterbücher ab: 166
|
|
142
|
+
Hersteller, 917 Modelle, neun Baugruppen mit je 36 Teilen, 40 Normteile,
|
|
143
|
+
126 Städte, 100 Straßen, 185 Vornamen und 180 Nachnamen. Ein Anteil wie
|
|
144
|
+
`discountShare` wählt jedes k-te Fahrzeug. Dieselbe Konfiguration ergibt
|
|
145
|
+
dieselben Hashes.
|
|
146
|
+
|
|
147
|
+
### Zeile für Zeile
|
|
148
|
+
|
|
149
|
+
Zeilen verweisen per Hash auf Zeilen, deshalb gibt Edge eine Zeile erst
|
|
150
|
+
nach den Zeilen aus, auf die sie verweist. Edge hasht jede Zeile, verwirft
|
|
151
|
+
eine Zeile, deren Hash schon ausgegeben wurde, und reicht die übrigen
|
|
152
|
+
einzeln und abgewartet an einen Sink weiter. `generate()` sammelt sie in
|
|
153
|
+
einem `EMemorySink`; `run(sink)` reicht sie an `onRow` und behält
|
|
154
|
+
nichts, nachdem `onTable` jede Tabelle mit ihrer Konfiguration angekündigt
|
|
155
|
+
hat.
|
|
156
|
+
|
|
157
|
+
Stücklisten und Szenen werden pro Fahrzeug, pro Modell oder pro Katalog
|
|
158
|
+
geteilt. `estimate()` zählt die Zeilen jeder Tabelle allein aus der
|
|
159
|
+
Konfiguration, und `scale.targetRows` wählt damit die Fahrzeuge pro
|
|
160
|
+
Katalog.
|
|
161
|
+
|
|
162
|
+
### Öffentliche API
|
|
163
|
+
|
|
164
|
+
| Export | Zweck |
|
|
165
|
+
| ---------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
166
|
+
| `Edge` | `new Edge(config)`, `preset()`, `estimate()`, `generate()`, `run()` |
|
|
167
|
+
| `mergeConfigs` | Führt zwei Konfigurationen abschnittsweise zusammen |
|
|
168
|
+
| `edgeDefaults`, `resolveEdgeConfig` | Die Standardwerte und eine geprüfte Konfiguration mit Standardwerten |
|
|
169
|
+
| `edgePresets`, `edgePresetNames` | Die Presets `tiny`, `small`, `medium`, `large` und `xl` |
|
|
170
|
+
| `ESink`, `EMemorySink` | Die Callback-Schnittstelle von `run()` und der Sink von `generate()` |
|
|
171
|
+
| `carWorldTableKeys`, `carWorldTableCfgs` | Die Tabellenschlüssel und ihre Tabellenkonfigurationen |
|
|
172
|
+
| `ECarWorld` und seine Zeilentypen | `EManufacturer`, `EAddress`, `EPerson`, `EWorkshop`, `EPrice`, `EBrand`, `EPart`, `ECadMeta` |
|
|
173
|
+
| `EConfig` und seine Abschnitte | `ELayersConfig`, `EPricesConfig`, `EBrandsConfig`, `EWorkshopsConfig`, `EPartsConfig`, `ECadConfig`, `ERevisionsConfig`, `EResolvedConfig` |
|
|
174
|
+
| `EStats`, `EEstimate`, `EProgress` | Die Ergebnisse und der Fortschritt eines Laufs |
|
|
175
|
+
| Wörterbücher | `manufacturers`, `vehicles`, `modelsOf`, `assemblies`, `standardParts`, `materialsOf`, `cadSystems`, `cadDetails`, `cadMaterials`, `cities`, `citiesIn`, `cityNamed`, `streets`, `firstNames`, `lastNames` |
|
|
176
|
+
|
|
177
|
+
## Mitwirken
|
|
178
|
+
|
|
179
|
+
Gearbeitet wird Ticket für Ticket mit `gg`, wie es der
|
|
180
|
+
[Develop Guide](doc/guides/develop-guide.md) beschreibt. Reviews folgen dem
|
|
181
|
+
[Review Guide](doc/guides/for-ai/ai-review-guide.md).
|
package/README.md
CHANGED
|
@@ -8,4 +8,168 @@ found in the LICENSE file in the root of this package.
|
|
|
8
8
|
|
|
9
9
|
# @rljson/edge - The Rljson Example Data Generator
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
Edge generates example data for Rljson applications: a car world with
|
|
12
|
+
manufacturers, their catalogs of cars, and for every car a price, a brand,
|
|
13
|
+
a workshop, a bill of materials and a CAD scene. One generated world uses
|
|
14
|
+
every Rljson data type, from a hundred rows for a unit test to 25 million
|
|
15
|
+
rows for a load test.
|
|
16
|
+
|
|
17
|
+
## Goals
|
|
18
|
+
|
|
19
|
+
- Give the Uikit, `db`, the `io` backends and the validators realistic test
|
|
20
|
+
data in every Rljson data type
|
|
21
|
+
- Generate the same rows and hashes on every run and every machine, without
|
|
22
|
+
random numbers
|
|
23
|
+
- Scale from a unit test fixture to a load test with 20 million objects
|
|
24
|
+
- Run in Node.js, in the browser and in Web Workers, without file system or
|
|
25
|
+
database dependencies
|
|
26
|
+
- Keep the data vivid: real brands and models, plausible parts, workshops
|
|
27
|
+
with addresses and owners
|
|
28
|
+
|
|
29
|
+
## State
|
|
30
|
+
|
|
31
|
+
[](https://github.com/rljson/edge/actions/workflows/quick_check.yaml)
|
|
32
|
+
|
|
33
|
+
## Installation
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
pnpm add @rljson/edge @rljson/rljson
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Documentation
|
|
40
|
+
|
|
41
|
+
- [Example data generator](https://github.com/rljson/rljson-pm/blob/main/doc/2026-Q4/concepts/topics/example-data-generator.md):
|
|
42
|
+
design, sizes and ideas, in the project management repo
|
|
43
|
+
- [Decisions edge-001 to edge-004](https://github.com/rljson/rljson-pm/blob/main/doc/2026-Q4/concepts/decisions/000-index.md):
|
|
44
|
+
no buffets, no randomness, callback sinks, derived model years
|
|
45
|
+
- The section »Generate Rljson« on [rljson.github.io](https://rljson.github.io):
|
|
46
|
+
step-by-step tutorials
|
|
47
|
+
- [Guides](doc/guides): how to develop, test and review in this repo
|
|
48
|
+
|
|
49
|
+
## Code Examples
|
|
50
|
+
|
|
51
|
+
[src/example.ts](src/example.ts) runs these examples in the tests.
|
|
52
|
+
|
|
53
|
+
Generate a world in memory:
|
|
54
|
+
|
|
55
|
+
```ts
|
|
56
|
+
import { Edge } from '@rljson/edge';
|
|
57
|
+
|
|
58
|
+
// The tiny preset: one manufacturer with one catalog of five cars
|
|
59
|
+
const { world, stats } = await Edge.preset('tiny').generate();
|
|
60
|
+
|
|
61
|
+
console.log(`${stats.cars} cars, ${stats.rowsTotal} rows`);
|
|
62
|
+
console.log(world.manufacturers._data[0].name);
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Configure a world and estimate its size first:
|
|
66
|
+
|
|
67
|
+
```ts
|
|
68
|
+
import { Edge } from '@rljson/edge';
|
|
69
|
+
|
|
70
|
+
const edge = new Edge({
|
|
71
|
+
manufacturers: { count: 2, catalogsPerManufacturer: 2 },
|
|
72
|
+
catalogs: { carsPerCatalog: 20, revisions: { count: 1 } },
|
|
73
|
+
layers: {
|
|
74
|
+
prices: { currencies: ['EUR', 'CHF'], range: { min: 20000, max: 80000 } },
|
|
75
|
+
brands: { modelsPerManufacturer: 3, popularity: 'zipf' },
|
|
76
|
+
workshops: { perCatalog: 4 },
|
|
77
|
+
parts: { depth: 3, fanOut: 2 },
|
|
78
|
+
cad: { depth: 2, fanOut: 3 },
|
|
79
|
+
},
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
console.log(`estimated rows: ${edge.estimate().rowsTotal}`);
|
|
83
|
+
const { stats } = await edge.generate();
|
|
84
|
+
console.log(`generated rows: ${stats.rowsTotal}`);
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Stream the rows of a large world through a callback instead of keeping
|
|
88
|
+
them:
|
|
89
|
+
|
|
90
|
+
```ts
|
|
91
|
+
import { Edge } from '@rljson/edge';
|
|
92
|
+
|
|
93
|
+
const rowsPerTable: Record<string, number> = {};
|
|
94
|
+
await Edge.preset('small').run({
|
|
95
|
+
onRow: (table) => {
|
|
96
|
+
rowsPerTable[table] = (rowsPerTable[table] ?? 0) + 1;
|
|
97
|
+
},
|
|
98
|
+
});
|
|
99
|
+
console.log(`prices: ${rowsPerTable.prices}, parts: ${rowsPerTable.parts}`);
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## How It Works
|
|
103
|
+
|
|
104
|
+
### The world
|
|
105
|
+
|
|
106
|
+
```text
|
|
107
|
+
Manufacturers components manufacturers (the entry point)
|
|
108
|
+
└─ Manufacturer
|
|
109
|
+
├─ Headquarters components addresses
|
|
110
|
+
└─ Catalogs jsonArray manufacturers.catalogsRef
|
|
111
|
+
└─ Catalog cakes catalogs (slices are cars)
|
|
112
|
+
├─ Slice ids sliceIds carIds
|
|
113
|
+
├─ Prices layers carPrices → components prices
|
|
114
|
+
├─ Brands layers carBrands → components brands
|
|
115
|
+
├─ Workshops layers carWorkshops → components workshops
|
|
116
|
+
│ ├─ addressRef → addresses
|
|
117
|
+
│ └─ ownerRef → persons
|
|
118
|
+
├─ Parts layers carParts → components parts
|
|
119
|
+
│ └─ subPartRefs → parts
|
|
120
|
+
├─ CAD layers carCad → trees cadScenes
|
|
121
|
+
│ └─ meta.partRef → parts
|
|
122
|
+
└─ Revisions revisions revisions (link the model years)
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
- Every model year of a catalog is a cake of its own. Its slice ids and
|
|
126
|
+
layers build on the year before with `base`, `add` and `remove`, and a
|
|
127
|
+
revision links the two cakes.
|
|
128
|
+
- A bill of materials reaches up to four levels below the car. Levels 1 to
|
|
129
|
+
3 belong to a variant, level 4 is a pool of standard parts every bill
|
|
130
|
+
shares. A CAD scene is a tree of groups and meshes, and a mesh refers to
|
|
131
|
+
a part.
|
|
132
|
+
- Lists are arrays of hashes in a `jsonArray` column that the table
|
|
133
|
+
configuration declares as a reference. Every table has a `TableCfg`, so
|
|
134
|
+
`BaseValidator` of `@rljson/rljson` checks types and references.
|
|
135
|
+
|
|
136
|
+
### No randomness
|
|
137
|
+
|
|
138
|
+
Every value derives from an index into the dictionaries: 166
|
|
139
|
+
manufacturers, 917 models, nine assemblies with 36 parts each, 40
|
|
140
|
+
standard parts, 126 cities, 100 streets, 185 first names and 180 last
|
|
141
|
+
names. A share like `discountShare` selects every k-th car. The same
|
|
142
|
+
configuration yields the same hashes.
|
|
143
|
+
|
|
144
|
+
### Row by row
|
|
145
|
+
|
|
146
|
+
Rows refer to rows by hash, so Edge emits a row after the rows it refers
|
|
147
|
+
to. It hashes every row, drops a row whose hash it emitted before, and
|
|
148
|
+
hands the rest to a sink, one at a time and awaited. `generate()` collects
|
|
149
|
+
them in an `EMemorySink`; `run(sink)` hands them to `onRow` and keeps
|
|
150
|
+
nothing, after `onTable` announced each table with its configuration.
|
|
151
|
+
|
|
152
|
+
Bills of materials and scenes are shared per car, per model or per
|
|
153
|
+
catalog. `estimate()` counts the rows of every table from the
|
|
154
|
+
configuration alone, and `scale.targetRows` uses it to pick the cars per
|
|
155
|
+
catalog.
|
|
156
|
+
|
|
157
|
+
### Public API
|
|
158
|
+
|
|
159
|
+
| Export | Purpose |
|
|
160
|
+
| ---------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
161
|
+
| `Edge` | `new Edge(config)`, `preset()`, `estimate()`, `generate()`, `run()` |
|
|
162
|
+
| `mergeConfigs` | Merges two configurations section by section |
|
|
163
|
+
| `edgeDefaults`, `resolveEdgeConfig` | The defaults, and a configuration with defaults and checks |
|
|
164
|
+
| `edgePresets`, `edgePresetNames` | The presets `tiny`, `small`, `medium`, `large` and `xl` |
|
|
165
|
+
| `ESink`, `EMemorySink` | The callback interface of `run()` and the sink of `generate()` |
|
|
166
|
+
| `carWorldTableKeys`, `carWorldTableCfgs` | The table keys and their table configurations |
|
|
167
|
+
| `ECarWorld` and its row types | `EManufacturer`, `EAddress`, `EPerson`, `EWorkshop`, `EPrice`, `EBrand`, `EPart`, `ECadMeta` |
|
|
168
|
+
| `EConfig` and its sections | `ELayersConfig`, `EPricesConfig`, `EBrandsConfig`, `EWorkshopsConfig`, `EPartsConfig`, `ECadConfig`, `ERevisionsConfig`, `EResolvedConfig` |
|
|
169
|
+
| `EStats`, `EEstimate`, `EProgress` | The results and the progress of a run |
|
|
170
|
+
| Dictionaries | `manufacturers`, `vehicles`, `modelsOf`, `assemblies`, `standardParts`, `materialsOf`, `cadSystems`, `cadDetails`, `cadMaterials`, `cities`, `citiesIn`, `cityNamed`, `streets`, `firstNames`, `lastNames` |
|
|
171
|
+
|
|
172
|
+
## Contributing
|
|
173
|
+
|
|
174
|
+
Work ticket by ticket with `gg`, as the [Develop Guide](doc/guides/develop-guide.md)
|
|
175
|
+
describes. Reviews follow the [Review Guide](doc/guides/for-ai/ai-review-guide.md).
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
@license
|
|
3
|
+
Copyright (c) 2025 Rljson
|
|
4
|
+
|
|
5
|
+
Use of this source code is governed by terms that can be
|
|
6
|
+
found in the LICENSE file in the root of this package.
|
|
7
|
+
-->
|
|
8
|
+
|
|
9
|
+
# @rljson/edge - Der Rljson-Beispieldatengenerator
|
|
10
|
+
|
|
11
|
+
Edge erzeugt Beispieldaten für Rljson-Anwendungen: eine Autowelt mit
|
|
12
|
+
Herstellern, ihren Fahrzeugkatalogen und für jedes Fahrzeug einem Preis,
|
|
13
|
+
einer Marke, einer Werkstatt, einer Stückliste und einer CAD-Szene. Eine
|
|
14
|
+
erzeugte Welt nutzt jeden Rljson-Datentyp, von hundert Zeilen für einen
|
|
15
|
+
Unit-Test bis zu 25 Millionen Zeilen für einen Lasttest.
|
|
16
|
+
|
|
17
|
+
## Ziele
|
|
18
|
+
|
|
19
|
+
- Dem Uikit, `db`, den `io`-Backends und den Validatoren realistische
|
|
20
|
+
Testdaten in jedem Rljson-Datentyp liefern
|
|
21
|
+
- Bei jedem Lauf und auf jedem Rechner dieselben Zeilen und Hashes erzeugen,
|
|
22
|
+
ohne Zufallszahlen
|
|
23
|
+
- Vom Fixture eines Unit-Tests bis zum Lasttest mit 20 Millionen Objekten
|
|
24
|
+
skalieren
|
|
25
|
+
- In Node.js, im Browser und in Web Workern laufen, ohne Abhängigkeit zu
|
|
26
|
+
Dateisystem oder Datenbank
|
|
27
|
+
- Anschauliche Daten liefern: echte Marken und Modelle, plausible Teile,
|
|
28
|
+
Werkstätten mit Adresse und Inhaber
|
|
29
|
+
|
|
30
|
+
## Stand
|
|
31
|
+
|
|
32
|
+
[](https://github.com/rljson/edge/actions/workflows/quick_check.yaml)
|
|
33
|
+
|
|
34
|
+
## Installation
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
pnpm add @rljson/edge @rljson/rljson
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Dokumentation
|
|
41
|
+
|
|
42
|
+
- [Beispieldatengenerator](https://github.com/rljson/rljson-pm/blob/main/doc/2026-Q4/concepts/topics/example-data-generator.md):
|
|
43
|
+
Entwurf, Größen und Ideen, im Projektmanagement-Repo
|
|
44
|
+
- [Entscheidungen edge-001 bis edge-004](https://github.com/rljson/rljson-pm/blob/main/doc/2026-Q4/concepts/decisions/000-index.md):
|
|
45
|
+
keine Buffets, kein Zufall, Callback-Sinks, abgeleitete Modelljahre
|
|
46
|
+
- Die Sektion »Generate Rljson« auf [rljson.github.io](https://rljson.github.io):
|
|
47
|
+
Schritt-für-Schritt-Tutorials
|
|
48
|
+
- [Guides](doc/guides): wie in diesem Repo entwickelt, getestet und
|
|
49
|
+
reviewt wird
|
|
50
|
+
|
|
51
|
+
## Code-Beispiele
|
|
52
|
+
|
|
53
|
+
[src/example.ts](src/example.ts) führt diese Beispiele in den Tests aus.
|
|
54
|
+
|
|
55
|
+
Eine Welt im Speicher erzeugen:
|
|
56
|
+
|
|
57
|
+
```ts
|
|
58
|
+
import { Edge } from '@rljson/edge';
|
|
59
|
+
|
|
60
|
+
// The tiny preset: one manufacturer with one catalog of five cars
|
|
61
|
+
const { world, stats } = await Edge.preset('tiny').generate();
|
|
62
|
+
|
|
63
|
+
console.log(`${stats.cars} cars, ${stats.rowsTotal} rows`);
|
|
64
|
+
console.log(world.manufacturers._data[0].name);
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Eine Welt konfigurieren und vorher ihre Größe schätzen:
|
|
68
|
+
|
|
69
|
+
```ts
|
|
70
|
+
import { Edge } from '@rljson/edge';
|
|
71
|
+
|
|
72
|
+
const edge = new Edge({
|
|
73
|
+
manufacturers: { count: 2, catalogsPerManufacturer: 2 },
|
|
74
|
+
catalogs: { carsPerCatalog: 20, revisions: { count: 1 } },
|
|
75
|
+
layers: {
|
|
76
|
+
prices: { currencies: ['EUR', 'CHF'], range: { min: 20000, max: 80000 } },
|
|
77
|
+
brands: { modelsPerManufacturer: 3, popularity: 'zipf' },
|
|
78
|
+
workshops: { perCatalog: 4 },
|
|
79
|
+
parts: { depth: 3, fanOut: 2 },
|
|
80
|
+
cad: { depth: 2, fanOut: 3 },
|
|
81
|
+
},
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
console.log(`estimated rows: ${edge.estimate().rowsTotal}`);
|
|
85
|
+
const { stats } = await edge.generate();
|
|
86
|
+
console.log(`generated rows: ${stats.rowsTotal}`);
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Die Zeilen einer großen Welt über einen Callback weiterreichen, statt sie
|
|
90
|
+
zu behalten:
|
|
91
|
+
|
|
92
|
+
```ts
|
|
93
|
+
import { Edge } from '@rljson/edge';
|
|
94
|
+
|
|
95
|
+
const rowsPerTable: Record<string, number> = {};
|
|
96
|
+
await Edge.preset('small').run({
|
|
97
|
+
onRow: (table) => {
|
|
98
|
+
rowsPerTable[table] = (rowsPerTable[table] ?? 0) + 1;
|
|
99
|
+
},
|
|
100
|
+
});
|
|
101
|
+
console.log(`prices: ${rowsPerTable.prices}, parts: ${rowsPerTable.parts}`);
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## Funktionsweise
|
|
105
|
+
|
|
106
|
+
### Die Welt
|
|
107
|
+
|
|
108
|
+
```text
|
|
109
|
+
Manufacturers components manufacturers (der Einstieg)
|
|
110
|
+
└─ Manufacturer
|
|
111
|
+
├─ Headquarters components addresses
|
|
112
|
+
└─ Catalogs jsonArray manufacturers.catalogsRef
|
|
113
|
+
└─ Catalog cakes catalogs (Slices sind Fahrzeuge)
|
|
114
|
+
├─ Slice ids sliceIds carIds
|
|
115
|
+
├─ Prices layers carPrices → components prices
|
|
116
|
+
├─ Brands layers carBrands → components brands
|
|
117
|
+
├─ Workshops layers carWorkshops → components workshops
|
|
118
|
+
│ ├─ addressRef → addresses
|
|
119
|
+
│ └─ ownerRef → persons
|
|
120
|
+
├─ Parts layers carParts → components parts
|
|
121
|
+
│ └─ subPartRefs → parts
|
|
122
|
+
├─ CAD layers carCad → trees cadScenes
|
|
123
|
+
│ └─ meta.partRef → parts
|
|
124
|
+
└─ Revisions revisions revisions (verknüpfen die Modelljahre)
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
- Jedes Modelljahr eines Katalogs ist ein eigener Cake. Seine Slice-Ids und
|
|
128
|
+
Layer bauen mit `base`, `add` und `remove` auf dem Vorjahr auf, und eine
|
|
129
|
+
Revision verknüpft die beiden Cakes.
|
|
130
|
+
- Eine Stückliste reicht bis zu vier Ebenen unter das Fahrzeug. Die Ebenen
|
|
131
|
+
1 bis 3 gehören zu einer Variante, Ebene 4 ist ein Pool von Normteilen,
|
|
132
|
+
den alle Stücklisten teilen. Eine CAD-Szene ist ein Baum aus Gruppen und
|
|
133
|
+
Meshes, und ein Mesh verweist auf ein Teil.
|
|
134
|
+
- Listen sind Hash-Arrays in einer `jsonArray`-Spalte, die die
|
|
135
|
+
Tabellenkonfiguration als Referenz deklariert. Jede Tabelle hat eine
|
|
136
|
+
`TableCfg`, so prüft `BaseValidator` aus `@rljson/rljson` Typen und
|
|
137
|
+
Referenzen.
|
|
138
|
+
|
|
139
|
+
### Kein Zufall
|
|
140
|
+
|
|
141
|
+
Jeder Wert leitet sich aus einem Index in die Wörterbücher ab: 166
|
|
142
|
+
Hersteller, 917 Modelle, neun Baugruppen mit je 36 Teilen, 40 Normteile,
|
|
143
|
+
126 Städte, 100 Straßen, 185 Vornamen und 180 Nachnamen. Ein Anteil wie
|
|
144
|
+
`discountShare` wählt jedes k-te Fahrzeug. Dieselbe Konfiguration ergibt
|
|
145
|
+
dieselben Hashes.
|
|
146
|
+
|
|
147
|
+
### Zeile für Zeile
|
|
148
|
+
|
|
149
|
+
Zeilen verweisen per Hash auf Zeilen, deshalb gibt Edge eine Zeile erst
|
|
150
|
+
nach den Zeilen aus, auf die sie verweist. Edge hasht jede Zeile, verwirft
|
|
151
|
+
eine Zeile, deren Hash schon ausgegeben wurde, und reicht die übrigen
|
|
152
|
+
einzeln und abgewartet an einen Sink weiter. `generate()` sammelt sie in
|
|
153
|
+
einem `EMemorySink`; `run(sink)` reicht sie an `onRow` und behält
|
|
154
|
+
nichts, nachdem `onTable` jede Tabelle mit ihrer Konfiguration angekündigt
|
|
155
|
+
hat.
|
|
156
|
+
|
|
157
|
+
Stücklisten und Szenen werden pro Fahrzeug, pro Modell oder pro Katalog
|
|
158
|
+
geteilt. `estimate()` zählt die Zeilen jeder Tabelle allein aus der
|
|
159
|
+
Konfiguration, und `scale.targetRows` wählt damit die Fahrzeuge pro
|
|
160
|
+
Katalog.
|
|
161
|
+
|
|
162
|
+
### Öffentliche API
|
|
163
|
+
|
|
164
|
+
| Export | Zweck |
|
|
165
|
+
| ---------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
166
|
+
| `Edge` | `new Edge(config)`, `preset()`, `estimate()`, `generate()`, `run()` |
|
|
167
|
+
| `mergeConfigs` | Führt zwei Konfigurationen abschnittsweise zusammen |
|
|
168
|
+
| `edgeDefaults`, `resolveEdgeConfig` | Die Standardwerte und eine geprüfte Konfiguration mit Standardwerten |
|
|
169
|
+
| `edgePresets`, `edgePresetNames` | Die Presets `tiny`, `small`, `medium`, `large` und `xl` |
|
|
170
|
+
| `ESink`, `EMemorySink` | Die Callback-Schnittstelle von `run()` und der Sink von `generate()` |
|
|
171
|
+
| `carWorldTableKeys`, `carWorldTableCfgs` | Die Tabellenschlüssel und ihre Tabellenkonfigurationen |
|
|
172
|
+
| `ECarWorld` und seine Zeilentypen | `EManufacturer`, `EAddress`, `EPerson`, `EWorkshop`, `EPrice`, `EBrand`, `EPart`, `ECadMeta` |
|
|
173
|
+
| `EConfig` und seine Abschnitte | `ELayersConfig`, `EPricesConfig`, `EBrandsConfig`, `EWorkshopsConfig`, `EPartsConfig`, `ECadConfig`, `ERevisionsConfig`, `EResolvedConfig` |
|
|
174
|
+
| `EStats`, `EEstimate`, `EProgress` | Die Ergebnisse und der Fortschritt eines Laufs |
|
|
175
|
+
| Wörterbücher | `manufacturers`, `vehicles`, `modelsOf`, `assemblies`, `standardParts`, `materialsOf`, `cadSystems`, `cadDetails`, `cadMaterials`, `cities`, `citiesIn`, `cityNamed`, `streets`, `firstNames`, `lastNames` |
|
|
176
|
+
|
|
177
|
+
## Mitwirken
|
|
178
|
+
|
|
179
|
+
Gearbeitet wird Ticket für Ticket mit `gg`, wie es der
|
|
180
|
+
[Develop Guide](doc/guides/develop-guide.md) beschreibt. Reviews folgen dem
|
|
181
|
+
[Review Guide](doc/guides/for-ai/ai-review-guide.md).
|