@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/dist/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,57 @@
|
|
|
1
|
+
import { EPartsConfig } from '../config/config.ts';
|
|
2
|
+
import { EEmitter } from '../core/emitter.ts';
|
|
3
|
+
import { EVariant } from './plan.ts';
|
|
4
|
+
/** A generated bill of materials */
|
|
5
|
+
export interface EBom {
|
|
6
|
+
/** The hash of the bill of materials: the part of level 0 */
|
|
7
|
+
rootRef: string;
|
|
8
|
+
/**
|
|
9
|
+
* The hashes of the parts in the order they were emitted, the root first.
|
|
10
|
+
* A standard part appears once for every part that contains it.
|
|
11
|
+
*/
|
|
12
|
+
partRefs: string[];
|
|
13
|
+
}
|
|
14
|
+
/** What a bill of materials builder needs */
|
|
15
|
+
export interface EBomBuilderOptions {
|
|
16
|
+
/** Receives the rows */
|
|
17
|
+
emitter: EEmitter;
|
|
18
|
+
/** The parts layer configuration */
|
|
19
|
+
config: Required<EPartsConfig>;
|
|
20
|
+
/** The part number prefix of the manufacturer, e.g. "AUD" */
|
|
21
|
+
prefix: string;
|
|
22
|
+
/** Who the bill of materials belongs to */
|
|
23
|
+
variant: EVariant;
|
|
24
|
+
}
|
|
25
|
+
/** The level of the standard parts, a pool every bill of materials shares */
|
|
26
|
+
export declare const standardPartsLevel = 4;
|
|
27
|
+
/**
|
|
28
|
+
* Emits a bill of materials from the leaves up.
|
|
29
|
+
*
|
|
30
|
+
* Part numbers are handed out from the top down, so a part's number is
|
|
31
|
+
* lower than the numbers of its sub parts. Levels 1 to 3 carry the variant
|
|
32
|
+
* in their part numbers, so two variants never share them. Level 4, the
|
|
33
|
+
* standard parts, comes from a pool every bill shares. Build once per
|
|
34
|
+
* instance.
|
|
35
|
+
*/
|
|
36
|
+
export declare class EBomBuilder {
|
|
37
|
+
private readonly options;
|
|
38
|
+
/**
|
|
39
|
+
* Constructor
|
|
40
|
+
* @param options - The emitter, the parts layer and the variant
|
|
41
|
+
*/
|
|
42
|
+
constructor(options: EBomBuilderOptions);
|
|
43
|
+
/** Emits the bill of materials and returns its hashes */
|
|
44
|
+
build(): Promise<EBom>;
|
|
45
|
+
private sequence;
|
|
46
|
+
private readonly partRefs;
|
|
47
|
+
private get code();
|
|
48
|
+
private nextPartNumber;
|
|
49
|
+
private emit;
|
|
50
|
+
private root;
|
|
51
|
+
private assembly;
|
|
52
|
+
private subAssemblies;
|
|
53
|
+
private subAssembly;
|
|
54
|
+
private parts;
|
|
55
|
+
private part;
|
|
56
|
+
private standardParts;
|
|
57
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { EBodyType, EVehicleModel } from '../dictionaries/vehicles.ts';
|
|
2
|
+
import { EBrand } from './car-world.ts';
|
|
3
|
+
/** The body types from the cheapest to the most expensive */
|
|
4
|
+
export declare const bodyTypeRanks: readonly EBodyType[];
|
|
5
|
+
/**
|
|
6
|
+
* Returns the power of a model in kW: the typical power of its body type,
|
|
7
|
+
* plus a bit for every model the manufacturer lists before it.
|
|
8
|
+
* @param model - The model
|
|
9
|
+
* @param modelIndex - The index of the model within the manufacturer
|
|
10
|
+
*/
|
|
11
|
+
export declare const powerKwOf: (model: EVehicleModel, modelIndex: number) => number;
|
|
12
|
+
/**
|
|
13
|
+
* Returns the brand row of a model.
|
|
14
|
+
* @param model - The model
|
|
15
|
+
* @param modelIndex - The index of the model within the manufacturer
|
|
16
|
+
*/
|
|
17
|
+
export declare const brandRow: (model: EVehicleModel, modelIndex: number) => EBrand;
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { Json } from '@rljson/json';
|
|
2
|
+
import { CakesTable, ComponentsTable, LayersTable, Ref, RevisionsTable, Rljson, Row, SliceIdsTable, TablesCfgTable, TreesTable } from '@rljson/rljson';
|
|
3
|
+
import { EBodyType, EFuel } from '../dictionaries/vehicles.ts';
|
|
4
|
+
/** A manufacturer: the entry point of the world */
|
|
5
|
+
export interface EManufacturer extends Row {
|
|
6
|
+
/** The brand in kebab case, e.g. "mercedes-benz" */
|
|
7
|
+
id: string;
|
|
8
|
+
/** The name of the company */
|
|
9
|
+
name: string;
|
|
10
|
+
/** The brand the cars carry */
|
|
11
|
+
brand: string;
|
|
12
|
+
/** The ISO 3166 country code of the headquarters */
|
|
13
|
+
country: string;
|
|
14
|
+
/** The year the brand was founded */
|
|
15
|
+
founded: number;
|
|
16
|
+
/** The website, a made up address */
|
|
17
|
+
website: string;
|
|
18
|
+
/** The address of the headquarters */
|
|
19
|
+
headquartersRef: Ref;
|
|
20
|
+
/** The catalogs of the manufacturer, every model year, oldest first */
|
|
21
|
+
catalogsRef: Ref[];
|
|
22
|
+
}
|
|
23
|
+
/** A postal address with coordinates */
|
|
24
|
+
export interface EAddress extends Row {
|
|
25
|
+
street: string;
|
|
26
|
+
houseNumber: string;
|
|
27
|
+
zip: string;
|
|
28
|
+
city: string;
|
|
29
|
+
country: string;
|
|
30
|
+
lat: number;
|
|
31
|
+
lng: number;
|
|
32
|
+
}
|
|
33
|
+
/** A person, e.g. the owner of a workshop */
|
|
34
|
+
export interface EPerson extends Row {
|
|
35
|
+
firstName: string;
|
|
36
|
+
lastName: string;
|
|
37
|
+
email: string;
|
|
38
|
+
phone: string;
|
|
39
|
+
birthYear: number;
|
|
40
|
+
}
|
|
41
|
+
/** A workshop that services cars */
|
|
42
|
+
export interface EWorkshop extends Row {
|
|
43
|
+
name: string;
|
|
44
|
+
/** The address of the workshop */
|
|
45
|
+
addressRef: Ref;
|
|
46
|
+
/** The person who owns the workshop */
|
|
47
|
+
ownerRef: Ref;
|
|
48
|
+
phone: string;
|
|
49
|
+
email: string;
|
|
50
|
+
/** The services the workshop offers */
|
|
51
|
+
services: string[];
|
|
52
|
+
/** The rating from 3 to 5 */
|
|
53
|
+
rating: number;
|
|
54
|
+
}
|
|
55
|
+
/** The price of a car */
|
|
56
|
+
export interface EPrice extends Row {
|
|
57
|
+
amount: number;
|
|
58
|
+
currency: string;
|
|
59
|
+
/** The first day of the model year, as ISO date */
|
|
60
|
+
validFrom: string;
|
|
61
|
+
/** The discount in percent, 0 for none */
|
|
62
|
+
discountPercent: number;
|
|
63
|
+
taxIncluded: boolean;
|
|
64
|
+
}
|
|
65
|
+
/** The brand and model of a car */
|
|
66
|
+
export interface EBrand extends Row {
|
|
67
|
+
brand: string;
|
|
68
|
+
model: string;
|
|
69
|
+
bodyType: EBodyType;
|
|
70
|
+
fuel: EFuel;
|
|
71
|
+
powerKw: number;
|
|
72
|
+
}
|
|
73
|
+
/** A part of a bill of materials. Level 0 is the bill itself. */
|
|
74
|
+
export interface EPart extends Row {
|
|
75
|
+
name: string;
|
|
76
|
+
/** A part number, unique per manufacturer */
|
|
77
|
+
partNumber: string;
|
|
78
|
+
/** The category, e.g. "engine", or "bom" for the bill of materials */
|
|
79
|
+
category: string;
|
|
80
|
+
/** 0 for the bill of materials, up to 4 for a standard part */
|
|
81
|
+
level: number;
|
|
82
|
+
/** How many of the part the parent contains */
|
|
83
|
+
quantity: number;
|
|
84
|
+
/** The weight of one part in kilograms */
|
|
85
|
+
weightKg: number;
|
|
86
|
+
/** The material, or null for an assembly */
|
|
87
|
+
material: string | null;
|
|
88
|
+
/** The sub parts, empty for a leaf */
|
|
89
|
+
subPartRefs: Ref[];
|
|
90
|
+
}
|
|
91
|
+
/** The meta data of a node of a CAD scene */
|
|
92
|
+
export interface ECadMeta extends Json {
|
|
93
|
+
/** A group has children, a mesh has geometry */
|
|
94
|
+
type: 'group' | 'mesh';
|
|
95
|
+
/** What the scene shows, e.g. the model or the car it belongs to */
|
|
96
|
+
variant: string;
|
|
97
|
+
/** The position, rotation and scale relative to the parent */
|
|
98
|
+
transform: {
|
|
99
|
+
position: number[];
|
|
100
|
+
rotation: number[];
|
|
101
|
+
scale: number[];
|
|
102
|
+
};
|
|
103
|
+
/** The material of a mesh, null for a group */
|
|
104
|
+
material: string | null;
|
|
105
|
+
/** The number of vertices of a mesh, null for a group */
|
|
106
|
+
vertices: number | null;
|
|
107
|
+
/** Width, height and depth in meters */
|
|
108
|
+
boundsM: number[];
|
|
109
|
+
/** The part the mesh shows, or null */
|
|
110
|
+
partRef: Ref | null;
|
|
111
|
+
}
|
|
112
|
+
/** The generated world: an Rljson object with typed tables */
|
|
113
|
+
export interface ECarWorld extends Rljson {
|
|
114
|
+
manufacturers: ComponentsTable<EManufacturer>;
|
|
115
|
+
addresses: ComponentsTable<EAddress>;
|
|
116
|
+
persons: ComponentsTable<EPerson>;
|
|
117
|
+
workshops: ComponentsTable<EWorkshop>;
|
|
118
|
+
prices: ComponentsTable<EPrice>;
|
|
119
|
+
brands: ComponentsTable<EBrand>;
|
|
120
|
+
parts: ComponentsTable<EPart>;
|
|
121
|
+
cadScenes: TreesTable;
|
|
122
|
+
carIds: SliceIdsTable;
|
|
123
|
+
carPrices: LayersTable;
|
|
124
|
+
carBrands: LayersTable;
|
|
125
|
+
carWorkshops: LayersTable;
|
|
126
|
+
carParts: LayersTable;
|
|
127
|
+
carCad: LayersTable;
|
|
128
|
+
catalogs: CakesTable;
|
|
129
|
+
revisions: RevisionsTable;
|
|
130
|
+
tableCfgs: TablesCfgTable;
|
|
131
|
+
}
|
|
132
|
+
/** The keys of the tables of a car world, in the order they are announced */
|
|
133
|
+
export declare const carWorldTableKeys: readonly ["tableCfgs", "addresses", "persons", "workshops", "prices", "brands", "parts", "cadScenes", "carIds", "carPrices", "carBrands", "carWorkshops", "carParts", "carCad", "catalogs", "revisions", "manufacturers"];
|
|
134
|
+
/** The key of a table of a car world */
|
|
135
|
+
export type ECarWorldTableKey = (typeof carWorldTableKeys)[number];
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { EResolvedConfig } from '../config/config.ts';
|
|
2
|
+
import { EEmitter } from '../core/emitter.ts';
|
|
3
|
+
import { EVehicleModel } from '../dictionaries/vehicles.ts';
|
|
4
|
+
import { EBom } from './bom-builder.ts';
|
|
5
|
+
/** What a catalog needs to know about its manufacturer */
|
|
6
|
+
export interface ECatalogManufacturer {
|
|
7
|
+
/** The index of the manufacturer within the world */
|
|
8
|
+
index: number;
|
|
9
|
+
/** The id of the manufacturer, e.g. "audi" */
|
|
10
|
+
id: string;
|
|
11
|
+
/** The part number prefix, e.g. "AUD" */
|
|
12
|
+
prefix: string;
|
|
13
|
+
/** The ISO 3166 country code */
|
|
14
|
+
country: string;
|
|
15
|
+
/** The models the manufacturer offers */
|
|
16
|
+
models: readonly EVehicleModel[];
|
|
17
|
+
}
|
|
18
|
+
/** Bills of materials and scenes shared by the catalogs of a manufacturer */
|
|
19
|
+
export interface ECatalogCaches {
|
|
20
|
+
boms: Map<string, EBom>;
|
|
21
|
+
scenes: Map<string, string>;
|
|
22
|
+
}
|
|
23
|
+
/** What generating a catalog yields */
|
|
24
|
+
export interface ECatalogResult {
|
|
25
|
+
/** The hashes of the cakes, one per model year, oldest first */
|
|
26
|
+
cakeRefs: string[];
|
|
27
|
+
/** How many different cars the catalog lists over all years */
|
|
28
|
+
cars: number;
|
|
29
|
+
}
|
|
30
|
+
/** What a catalog generator needs */
|
|
31
|
+
export interface ECatalogGeneratorOptions {
|
|
32
|
+
/** Receives the rows */
|
|
33
|
+
emitter: EEmitter;
|
|
34
|
+
/** The resolved configuration */
|
|
35
|
+
config: EResolvedConfig;
|
|
36
|
+
/** The manufacturer of the catalog */
|
|
37
|
+
manufacturer: ECatalogManufacturer;
|
|
38
|
+
/** The index of the catalog within the manufacturer */
|
|
39
|
+
catalogIndex: number;
|
|
40
|
+
/** Bills of materials and scenes shared within the manufacturer */
|
|
41
|
+
caches: ECatalogCaches;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Emits one catalog with all its model years: components, slice ids, layers,
|
|
45
|
+
* cakes and revisions.
|
|
46
|
+
*
|
|
47
|
+
* Every model year is a cake of its own. Its slice ids and layers build on
|
|
48
|
+
* the year before, and a revision links the two cakes. Generate once per
|
|
49
|
+
* instance.
|
|
50
|
+
*/
|
|
51
|
+
export declare class ECatalogGenerator {
|
|
52
|
+
private readonly options;
|
|
53
|
+
/**
|
|
54
|
+
* Constructor
|
|
55
|
+
* @param options - The emitter, the configuration and the catalog
|
|
56
|
+
*/
|
|
57
|
+
constructor(options: ECatalogGeneratorOptions);
|
|
58
|
+
/** Emits the catalog and returns the hashes of its cakes */
|
|
59
|
+
generate(): Promise<ECatalogResult>;
|
|
60
|
+
private readonly catalogGlobal;
|
|
61
|
+
private readonly baseId;
|
|
62
|
+
private readonly segment;
|
|
63
|
+
private readonly plan;
|
|
64
|
+
private readonly modelCount;
|
|
65
|
+
private readonly currency;
|
|
66
|
+
private readonly aspects;
|
|
67
|
+
private workshopRefs;
|
|
68
|
+
private previousSliceRef;
|
|
69
|
+
private readonly cakeRefs;
|
|
70
|
+
private readonly previousLayer;
|
|
71
|
+
private readonly raises;
|
|
72
|
+
private readonly versionBoms;
|
|
73
|
+
private readonly current;
|
|
74
|
+
private get emitter();
|
|
75
|
+
private get layers();
|
|
76
|
+
private isFirst;
|
|
77
|
+
private newCarsOf;
|
|
78
|
+
private generateWorkshops;
|
|
79
|
+
private generateVersion;
|
|
80
|
+
private modelIndexOf;
|
|
81
|
+
private assignBrands;
|
|
82
|
+
private assignPrices;
|
|
83
|
+
private priceInputOf;
|
|
84
|
+
private assignWorkshops;
|
|
85
|
+
private assignParts;
|
|
86
|
+
private assignScenes;
|
|
87
|
+
private variantOf;
|
|
88
|
+
private shared;
|
|
89
|
+
private bomOf;
|
|
90
|
+
private sceneOf;
|
|
91
|
+
private partRefsOf;
|
|
92
|
+
private emitSliceIds;
|
|
93
|
+
private emitLayers;
|
|
94
|
+
private layerOf;
|
|
95
|
+
private addedCars;
|
|
96
|
+
private forget;
|
|
97
|
+
private emitCake;
|
|
98
|
+
private emitRevision;
|
|
99
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { EResolvedConfig } from '../config/config.ts';
|
|
2
|
+
import { EEmitter } from '../core/emitter.ts';
|
|
3
|
+
/**
|
|
4
|
+
* Returns the part number prefix of a brand: its first three letters.
|
|
5
|
+
* @param brand - The brand, e.g. "Mercedes-Benz"
|
|
6
|
+
*/
|
|
7
|
+
export declare const prefixOf: (brand: string) => string;
|
|
8
|
+
/**
|
|
9
|
+
* Emits a whole car world: the table configurations, then manufacturer by
|
|
10
|
+
* manufacturer their catalogs, and returns how many cars it holds.
|
|
11
|
+
* @param config - The resolved configuration
|
|
12
|
+
* @param emitter - Receives the rows
|
|
13
|
+
*/
|
|
14
|
+
export declare const generateCarWorld: (config: EResolvedConfig, emitter: EEmitter) => Promise<{
|
|
15
|
+
cars: number;
|
|
16
|
+
}>;
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { EResolvedConfig, ERevisionsConfig, ESharing } from '../config/config.ts';
|
|
2
|
+
import { EVehicleModel } from '../dictionaries/vehicles.ts';
|
|
3
|
+
/** The segments a manufacturer's catalogs are named after */
|
|
4
|
+
export declare const segments: readonly ["cars", "vans", "suvs", "fleet", "classics", "sports", "trucks", "compact", "premium", "electric"];
|
|
5
|
+
/** The digits of the car number in a slice id */
|
|
6
|
+
export declare const carNoDigits = 6;
|
|
7
|
+
/** The models a manufacturer uses when the brands layer is off */
|
|
8
|
+
export declare const modelsWithoutBrands = 5;
|
|
9
|
+
/**
|
|
10
|
+
* Returns the segment of a catalog, unique within a manufacturer.
|
|
11
|
+
* @param catalogIndex - The index of the catalog within the manufacturer
|
|
12
|
+
*/
|
|
13
|
+
export declare const segmentOf: (catalogIndex: number) => string;
|
|
14
|
+
/**
|
|
15
|
+
* Returns the id of a catalog without its year, e.g. "audi-cars".
|
|
16
|
+
* @param manufacturerId - The id of the manufacturer
|
|
17
|
+
* @param catalogIndex - The index of the catalog within the manufacturer
|
|
18
|
+
*/
|
|
19
|
+
export declare const catalogBaseId: (manufacturerId: string, catalogIndex: number) => string;
|
|
20
|
+
/**
|
|
21
|
+
* Returns the slice id of a car, e.g. "audi-cars-000017".
|
|
22
|
+
* @param baseId - The id of the catalog without its year
|
|
23
|
+
* @param carNo - The number of the car, starting at 1
|
|
24
|
+
*/
|
|
25
|
+
export declare const carSliceId: (baseId: string, carNo: number) => string;
|
|
26
|
+
/**
|
|
27
|
+
* Returns the number of a car from its slice id.
|
|
28
|
+
* @param sliceId - The slice id of the car
|
|
29
|
+
*/
|
|
30
|
+
export declare const carNoOf: (sliceId: string) => number;
|
|
31
|
+
/** One model year of a catalog */
|
|
32
|
+
export interface EVersionPlan {
|
|
33
|
+
/** The model year */
|
|
34
|
+
year: number;
|
|
35
|
+
/** The slice ids of all cars of the year */
|
|
36
|
+
carIds: string[];
|
|
37
|
+
/** The cars the year adds; all cars in the first year */
|
|
38
|
+
added: string[];
|
|
39
|
+
/** The cars the year removes */
|
|
40
|
+
removed: string[];
|
|
41
|
+
/** The cars whose price the year changes */
|
|
42
|
+
changed: string[];
|
|
43
|
+
}
|
|
44
|
+
/** What the model years of a catalog depend on */
|
|
45
|
+
export interface EPlanOptions {
|
|
46
|
+
/** The id of the catalog without its year */
|
|
47
|
+
baseId: string;
|
|
48
|
+
/** The number of cars in the first year */
|
|
49
|
+
cars: number;
|
|
50
|
+
/** The first model year */
|
|
51
|
+
startYear: number;
|
|
52
|
+
/** How many years follow, and their shares */
|
|
53
|
+
revisions: Required<ERevisionsConfig>;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Plans the model years of a catalog: which cars each year adds, removes and
|
|
57
|
+
* changes. Everything derives from the shares, nothing is random.
|
|
58
|
+
* @param options - The catalog, its first year and its revisions
|
|
59
|
+
*/
|
|
60
|
+
export declare const planVersions: (options: EPlanOptions) => EVersionPlan[];
|
|
61
|
+
/** The counts of a catalog across its model years, for the estimate */
|
|
62
|
+
export interface EVersionCounts {
|
|
63
|
+
/** How many different cars the catalog lists over all years */
|
|
64
|
+
distinctCars: number;
|
|
65
|
+
/** How many price rows the years need at most, all years together */
|
|
66
|
+
priceRows: number;
|
|
67
|
+
/** How many price rows each year needs at most: new cars and changed prices */
|
|
68
|
+
priceRowsPerVersion: number[];
|
|
69
|
+
/** How many assignments the layers of all years hold together */
|
|
70
|
+
assignments: number;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Counts what planVersions would plan, without building a single id.
|
|
74
|
+
* @param cars - The number of cars in the first year
|
|
75
|
+
* @param revisions - How many years follow, and their shares
|
|
76
|
+
*/
|
|
77
|
+
export declare const countVersions: (cars: number, revisions: Required<ERevisionsConfig>) => EVersionCounts;
|
|
78
|
+
/**
|
|
79
|
+
* Returns how many models the cars of a manufacturer use: the configured
|
|
80
|
+
* number, capped by the models the dictionary knows.
|
|
81
|
+
* @param config - The resolved configuration
|
|
82
|
+
* @param models - The models of the manufacturer
|
|
83
|
+
*/
|
|
84
|
+
export declare const modelCountOf: (config: EResolvedConfig, models: readonly EVehicleModel[]) => number;
|
|
85
|
+
/** What the model of a car depends on */
|
|
86
|
+
export interface EModelOptions {
|
|
87
|
+
/** The number of the car, starting at 1 */
|
|
88
|
+
carNo: number;
|
|
89
|
+
/** The number of cars in the first year */
|
|
90
|
+
initialCars: number;
|
|
91
|
+
/** The number of models the manufacturer offers */
|
|
92
|
+
models: number;
|
|
93
|
+
/** uniform spreads the cars evenly, zipf favours the first models */
|
|
94
|
+
popularity: 'uniform' | 'zipf';
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Returns the index of the model of a car. A car keeps its model over the
|
|
98
|
+
* years, so the index derives from the car number alone.
|
|
99
|
+
* @param options - The car, the catalog size and the models
|
|
100
|
+
*/
|
|
101
|
+
export declare const modelIndexOf: (options: EModelOptions) => number;
|
|
102
|
+
/** Who shares a bill of materials or a scene: a code for ids and a name */
|
|
103
|
+
export interface EVariant {
|
|
104
|
+
/** A short code for part numbers and cache keys, e.g. "a4" */
|
|
105
|
+
code: string;
|
|
106
|
+
/** A readable name, e.g. "Audi A4" */
|
|
107
|
+
name: string;
|
|
108
|
+
/** A number that varies the assemblies and systems the variant uses */
|
|
109
|
+
offset: number;
|
|
110
|
+
}
|
|
111
|
+
/** What the variant of a car depends on */
|
|
112
|
+
export interface EVariantOptions {
|
|
113
|
+
/** perCar, perModel or perCatalog */
|
|
114
|
+
sharing: ESharing;
|
|
115
|
+
/** The slice id of the car */
|
|
116
|
+
sliceId: string;
|
|
117
|
+
/** The model of the car */
|
|
118
|
+
model: EVehicleModel;
|
|
119
|
+
/** The index of the model within the manufacturer */
|
|
120
|
+
modelIndex: number;
|
|
121
|
+
/** The segment of the catalog */
|
|
122
|
+
segment: string;
|
|
123
|
+
/** The index of the catalog within the manufacturer */
|
|
124
|
+
catalogIndex: number;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Returns the variant a car's bill of materials or scene belongs to.
|
|
128
|
+
* @param options - The sharing, the car, its model and its catalog
|
|
129
|
+
*/
|
|
130
|
+
export declare const variantOf: (options: EVariantOptions) => EVariant;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { EPricesConfig } from '../config/config.ts';
|
|
2
|
+
import { EVehicleModel } from '../dictionaries/vehicles.ts';
|
|
3
|
+
import { EPrice } from './car-world.ts';
|
|
4
|
+
/** A model has at most this many different prices per model year */
|
|
5
|
+
export declare const pricesPerModel = 100;
|
|
6
|
+
/** The discounts are this many steps of discountStepPercent: 5, 10 and 15 */
|
|
7
|
+
export declare const discountSteps = 3;
|
|
8
|
+
/** What a price depends on */
|
|
9
|
+
export interface EPriceInput {
|
|
10
|
+
/** The model of the car: its body type decides the price class */
|
|
11
|
+
model: EVehicleModel;
|
|
12
|
+
/** The index of the model within the manufacturer */
|
|
13
|
+
modelIndex: number;
|
|
14
|
+
/** The number of the car, starting at 1 */
|
|
15
|
+
carNo: number;
|
|
16
|
+
/** The model year the price is valid from */
|
|
17
|
+
year: number;
|
|
18
|
+
/** The currency of the catalog */
|
|
19
|
+
currency: string;
|
|
20
|
+
/** How many model years raised the price: 0 for the first price of a car */
|
|
21
|
+
raises: number;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Returns the price row of a car.
|
|
25
|
+
*
|
|
26
|
+
* Expensive body types land high in the range, cheap ones low. The model and
|
|
27
|
+
* the car number spread the prices a little: a model has at most a hundred
|
|
28
|
+
* different prices per year. Every raise adds three percent.
|
|
29
|
+
* @param config - The prices layer configuration
|
|
30
|
+
* @param input - What the price depends on
|
|
31
|
+
*/
|
|
32
|
+
export declare const priceRow: (config: Required<EPricesConfig>, input: EPriceInput) => EPrice;
|