retold 4.0.1 → 4.0.3
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/.claude/settings.local.json +38 -1
- package/README.md +92 -2
- package/docs/README.md +7 -6
- package/docs/_sidebar.md +36 -21
- package/docs/_topbar.md +2 -2
- package/docs/architecture/comprehensions.md +282 -0
- package/docs/architecture/fluid-models.md +355 -0
- package/docs/architecture/module-architecture.md +234 -0
- package/docs/{modules.md → architecture/modules.md} +25 -22
- package/docs/cover.md +2 -2
- package/docs/css/docuserve.css +6 -6
- package/docs/examples/examples.md +71 -0
- package/docs/examples/todolist/todo-list-cli-client.md +178 -0
- package/docs/examples/todolist/todo-list-console-client.md +152 -0
- package/docs/examples/todolist/todo-list-model.md +114 -0
- package/docs/examples/todolist/todo-list-server.md +128 -0
- package/docs/examples/todolist/todo-list-web-client.md +177 -0
- package/docs/examples/todolist/todo-list.md +162 -0
- package/docs/getting-started.md +8 -7
- package/docs/index.html +4 -4
- package/docs/{meadow.md → modules/meadow.md} +4 -6
- package/docs/{orator.md → modules/orator.md} +1 -0
- package/docs/{pict.md → modules/pict.md} +30 -8
- package/docs/{utility.md → modules/utility.md} +0 -9
- package/docs/retold-catalog.json +1792 -231
- package/docs/retold-keyword-index.json +136439 -64616
- package/examples/todo-list/Dockerfile +45 -0
- package/examples/todo-list/README.md +394 -0
- package/examples/todo-list/cli-client/package-lock.json +418 -0
- package/examples/todo-list/cli-client/package.json +19 -0
- package/examples/todo-list/cli-client/source/TodoCLI-CLIProgram.js +30 -0
- package/examples/todo-list/cli-client/source/TodoCLI-Run.js +3 -0
- package/examples/todo-list/cli-client/source/commands/add/TodoCLI-Command-Add.js +74 -0
- package/examples/todo-list/cli-client/source/commands/complete/TodoCLI-Command-Complete.js +84 -0
- package/examples/todo-list/cli-client/source/commands/list/TodoCLI-Command-List.js +110 -0
- package/examples/todo-list/cli-client/source/commands/remove/TodoCLI-Command-Remove.js +49 -0
- package/examples/todo-list/cli-client/source/services/TodoCLI-Service-API.js +92 -0
- package/examples/todo-list/console-client/console-client.cjs +913 -0
- package/examples/todo-list/console-client/package-lock.json +426 -0
- package/examples/todo-list/console-client/package.json +19 -0
- package/examples/todo-list/console-client/views/PictView-TUI-Header.cjs +43 -0
- package/examples/todo-list/console-client/views/PictView-TUI-Layout.cjs +58 -0
- package/examples/todo-list/console-client/views/PictView-TUI-StatusBar.cjs +41 -0
- package/examples/todo-list/console-client/views/PictView-TUI-TaskList.cjs +104 -0
- package/examples/todo-list/docker-motd.sh +36 -0
- package/examples/todo-list/docker-run.sh +2 -0
- package/examples/todo-list/docker-shell.sh +2 -0
- package/examples/todo-list/model/MeadowSchema-Task.json +152 -0
- package/examples/todo-list/model/Task-Compiled.json +25 -0
- package/examples/todo-list/model/Task.mddl +15 -0
- package/examples/todo-list/model/data/seeded_todo_events.csv +1001 -0
- package/examples/todo-list/server/database-initialization-service.cjs +273 -0
- package/examples/todo-list/server/package-lock.json +6113 -0
- package/examples/todo-list/server/package.json +19 -0
- package/examples/todo-list/server/server.cjs +138 -0
- package/examples/todo-list/web-client/css/todolist-theme.css +235 -0
- package/examples/todo-list/web-client/generate-build-config.cjs +18 -0
- package/examples/todo-list/web-client/html/index.html +18 -0
- package/examples/todo-list/web-client/package-lock.json +12030 -0
- package/examples/todo-list/web-client/package.json +43 -0
- package/examples/todo-list/web-client/source/TodoList-Application-Config.json +12 -0
- package/examples/todo-list/web-client/source/TodoList-Application.cjs +383 -0
- package/examples/todo-list/web-client/source/providers/Provider-TaskData.cjs +243 -0
- package/examples/todo-list/web-client/source/providers/Router-Config.json +32 -0
- package/examples/todo-list/web-client/source/views/View-Layout.cjs +75 -0
- package/examples/todo-list/web-client/source/views/View-TaskForm.cjs +87 -0
- package/examples/todo-list/web-client/source/views/View-TaskList.cjs +127 -0
- package/examples/todo-list/web-client/source/views/calendar/View-MonthView.cjs +293 -0
- package/examples/todo-list/web-client/source/views/calendar/View-WeekView.cjs +149 -0
- package/examples/todo-list/web-client/source/views/calendar/View-YearView.cjs +226 -0
- package/modules/Include-Retold-Module-List.sh +2 -2
- package/package.json +5 -5
- package/docs/js/pict.min.js +0 -12
- package/docs/js/pict.min.js.map +0 -1
- package/docs/pict-docuserve.min.js +0 -58
- package/docs/pict-docuserve.min.js.map +0 -1
- /package/docs/{architecture.md → architecture/architecture.md} +0 -0
- /package/docs/{fable.md → modules/fable.md} +0 -0
|
@@ -52,7 +52,44 @@
|
|
|
52
52
|
"Bash(kill:*)",
|
|
53
53
|
"Bash(wait)",
|
|
54
54
|
"Bash(while read d)",
|
|
55
|
-
"Bash(do echo \"Cleaning: $d\")"
|
|
55
|
+
"Bash(do echo \"Cleaning: $d\")",
|
|
56
|
+
"Bash(git pull:*)",
|
|
57
|
+
"Bash(git push:*)",
|
|
58
|
+
"Bash(git add:*)",
|
|
59
|
+
"Bash(git rebase:*)",
|
|
60
|
+
"Bash(git commit:*)",
|
|
61
|
+
"Bash(cd:*)",
|
|
62
|
+
"Bash(node source/cli/Meadow-Integration-CLI-Run.js:*)",
|
|
63
|
+
"Bash(bash:*)",
|
|
64
|
+
"Bash(npm test)",
|
|
65
|
+
"WebFetch(domain:stevenvelozo.github.io)",
|
|
66
|
+
"Bash(git ls-tree:*)",
|
|
67
|
+
"Bash(npx mocha:*)",
|
|
68
|
+
"Bash(pkill:*)",
|
|
69
|
+
"Bash(ln:*)",
|
|
70
|
+
"Bash(timeout 10 node:*)",
|
|
71
|
+
"Bash(npm ls:*)",
|
|
72
|
+
"Bash(node -e \"\nconst proc = require\\(''child_process''\\).spawn\\(''node'', [''server.cjs''], { cwd: ''/Users/stevenvelozo/Code/retold/examples/todo-list/server'', stdio: ''pipe'' }\\);\nlet output = '''';\nproc.stdout.on\\(''data'', \\(d\\) => { output += d.toString\\(\\); }\\);\nproc.stderr.on\\(''data'', \\(d\\) => { output += d.toString\\(\\); }\\);\nsetTimeout\\(\\(\\) => {\n const http = require\\(''http''\\);\n\n // Test 1: Static HTML serving\n http.get\\(''http://localhost:8086/'', \\(res\\) => {\n let data = '''';\n res.on\\(''data'', \\(c\\) => { data += c; }\\);\n res.on\\(''end'', \\(\\) => {\n console.log\\(''=== Test 1: Static HTML ===''\\);\n console.log\\(''Status:'', res.statusCode\\);\n console.log\\(''Contains RetoldExampleTodoWebClient:'', data.includes\\(''RetoldExampleTodoWebClient''\\)\\);\n\n // Test 2: API list\n http.get\\(''http://localhost:8086/1.0/Tasks'', \\(res2\\) => {\n let data2 = '''';\n res2.on\\(''data'', \\(c\\) => { data2 += c; }\\);\n res2.on\\(''end'', \\(\\) => {\n let tasks = JSON.parse\\(data2\\);\n console.log\\(''''\\);\n console.log\\(''=== Test 2: API Reads ===''\\);\n console.log\\(''Tasks returned:'', tasks.length\\);\n\n // Test 3: Create\n let postData = JSON.stringify\\({Name:''CJS test'',Status:''Pending'',LengthInHours:1}\\);\n let req = http.request\\({hostname:''localhost'',port:8086,path:''/1.0/Task'',method:''POST'',headers:{''Content-Type'':''application/json''}}, \\(res3\\) => {\n let data3 = '''';\n res3.on\\(''data'', \\(c\\) => { data3 += c; }\\);\n res3.on\\(''end'', \\(\\) => {\n let created = JSON.parse\\(data3\\);\n console.log\\(''''\\);\n console.log\\(''=== Test 3: Create ===''\\);\n console.log\\(''Created ID:'', created.IDTask, ''Name:'', created.Name\\);\n\n // Test 4: Update\n let putData = JSON.stringify\\({IDTask:created.IDTask, Name:''CJS test \\(updated\\)'', Status:''Complete''}\\);\n let req2 = http.request\\({hostname:''localhost'',port:8086,path:''/1.0/Task'',method:''PUT'',headers:{''Content-Type'':''application/json''}}, \\(res4\\) => {\n let data4 = '''';\n res4.on\\(''data'', \\(c\\) => { data4 += c; }\\);\n res4.on\\(''end'', \\(\\) => {\n let updated = JSON.parse\\(data4\\);\n console.log\\(''''\\);\n console.log\\(''=== Test 4: Update ===''\\);\n console.log\\(''Updated:'', updated.Name, updated.Status\\);\n\n // Test 5: Delete\n let req3 = http.request\\({hostname:''localhost'',port:8086,path:''/1.0/Task/''+created.IDTask,method:''DELETE''}, \\(res5\\) => {\n let data5 = '''';\n res5.on\\(''data'', \\(c\\) => { data5 += c; }\\);\n res5.on\\(''end'', \\(\\) => {\n let del = JSON.parse\\(data5\\);\n console.log\\(''''\\);\n console.log\\(''=== Test 5: Delete ===''\\);\n console.log\\(''Deleted count:'', del.Count\\);\n console.log\\(''''\\);\n console.log\\(''=== ALL TESTS PASSED ===''\\);\n proc.kill\\(\\);\n process.exit\\(0\\);\n }\\);\n }\\);\n req3.end\\(\\);\n }\\);\n }\\);\n req2.write\\(putData\\);\n req2.end\\(\\);\n }\\);\n }\\);\n req.write\\(postData\\);\n req.end\\(\\);\n }\\);\n }\\);\n }\\);\n }\\);\n}, 3000\\);\n\")",
|
|
73
|
+
"Bash(node -e \" const _origStderrWrite = process.stderr.write; process.stderr.write = function \\(pChunk\\) { if \\(typeof pChunk === ''string'' && pChunk.indexOf\\(''Setulc''\\) !== -1\\) { return true; } return _origStderrWrite.apply\\(process.stderr, arguments\\); }; const blessed = require\\(''blessed''\\); const libPict = require\\(''pict''\\); const libPictApplication = require\\(''pict-application''\\); const libPictTerminalUI = require\\(''pict-terminalui''\\); const libViewLayout = require\\(''/Users/stevenvelozo/Code/retold/examples/todo-list/console-client/views/PictView-TUI-Layout.cjs''\\); const libViewHeader = require\\(''/Users/stevenvelozo/Code/retold/examples/todo-list/console-client/views/PictView-TUI-Header.cjs''\\); const libViewTaskList = require\\(''/Users/stevenvelozo/Code/retold/examples/todo-list/console-client/views/PictView-TUI-TaskList.cjs''\\); const libViewStatusBar = require\\(''/Users/stevenvelozo/Code/retold/examples/todo-list/console-client/views/PictView-TUI-StatusBar.cjs''\\); console.log\\(''All console-client .cjs modules loaded OK''\\); console.log\\(''Views:'', [libViewLayout, libViewHeader, libViewTaskList, libViewStatusBar].map\\(v => v.default_configuration.ViewIdentifier\\).join\\('', ''\\)\\); process.exit\\(0\\); \")",
|
|
74
|
+
"Bash(node Stricture.js:*)",
|
|
75
|
+
"Bash(timeout 15 node:*)",
|
|
76
|
+
"Bash(npm update:*)",
|
|
77
|
+
"Bash(awk:*)",
|
|
78
|
+
"Bash(/Users/stevenvelozo/Code/retold/examples/todo-list/model/data/fix_csv.py << 'PYEOF'\nimport re\n\nINPUT_FILE = '/Users/stevenvelozo/Code/retold/examples/todo-list/model/data/seeded_todo_events.csv'\n\nwith open\\(INPUT_FILE, 'r'\\) as f:\n lines = f.readlines\\(\\)\n\nheader = lines[0].strip\\(\\)\noutput_lines = [header]\n\n# Pattern for the last 3 fields: date, number, status\n# DueDate is YYYY-MM-DD, LengthInHours is a number \\(int or float\\), Status is one of three values\ntail_pattern = re.compile\\(r',\\(\\\\d{4}-\\\\d{2}-\\\\d{2}\\),\\([\\\\d.]+\\),\\(Pending|In Progress|Complete\\)\\\\s*\\)\n\nfor i, line in enumerate\\(lines[1:], start=2\\):\n line = line.strip\\(\\)\n if not line:\n continue\n\n # Find the tail \\(DueDate, LengthInHours, Status\\) from the right\n match = tail_pattern.search\\(line\\)\n if not match:\n print\\(f\"WARNING: Line {i} does not match expected tail pattern: {line[:80]}\"\\)\n output_lines.append\\(line\\)\n continue\n\n tail_start = match.start\\(\\)\n due_date = match.group\\(1\\)\n length = match.group\\(2\\)\n status = match.group\\(3\\)\n\n # Everything before the tail is \"Name,Description\" \\(with possible extra commas in Description\\)\n front = line[:tail_start]\n\n # Split front into Name and Description at the first comma\n first_comma = front.index\\(','\\)\n name = front[:first_comma]\n description = front[first_comma + 1:]\n\n # Strip any existing surrounding quotes from the description\n if description.startswith\\('\"'\\) and description.endswith\\('\"'\\):\n description = description[1:-1]\n\n # Escape any double quotes inside the description \\(double them per CSV standard\\)\n description = description.replace\\('\"', '\"\"'\\)\n\n # Reconstruct the line with the description properly quoted\n fixed_line = f'{name},\"{description}\",{due_date},{length},{status}'\n output_lines.append\\(fixed_line\\)\n\nwith open\\(INPUT_FILE, 'w'\\) as f:\n f.write\\('\\\\n'.join\\(output_lines\\) + '\\\\n'\\)\n\nprint\\(f\"Processed {len\\(output_lines\\) - 1} data rows \\(plus header\\).\"\\)\nprint\\(\"File written successfully.\"\\)\nPYEOF)",
|
|
79
|
+
"Bash(for f in architecture.md fable.md meadow.md orator.md pict.md utility.md modules.md examples.md todo-list.md todo-list-model.md todo-list-server.md todo-list-web-client.md todo-list-console-client.md todo-list-cli-client.md)",
|
|
80
|
+
"Bash(do [ -f \"/Users/stevenvelozo/Code/retold/docs/$f\" ])",
|
|
81
|
+
"WebFetch(domain:registry.npmjs.org)",
|
|
82
|
+
"WebFetch(domain:data.jsdelivr.com)",
|
|
83
|
+
"WebFetch(domain:purge.jsdelivr.net)",
|
|
84
|
+
"WebFetch(domain:raw.githubusercontent.com)",
|
|
85
|
+
"Bash(npx quack prepare-docs:*)",
|
|
86
|
+
"Bash(xargs kill)",
|
|
87
|
+
"WebFetch(domain:cdn.jsdelivr.net)",
|
|
88
|
+
"Bash(git stash:*)",
|
|
89
|
+
"Bash(npx indoctrinate:*)",
|
|
90
|
+
"Bash(npm link)",
|
|
91
|
+
"Bash(npm link:*)",
|
|
92
|
+
"Bash(indoctrinate --version:*)"
|
|
56
93
|
]
|
|
57
94
|
}
|
|
58
95
|
}
|
package/README.md
CHANGED
|
@@ -1,2 +1,92 @@
|
|
|
1
|
-
#
|
|
2
|
-
|
|
1
|
+
# Retold
|
|
2
|
+
|
|
3
|
+
> A story-obsessed application suite.
|
|
4
|
+
|
|
5
|
+
Retold is a collection of ~50 JavaScript/Node.js modules for building web applications and APIs. The modules span five groups — from core dependency injection up through data access, API serving, and full MVC — all designed to compose together through a shared service provider pattern. Plain JavaScript, no TypeScript. MIT licensed.
|
|
6
|
+
|
|
7
|
+
## Module Groups
|
|
8
|
+
|
|
9
|
+
| Group | Purpose |
|
|
10
|
+
|-------|---------|
|
|
11
|
+
| **Fable** | Core ecosystem: dependency injection, configuration, logging, UUID generation, expression parsing, REST client, template engine |
|
|
12
|
+
| **Meadow** | Data access layer: provider-agnostic ORM, query generation (FoxHound), schema definitions (Stricture), database connectors (MySQL, MSSQL, SQLite), auto-generated REST endpoints |
|
|
13
|
+
| **Orator** | API server: HTTP server abstraction over Restify, static file serving, reverse proxy, WebSocket reporting |
|
|
14
|
+
| **Pict** | MVC tools: views, templates, providers, application lifecycle — for browser, terminal, or any text-based UI |
|
|
15
|
+
| **Utility** | Build tools (Quackage), manifest management (Manyfest), documentation generation (Indoctrinate), process supervision (Ultravisor) |
|
|
16
|
+
|
|
17
|
+
## The Service Provider Pattern
|
|
18
|
+
|
|
19
|
+
Every Retold module extends `fable-serviceproviderbase` and registers with a Fable instance. That instance provides dependency injection, logging, UUID generation, and shared configuration. Any registered service can reach any other through `this.fable`, so modules are loosely coupled — you can swap database providers, change server implementations, or add custom services without modifying existing code.
|
|
20
|
+
|
|
21
|
+
```javascript
|
|
22
|
+
const libFable = require('fable');
|
|
23
|
+
const libMeadow = require('meadow');
|
|
24
|
+
const libOrator = require('orator');
|
|
25
|
+
|
|
26
|
+
let _Fable = new libFable({ Product: 'MyApp', LogLevel: 3 });
|
|
27
|
+
|
|
28
|
+
// Services register with the Fable instance
|
|
29
|
+
let _Meadow = _Fable.instantiateServiceProvider('Meadow');
|
|
30
|
+
let _Orator = _Fable.instantiateServiceProvider('Orator');
|
|
31
|
+
|
|
32
|
+
// Every service can reach every other service
|
|
33
|
+
// _Meadow.fable.Orator, _Orator.fable.Meadow, etc.
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Quick Start
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
# Core foundation
|
|
40
|
+
npm install fable
|
|
41
|
+
|
|
42
|
+
# Data access
|
|
43
|
+
npm install meadow foxhound stricture
|
|
44
|
+
|
|
45
|
+
# API server
|
|
46
|
+
npm install orator orator-serviceserver-restify meadow-endpoints
|
|
47
|
+
|
|
48
|
+
# Browser MVC
|
|
49
|
+
npm install pict
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
```javascript
|
|
53
|
+
const libFable = require('fable');
|
|
54
|
+
|
|
55
|
+
let _Fable = new libFable({
|
|
56
|
+
Product: 'MyApp',
|
|
57
|
+
ProductVersion: '1.0.0',
|
|
58
|
+
LogLevel: 3
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
_Fable.log.info('Retold application started.');
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Repository Structure
|
|
65
|
+
|
|
66
|
+
Each module is its own git repo, cloned into a category folder under `modules/`. The root repo tracks module organization — individual module code lives in their respective repos.
|
|
67
|
+
|
|
68
|
+
```
|
|
69
|
+
retold/
|
|
70
|
+
├── source/Retold.cjs
|
|
71
|
+
├── test/
|
|
72
|
+
├── docs/ # Documentation site (pict-docuserve)
|
|
73
|
+
└── modules/
|
|
74
|
+
├── fable/ # Core ecosystem (~6 modules)
|
|
75
|
+
├── meadow/ # Data access (~13 modules)
|
|
76
|
+
├── orator/ # API server (~7 modules)
|
|
77
|
+
├── pict/ # MVC tools (~15 modules)
|
|
78
|
+
└── utility/ # Build & docs (~10 modules)
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## Documentation
|
|
82
|
+
|
|
83
|
+
Full documentation lives in the [`docs/`](docs/) folder and is served by pict-docuserve.
|
|
84
|
+
|
|
85
|
+
- [Architecture](docs/architecture/architecture.md) — Layer model, service provider pattern, component breakdown
|
|
86
|
+
- [Getting Started](docs/getting-started.md) — Building your first Retold application step by step
|
|
87
|
+
- [Examples](docs/examples/examples.md) — Complete runnable applications including a full-stack Todo List
|
|
88
|
+
- [All Modules](docs/architecture/modules.md) — Every repository in the suite with descriptions and links
|
|
89
|
+
|
|
90
|
+
## License
|
|
91
|
+
|
|
92
|
+
MIT
|
package/docs/README.md
CHANGED
|
@@ -148,13 +148,14 @@ _Fable.log.info('Retold application started.');
|
|
|
148
148
|
|
|
149
149
|
## Learn More
|
|
150
150
|
|
|
151
|
-
- **[Architecture](architecture.md)** — Detailed layer-by-layer breakdown of the module stack
|
|
151
|
+
- **[Architecture](architecture/architecture.md)** — Detailed layer-by-layer breakdown of the module stack
|
|
152
152
|
- **[Getting Started](getting-started.md)** — Building your first Retold application
|
|
153
|
-
- **[
|
|
154
|
-
- **[
|
|
155
|
-
- **[
|
|
156
|
-
- **[
|
|
157
|
-
- **[
|
|
153
|
+
- **[Examples](examples/examples.md)** — Complete runnable applications including the Todo List full-stack example
|
|
154
|
+
- **[Fable In Depth](modules/fable.md)** — The core ecosystem and service provider pattern
|
|
155
|
+
- **[Meadow In Depth](modules/meadow.md)** — Data access, schemas, and query generation
|
|
156
|
+
- **[Orator In Depth](modules/orator.md)** — API servers and endpoint generation
|
|
157
|
+
- **[Pict In Depth](modules/pict.md)** — MVC tools for browser and console UIs
|
|
158
|
+
- **[All Modules](modules/modules.md)** — Exhaustive list of every Retold repository
|
|
158
159
|
|
|
159
160
|
## License
|
|
160
161
|
|
package/docs/_sidebar.md
CHANGED
|
@@ -1,10 +1,22 @@
|
|
|
1
1
|
- Getting Started
|
|
2
2
|
|
|
3
|
-
- [Architecture](architecture.md)
|
|
4
3
|
- [Getting Started](getting-started.md)
|
|
5
|
-
- [
|
|
4
|
+
- [Architecture](architecture/architecture.md)
|
|
5
|
+
- [Ecosystem Architecture](architecture/module-architecture.md)
|
|
6
|
+
- [Fluid Models](architecture/fluid-models.md)
|
|
7
|
+
- [Comprehensions](architecture/comprehensions.md)
|
|
8
|
+
- [All Modules](architecture/modules.md)
|
|
6
9
|
|
|
7
|
-
- [
|
|
10
|
+
- [Examples](examples/examples.md)
|
|
11
|
+
|
|
12
|
+
- [Todo List Application](examples/todolist/todo-list.md)
|
|
13
|
+
- [Todo List: System Model](examples/todolist/todo-list-model.md)
|
|
14
|
+
- [Todo List: API Server](examples/todolist/todo-list-server.md)
|
|
15
|
+
- [Todo List: Web Client](examples/todolist/todo-list-web-client.md)
|
|
16
|
+
- [Todo List: Console Client](examples/todolist/todo-list-console-client.md)
|
|
17
|
+
- [Todo List: CLI Client](examples/todolist/todo-list-cli-client.md)
|
|
18
|
+
|
|
19
|
+
- [Fable — Core Ecosystem](modules/fable.md)
|
|
8
20
|
|
|
9
21
|
- [fable](/fable/fable/)
|
|
10
22
|
- [fable-serviceproviderbase](/fable/fable-serviceproviderbase/)
|
|
@@ -13,23 +25,22 @@
|
|
|
13
25
|
- [fable-uuid](/fable/fable-uuid/)
|
|
14
26
|
- [fable-log-logger-bunyan](/fable/fable-log-logger-bunyan/)
|
|
15
27
|
|
|
16
|
-
- [Meadow — Data Access](meadow.md)
|
|
28
|
+
- [Meadow — Data Access](modules/meadow.md)
|
|
17
29
|
|
|
18
|
-
- [meadow](/meadow/meadow/)
|
|
19
|
-
- [foxhound](/meadow/foxhound/)
|
|
20
30
|
- [stricture](/meadow/stricture/)
|
|
31
|
+
- [foxhound](/meadow/foxhound/)
|
|
32
|
+
- [bibliograph](/meadow/bibliograph/)
|
|
33
|
+
- [meadow](/meadow/meadow/)
|
|
34
|
+
- [parime](/meadow/parime/)
|
|
21
35
|
- [meadow-endpoints](/meadow/meadow-endpoints/)
|
|
22
|
-
- [retold-data-service](/meadow/retold-data-service/)
|
|
23
36
|
- [meadow-connection-mysql](/meadow/meadow-connection-mysql/)
|
|
24
37
|
- [meadow-connection-mssql](/meadow/meadow-connection-mssql/)
|
|
25
38
|
- [meadow-connection-sqlite](/meadow/meadow-connection-sqlite/)
|
|
39
|
+
- [retold-data-service](/meadow/retold-data-service/)
|
|
26
40
|
- [retold-harness](/meadow/retold-harness/)
|
|
27
|
-
- [bibliograph](/meadow/bibliograph/)
|
|
28
|
-
- [parime](/meadow/parime/)
|
|
29
41
|
- [meadow-integration](/meadow/meadow-integration/)
|
|
30
|
-
- [meadow-graph-client](/meadow/meadow-graph-client/)
|
|
31
42
|
|
|
32
|
-
- [Orator — API Server](orator.md)
|
|
43
|
+
- [Orator — API Server](modules/orator.md)
|
|
33
44
|
|
|
34
45
|
- [orator](/orator/orator/)
|
|
35
46
|
- [orator-serviceserver-restify](/orator/orator-serviceserver-restify/)
|
|
@@ -37,29 +48,33 @@
|
|
|
37
48
|
- [orator-http-proxy](/orator/orator-http-proxy/)
|
|
38
49
|
- [tidings](/orator/tidings/)
|
|
39
50
|
- [orator-endpoint](/orator/orator-endpoint/)
|
|
51
|
+
- [orator-conversion](/orator/orator-conversion/)
|
|
40
52
|
|
|
41
|
-
- [Pict — MVC Tools](pict.md)
|
|
53
|
+
- [Pict — MVC Tools](modules/pict.md)
|
|
42
54
|
|
|
43
55
|
- [pict](/pict/pict/)
|
|
44
|
-
- [pict-view](/pict/pict-view/)
|
|
45
56
|
- [pict-template](/pict/pict-template/)
|
|
57
|
+
- [pict-view](/pict/pict-view/)
|
|
46
58
|
- [pict-provider](/pict/pict-provider/)
|
|
47
59
|
- [pict-application](/pict/pict-application/)
|
|
48
|
-
- [pict-
|
|
60
|
+
- [pict-panel](/pict/pict-panel/)
|
|
61
|
+
- [pict-nonlinearconfig](/pict/pict-nonlinearconfig/)
|
|
62
|
+
- [pict-section-flow](/pict/pict-section-flow/)
|
|
63
|
+
- [pict-docuserve](/pict/pict-docuserve/)
|
|
64
|
+
- [cryptbrau](/pict/cryptbrau/)
|
|
65
|
+
- [informary](/pict/informary/)
|
|
66
|
+
- [pict-service-commandlineutility](/pict/pict-service-commandlineutility/)
|
|
49
67
|
- [pict-section-recordset](/pict/pict-section-recordset/)
|
|
50
|
-
- [pict-section-tuigrid](/pict/pict-section-tuigrid/)
|
|
51
68
|
- [pict-section-content](/pict/pict-section-content/)
|
|
52
|
-
- [pict-
|
|
69
|
+
- [pict-section-form](/pict/pict-section-form/)
|
|
70
|
+
- [pict-section-tuigrid](/pict/pict-section-tuigrid/)
|
|
53
71
|
- [pict-router](/pict/pict-router/)
|
|
54
|
-
- [informary](/pict/informary/)
|
|
55
|
-
- [cryptbrau](/pict/cryptbrau/)
|
|
56
72
|
- [pict-serviceproviderbase](/pict/pict-serviceproviderbase/)
|
|
57
|
-
- [pict-
|
|
73
|
+
- [pict-terminalui](/pict/pict-terminalui/)
|
|
58
74
|
|
|
59
|
-
- [Utility — Build Tools](utility.md)
|
|
75
|
+
- [Utility — Build Tools](modules/utility.md)
|
|
60
76
|
|
|
61
77
|
- [indoctrinate](/utility/indoctrinate/)
|
|
62
78
|
- [manyfest](/utility/manyfest/)
|
|
63
79
|
- [quackage](/utility/quackage/)
|
|
64
|
-
- [choreographic](/utility/choreographic/)
|
|
65
80
|
- [ultravisor](/utility/ultravisor/)
|
package/docs/_topbar.md
CHANGED
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
# Comprehensions
|
|
2
|
+
|
|
3
|
+
Comprehensions are the intermediate data format that Retold uses for data integration pipelines. When you need to ingest records from external systems — CSV files, JSON feeds, other databases — comprehensions provide a consistent structure for staging, deduplicating, merging, and cross-referencing that data before it reaches Meadow.
|
|
4
|
+
|
|
5
|
+
The modules [bibliograph](/meadow/bibliograph/) and [meadow-integration](/meadow/meadow-integration/) both work with comprehensions. Bibliograph provides key-value record comprehension for change tracking. Meadow-integration provides the full transformation and integration pipeline — mapping source data into comprehensions and pushing them into Meadow entities through the integration adapter.
|
|
6
|
+
|
|
7
|
+
## The Object Format
|
|
8
|
+
|
|
9
|
+
A comprehension is traditionally a JSON object where entity records are keyed by their GUID. This is the primary format and the one the integration pipeline works with internally.
|
|
10
|
+
|
|
11
|
+
```json
|
|
12
|
+
{
|
|
13
|
+
"Book": {
|
|
14
|
+
"Book_1": { "GUIDBook": "Book_1", "Title": "The Hunger Games", "ISBN": "9780439023481" },
|
|
15
|
+
"Book_2": { "GUIDBook": "Book_2", "Title": "Dune", "ISBN": "9780441172719" }
|
|
16
|
+
},
|
|
17
|
+
"Author": {
|
|
18
|
+
"Author_SuzanneCollins": { "GUIDAuthor": "Author_SuzanneCollins", "Name": "Suzanne Collins" },
|
|
19
|
+
"Author_FrankHerbert": { "GUIDAuthor": "Author_FrankHerbert", "Name": "Frank Herbert" }
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Each top-level key is an entity name. Within each entity, records are stored as properties keyed by their GUID value. This gives you:
|
|
25
|
+
|
|
26
|
+
- **O(1) lookup** — find any record by GUID without scanning
|
|
27
|
+
- **Natural deduplication** — writing the same GUID twice merges rather than duplicates
|
|
28
|
+
- **Easy merging** — `Object.assign()` combines records from multiple sources
|
|
29
|
+
- **Multi-entity support** — a single comprehension can hold Books, Authors, and join records together
|
|
30
|
+
|
|
31
|
+
## The Array Format
|
|
32
|
+
|
|
33
|
+
Comprehensions can also be arrays of records. This format is useful for export, for consumption by tools that expect flat record lists, or for feeding data to systems and softwares that do not benefit from GUID-keyed lookup.
|
|
34
|
+
|
|
35
|
+
```json
|
|
36
|
+
[
|
|
37
|
+
{ "GUIDBook": "Book_1", "Title": "The Hunger Games", "ISBN": "9780439023481" },
|
|
38
|
+
{ "GUIDBook": "Book_2", "Title": "Dune", "ISBN": "9780441172719" }
|
|
39
|
+
]
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
The array format loses the O(1) lookup performance and the automatic deduplication of the object format. You convert between the two formats using the `comprehensionarray` command or the `/1.0/Comprehension/ToArray` endpoint.
|
|
43
|
+
|
|
44
|
+
## How Data Flows Through Comprehensions
|
|
45
|
+
|
|
46
|
+
Source data enters the integration pipeline through mapping files, gets staged as a comprehension, and then flows through the integration adapter into Meadow entities.
|
|
47
|
+
|
|
48
|
+
```mermaid
|
|
49
|
+
graph LR
|
|
50
|
+
source["Source Data<br/><i>CSV, JSON, TSV</i>"]
|
|
51
|
+
mapping["Mapping File<br/><i>GUIDTemplate,<br/>field mappings,<br/>solvers</i>"]
|
|
52
|
+
comp["Comprehension<br/><i>GUID-keyed<br/>entity records</i>"]
|
|
53
|
+
adapter["Integration<br/>Adapter<br/><i>Marshal, upsert</i>"]
|
|
54
|
+
meadow["Meadow<br/>Entities<br/><i>Database records</i>"]
|
|
55
|
+
|
|
56
|
+
source --> mapping
|
|
57
|
+
mapping --> comp
|
|
58
|
+
comp --> adapter
|
|
59
|
+
adapter --> meadow
|
|
60
|
+
|
|
61
|
+
style source fill:#f5f5f5,stroke:#bdbdbd,color:#333
|
|
62
|
+
style mapping fill:#fff3e0,stroke:#ffa726,color:#333
|
|
63
|
+
style comp fill:#fff3e0,stroke:#ffa726,color:#333
|
|
64
|
+
style adapter fill:#fff3e0,stroke:#ffa726,color:#333
|
|
65
|
+
style meadow fill:#fff3e0,stroke:#ff9800,color:#333
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Mapping files control the transformation from source columns to comprehension fields. They define the entity name, the GUID template, and the field-by-field mappings using Pict template expressions.
|
|
69
|
+
|
|
70
|
+
## GUID Design
|
|
71
|
+
|
|
72
|
+
GUIDs are the primary key for comprehension records. Good GUID design ensures three things:
|
|
73
|
+
|
|
74
|
+
- **Uniqueness** — each record gets a distinct key
|
|
75
|
+
- **Determinism** — the same source data always generates the same GUID
|
|
76
|
+
- **Mergeability** — related data from different sources can be matched by GUID
|
|
77
|
+
|
|
78
|
+
GUID templates use Pict's jellyfish template syntax (`{~D:...~}`) to pull values from the source record:
|
|
79
|
+
|
|
80
|
+
```json
|
|
81
|
+
{
|
|
82
|
+
"Entity": "Book",
|
|
83
|
+
"GUIDTemplate": "Book_{~D:Record.id~}",
|
|
84
|
+
"Mappings": {
|
|
85
|
+
"Title": "{~D:Record.title~}",
|
|
86
|
+
"ISBN": "{~D:Record.isbn~}"
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
This produces records keyed by `Book_1`, `Book_2`, etc. When the same GUID template is used across multiple transform runs on different source files, records with matching GUIDs merge automatically in the comprehension.
|
|
92
|
+
|
|
93
|
+
## Combinatorial Keys
|
|
94
|
+
|
|
95
|
+
When no single source column provides a natural unique key, you build a combinatorial GUID from multiple columns.
|
|
96
|
+
|
|
97
|
+
```json
|
|
98
|
+
{
|
|
99
|
+
"Entity": "Transaction",
|
|
100
|
+
"GUIDTemplate": "TXN_{~D:Record.date~}_{~D:Record.account_id~}_{~D:Record.seq~}",
|
|
101
|
+
"Mappings": {
|
|
102
|
+
"Amount": "{~D:Record.amount~}",
|
|
103
|
+
"AccountID": "{~D:Record.account_id~}",
|
|
104
|
+
"TransactionDate": "{~D:Record.date~}"
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
This produces GUIDs like `TXN_2025-02-17_12345_001` — unique across the combination of date, account, and sequence number. The composite key ensures that two transactions on the same day for the same account are distinguishable, while the determinism means re-running the transform on the same source data produces the same GUIDs (merging cleanly rather than creating duplicates).
|
|
110
|
+
|
|
111
|
+
Format modifiers like `{~PascalCaseIdentifier:Record.name~}` are useful in combinatorial keys when the source values contain spaces or special characters that would make messy GUIDs.
|
|
112
|
+
|
|
113
|
+
## The `_GUID` Prefix — Bypassing Magic Marshaling
|
|
114
|
+
|
|
115
|
+
When the integration adapter pushes comprehension records into Meadow, places a prefix on GUID fields based on the integration being run. This can signify the source system, data set or anything else the developer wants to connote in the GUID string itself. The underscore prefix controls which code path a GUID field takes.
|
|
116
|
+
|
|
117
|
+
**`GUIDBook`** — a field starting with `GUID` is treated as an **external system GUID**. The adapter runs it through the full marshaling pipeline: it looks up the external GUID in the mapping table to find the corresponding Meadow numeric ID, and writes that ID into the output record as `IDBook`.
|
|
118
|
+
|
|
119
|
+
**`_GUIDBook`** — a field starting with `_GUID` is treated as a **Meadow GUID** that already exists in the system. The adapter skips the external-to-internal translation and does a direct lookup from Meadow GUID to numeric ID. No prefix magic is applied.
|
|
120
|
+
|
|
121
|
+
```json
|
|
122
|
+
{
|
|
123
|
+
"BookReview": {
|
|
124
|
+
"Review_1": {
|
|
125
|
+
"GUIDBookReview": "Review_1",
|
|
126
|
+
"GUIDBook": "Book_1",
|
|
127
|
+
"_GUIDUser": "0x01234567"
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
In this example, `GUIDBook` with value `Book_1` is an external key from the comprehension — the adapter will look up what Meadow ID corresponds to external GUID `Book_1`. But `_GUIDUser` with value `0x01234567` is already a Meadow GUID — the adapter looks it up directly without applying the integration prefix.
|
|
134
|
+
|
|
135
|
+
The distinction matters when you are integrating data that references both external records (from the same import batch) and existing Meadow records (already in the database). Use `GUID` for references within the comprehension. Use `_GUID` when pointing at records that already exist in Meadow.
|
|
136
|
+
|
|
137
|
+
## Cross-Connecting Recordsets
|
|
138
|
+
|
|
139
|
+
Comprehensions handle relationships between entities through GUID cross-references. A record in one entity can reference records in other entities by including `GUID`-prefixed fields that match the target entity's GUID values.
|
|
140
|
+
|
|
141
|
+
### Join Tables from a Single Source
|
|
142
|
+
|
|
143
|
+
A common pattern is generating join table records from a single source that contains embedded relationships. For example, a books CSV where the `authors` column contains comma-separated names:
|
|
144
|
+
|
|
145
|
+
```
|
|
146
|
+
id,title,authors
|
|
147
|
+
1,The Hunger Games,"Suzanne Collins"
|
|
148
|
+
2,Dune,"Frank Herbert"
|
|
149
|
+
3,Good Omens,"Terry Pratchett,Neil Gaiman"
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
Three mapping files transform this one CSV into three related entity sets:
|
|
153
|
+
|
|
154
|
+
**Books:**
|
|
155
|
+
```json
|
|
156
|
+
{
|
|
157
|
+
"Entity": "Book",
|
|
158
|
+
"GUIDTemplate": "Book_{~D:Record.id~}",
|
|
159
|
+
"Mappings": { "Title": "{~D:Record.title~}" }
|
|
160
|
+
}
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
**Authors** (one source row can produce multiple records):
|
|
164
|
+
```json
|
|
165
|
+
{
|
|
166
|
+
"Entity": "Author",
|
|
167
|
+
"MultipleGUIDUniqueness": true,
|
|
168
|
+
"Solvers": [
|
|
169
|
+
"NewRecordsGUIDUniqueness = STRINGGETSEGMENTS(IncomingRecord.authors,\",\")"
|
|
170
|
+
],
|
|
171
|
+
"GUIDTemplate": "Author_{~PascalCaseIdentifier:Record._GUIDUniqueness~}",
|
|
172
|
+
"Mappings": { "Name": "{~D:Record._GUIDUniqueness~}" }
|
|
173
|
+
}
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
**BookAuthorJoin** (cross-references both entities):
|
|
177
|
+
```json
|
|
178
|
+
{
|
|
179
|
+
"Entity": "BookAuthorJoin",
|
|
180
|
+
"MultipleGUIDUniqueness": true,
|
|
181
|
+
"Solvers": [
|
|
182
|
+
"NewRecordsGUIDUniqueness = STRINGGETSEGMENTS(IncomingRecord.authors,\",\")"
|
|
183
|
+
],
|
|
184
|
+
"GUIDTemplate": "BAJ_A_{~PascalCaseIdentifier:Record._GUIDUniqueness~}_B_{~D:Record.id~}",
|
|
185
|
+
"Mappings": {
|
|
186
|
+
"GUIDBook": "Book_{~D:Record.id~}",
|
|
187
|
+
"GUIDAuthor": "Author_{~PascalCaseIdentifier:Record._GUIDUniqueness~}"
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
The `MultipleGUIDUniqueness` flag combined with a Solver expression splits the comma-separated authors into individual entries. For each entry, the system creates a separate record with `_GUIDUniqueness` set to that entry's value. The GUID template and mappings use `_GUIDUniqueness` to build unique keys and cross-references.
|
|
193
|
+
|
|
194
|
+
For "Good Omens" with two authors, this produces:
|
|
195
|
+
|
|
196
|
+
```mermaid
|
|
197
|
+
graph TB
|
|
198
|
+
subgraph Comprehension["Resulting Comprehension"]
|
|
199
|
+
direction TB
|
|
200
|
+
subgraph Books["Book"]
|
|
201
|
+
b3["<b>Book_3</b><br/>Title: Good Omens"]
|
|
202
|
+
end
|
|
203
|
+
subgraph Authors["Author"]
|
|
204
|
+
a1["<b>Author_TerryPratchett</b><br/>Name: Terry Pratchett"]
|
|
205
|
+
a2["<b>Author_NeilGaiman</b><br/>Name: Neil Gaiman"]
|
|
206
|
+
end
|
|
207
|
+
subgraph Joins["BookAuthorJoin"]
|
|
208
|
+
j1["<b>BAJ_A_TerryPratchett_B_3</b>"]
|
|
209
|
+
j2["<b>BAJ_A_NeilGaiman_B_3</b>"]
|
|
210
|
+
end
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
j1 -. "GUIDBook" .-> b3
|
|
214
|
+
j1 -. "GUIDAuthor" .-> a1
|
|
215
|
+
j2 -. "GUIDBook" .-> b3
|
|
216
|
+
j2 -. "GUIDAuthor" .-> a2
|
|
217
|
+
|
|
218
|
+
style Comprehension fill:#fff8e1,stroke:#ffcc80,color:#333
|
|
219
|
+
style Books fill:#fff3e0,stroke:#ffa726,color:#333
|
|
220
|
+
style Authors fill:#fff3e0,stroke:#ffa726,color:#333
|
|
221
|
+
style Joins fill:#fff3e0,stroke:#ffa726,color:#333
|
|
222
|
+
style b3 fill:#fff,stroke:#ffcc80,color:#333
|
|
223
|
+
style a1 fill:#fff,stroke:#ffcc80,color:#333
|
|
224
|
+
style a2 fill:#fff,stroke:#ffcc80,color:#333
|
|
225
|
+
style j1 fill:#fff,stroke:#ffcc80,color:#333
|
|
226
|
+
style j2 fill:#fff,stroke:#ffcc80,color:#333
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
The join records contain `GUIDBook` and `GUIDAuthor` fields whose values match the GUIDs of the Book and Author entities. When the integration adapter pushes these records to Meadow, it resolves each GUID cross-reference to the corresponding numeric ID, producing proper foreign key relationships in the database.
|
|
230
|
+
|
|
231
|
+
### Merging Across Sources
|
|
232
|
+
|
|
233
|
+
When the same entities have data spread across multiple source files, comprehension merging combines them by GUID. The `comprehensionintersect` command (or `/1.0/Comprehension/Intersect` endpoint) takes two comprehensions and merges records with matching GUIDs:
|
|
234
|
+
|
|
235
|
+
```json
|
|
236
|
+
// Primary: population data
|
|
237
|
+
{
|
|
238
|
+
"Neighborhood": {
|
|
239
|
+
"SEATTLE_BALLARD": { "GUIDNeighborhood": "SEATTLE_BALLARD", "Name": "Ballard", "Population": 50000 }
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// Secondary: housing data
|
|
244
|
+
{
|
|
245
|
+
"Neighborhood": {
|
|
246
|
+
"SEATTLE_BALLARD": { "GUIDNeighborhood": "SEATTLE_BALLARD", "MedianHomePrice": 750000 }
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// Merged result
|
|
251
|
+
{
|
|
252
|
+
"Neighborhood": {
|
|
253
|
+
"SEATTLE_BALLARD": { "GUIDNeighborhood": "SEATTLE_BALLARD", "Name": "Ballard", "Population": 50000, "MedianHomePrice": 750000 }
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
This is why deterministic GUID design matters — when two sources use the same GUID template for the same logical entity, their data merges cleanly.
|
|
259
|
+
|
|
260
|
+
## The Integration Adapter: GUID to Database ID
|
|
261
|
+
|
|
262
|
+
When comprehension records are pushed into Meadow through the integration adapter, a three-layer GUID transformation maps external identifiers to database IDs:
|
|
263
|
+
|
|
264
|
+
```mermaid
|
|
265
|
+
graph LR
|
|
266
|
+
ext["External GUID<br/><code>Book_1</code><br/><i>from comprehension</i>"]
|
|
267
|
+
mguid["Meadow GUID<br/><code>INTG-DEF-E-Book-Book_1</code><br/><i>prefixed, unique</i>"]
|
|
268
|
+
mid["Meadow ID<br/><code>42</code><br/><i>database primary key</i>"]
|
|
269
|
+
|
|
270
|
+
ext -- "adapter prefixing" --> mguid
|
|
271
|
+
mguid -- "upsert returns ID" --> mid
|
|
272
|
+
|
|
273
|
+
style ext fill:#f5f5f5,stroke:#bdbdbd,color:#333
|
|
274
|
+
style mguid fill:#fff3e0,stroke:#ffa726,color:#333
|
|
275
|
+
style mid fill:#fff3e0,stroke:#ff9800,color:#333
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
1. The external GUID from the comprehension (e.g. `Book_1`) gets a configurable prefix applied by the adapter, producing a Meadow GUID (e.g. `INTG-DEF-E-Book-Book_1`)
|
|
279
|
+
2. The adapter upserts the record to the Meadow API. The server returns the numeric database ID
|
|
280
|
+
3. The GUIDMap service tracks the bidirectional mapping: external GUID to Meadow GUID to database ID
|
|
281
|
+
|
|
282
|
+
This mapping persists across the entire integration run. When later entities reference `GUIDBook: "Book_1"`, the adapter looks up the mapping and resolves it to the correct numeric `IDBook` value. Entity integration order matters — referenced entities must be pushed before the entities that reference them.
|