@almadar/std 16.218.0 → 16.219.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. package/behaviors/lolo/infra/atoms/std-data-import.lolo +56 -5
  2. package/behaviors/lolo/infra/atoms/std-notification-center.lolo +18 -18
  3. package/behaviors/lolo/infra/atoms/std-notification-preferences.lolo +5 -5
  4. package/behaviors/lolo/infra/atoms/std-notify-on-event.lolo +17 -18
  5. package/behaviors/lolo/ui/core/atoms/std-gantt-schedule.lolo +215 -0
  6. package/behaviors/lolo/ui/core/atoms/ui-textarea.lolo +2 -2
  7. package/behaviors/lolo/ui/core/molecules/ui-gantt.lolo +169 -0
  8. package/behaviors/lolo/ui/game/atoms/ui-canvas-2d.lolo +2 -2
  9. package/behaviors/lolo/ui/game/atoms/ui-canvas.lolo +2 -2
  10. package/behaviors/lolo/ui/learning/atoms/ui-math-canvas.lolo +2 -2
  11. package/behaviors/registry/infra/atoms/std-data-import.orb +329 -6
  12. package/behaviors/registry/infra/atoms/std-notification-center.orb +41 -33
  13. package/behaviors/registry/infra/atoms/std-notification-preferences.orb +10 -17
  14. package/behaviors/registry/infra/atoms/std-notify-on-event.orb +198 -195
  15. package/behaviors/registry/ui/core/atoms/std-gantt-schedule.orb +723 -0
  16. package/behaviors/registry/ui/core/molecules/ui-gantt.orb +508 -0
  17. package/canonical-operators.json +46 -1
  18. package/dist/behaviors/exports-reader.js +3212 -2818
  19. package/dist/behaviors/functions/index.d.ts +238 -5
  20. package/dist/behaviors/functions/index.js +3207 -2819
  21. package/dist/behaviors/index.d.ts +1 -1
  22. package/dist/behaviors/index.js +3213 -2819
  23. package/dist/behaviors/query.js +3212 -2818
  24. package/dist/behaviors-embeddings.hash.json +3 -3
  25. package/dist/behaviors-embeddings.json +19 -7
  26. package/dist/behaviors-registry.json +1772 -127
  27. package/dist/index.d.ts +3 -3
  28. package/dist/index.js +3249 -2819
  29. package/dist/knob-embeddings.hash.json +3 -3
  30. package/dist/knob-embeddings.json +1 -1
  31. package/dist/modules/anim.d.ts +1 -1
  32. package/dist/modules/array.d.ts +1 -1
  33. package/dist/modules/async.d.ts +1 -1
  34. package/dist/modules/behavior.d.ts +1 -1
  35. package/dist/modules/browser.d.ts +1 -1
  36. package/dist/modules/composition.d.ts +1 -1
  37. package/dist/modules/contract.d.ts +1 -1
  38. package/dist/modules/core.d.ts +1 -1
  39. package/dist/modules/data.d.ts +1 -1
  40. package/dist/modules/ease.d.ts +1 -1
  41. package/dist/modules/format.d.ts +1 -1
  42. package/dist/modules/geo.d.ts +1 -1
  43. package/dist/modules/graph.d.ts +1 -1
  44. package/dist/modules/grid.d.ts +1 -1
  45. package/dist/modules/index.d.ts +2 -1
  46. package/dist/modules/index.js +30 -1
  47. package/dist/modules/integration.d.ts +1 -1
  48. package/dist/modules/json.d.ts +20 -0
  49. package/dist/modules/json.js +30 -0
  50. package/dist/modules/llm.d.ts +1 -1
  51. package/dist/modules/math.d.ts +1 -1
  52. package/dist/modules/memory.d.ts +1 -1
  53. package/dist/modules/nn.d.ts +1 -1
  54. package/dist/modules/noise.d.ts +1 -1
  55. package/dist/modules/object.d.ts +1 -1
  56. package/dist/modules/os.d.ts +1 -1
  57. package/dist/modules/path.d.ts +1 -1
  58. package/dist/modules/prob.d.ts +1 -1
  59. package/dist/modules/session.d.ts +1 -1
  60. package/dist/modules/str.d.ts +1 -1
  61. package/dist/modules/tensor.d.ts +1 -1
  62. package/dist/modules/time.d.ts +1 -1
  63. package/dist/modules/trace.d.ts +1 -1
  64. package/dist/modules/train.d.ts +1 -1
  65. package/dist/modules/validate.d.ts +1 -1
  66. package/dist/modules/vector.d.ts +1 -1
  67. package/dist/modules/workspace.d.ts +1 -1
  68. package/dist/registry/factory-signatures.json +1 -1
  69. package/dist/registry.d.ts +1 -1
  70. package/dist/registry.js +28 -0
  71. package/dist/{types-C-Yerb4-.d.ts → types-DpbKBJu8.d.ts} +3 -3
  72. package/i18n/ar.json +3 -1
  73. package/i18n/en.json +2 -0
  74. package/i18n/sl.json +3 -1
  75. package/package.json +2 -2
@@ -0,0 +1,169 @@
1
+ app ui-gantt "1.0.0"
2
+ "Gantt — view-only task schedule rendering rows as bars on a day axis. — UI factory (1:1 with the @almadar/ui gantt pattern)."
3
+ ;; @capabilities "gantt chart, project timeline, schedule view, task bars, dependency arrows, roadmap, milestone plan"
4
+ orbital GanttOrbital {
5
+ type GanttLoaded = Event { data : [GanttItem] } "Gantt rows finished loading; payload.data holds the collection"
6
+ type GanttLinksItem = {
7
+ from : string!
8
+ to : string!
9
+ }
10
+ type GanttError = {
11
+ message : string!
12
+ name : string
13
+ code : string
14
+ stack : string
15
+ }
16
+
17
+ entity GanttItem [runtime] {
18
+ id : string!
19
+ }
20
+
21
+ trait GanttRender -> @rebindable GanttItem [interaction, instance] {
22
+ initial: idle
23
+
24
+ state idle {
25
+ INIT -> idle when @config.selfFetch
26
+ (fetch GanttItem { emit: { success: GanttLoaded } })
27
+ (render-ui main {
28
+ tasks: @entity
29
+ links: @config.links
30
+ titleField: @config.titleField
31
+ startField: @config.startField
32
+ endField: @config.endField
33
+ durationField: @config.durationField
34
+ statusField: @config.statusField
35
+ groupField: @config.groupField
36
+ rangeStart: @config.rangeStart
37
+ rangeEnd: @config.rangeEnd
38
+ showToday: @config.showToday
39
+ dayWidth: @config.dayWidth
40
+ barClickEvent: @config.barClickEvent
41
+ className: @config.className
42
+ isLoading: @config.isLoading
43
+ error: @config.error
44
+ type: gantt
45
+ })
46
+ INIT -> idle when (not @config.selfFetch)
47
+ (render-ui main {
48
+ tasks: @entity
49
+ links: @config.links
50
+ titleField: @config.titleField
51
+ startField: @config.startField
52
+ endField: @config.endField
53
+ durationField: @config.durationField
54
+ statusField: @config.statusField
55
+ groupField: @config.groupField
56
+ rangeStart: @config.rangeStart
57
+ rangeEnd: @config.rangeEnd
58
+ showToday: @config.showToday
59
+ dayWidth: @config.dayWidth
60
+ barClickEvent: @config.barClickEvent
61
+ className: @config.className
62
+ isLoading: @config.isLoading
63
+ error: @config.error
64
+ type: gantt
65
+ })
66
+ GanttLoaded -> idle
67
+ (render-ui main {
68
+ tasks: @payload.data
69
+ links: @config.links
70
+ titleField: @config.titleField
71
+ startField: @config.startField
72
+ endField: @config.endField
73
+ durationField: @config.durationField
74
+ statusField: @config.statusField
75
+ groupField: @config.groupField
76
+ rangeStart: @config.rangeStart
77
+ rangeEnd: @config.rangeEnd
78
+ showToday: @config.showToday
79
+ dayWidth: @config.dayWidth
80
+ barClickEvent: @config.barClickEvent
81
+ className: @config.className
82
+ isLoading: @config.isLoading
83
+ error: @config.error
84
+ type: gantt
85
+ })
86
+ }
87
+
88
+ emits {
89
+ @config.barClickEvent -> external {
90
+ id : string!
91
+ }
92
+ @description "Event emitted when a bar is clicked: UI:{barClickEvent} with { id }"
93
+ @tier "essential"
94
+ GanttLoaded
95
+ @description "Gantt rows finished loading; payload.data holds the collection."
96
+ @synonyms "loaded, fetched, retrieved"
97
+ @tier "essential"
98
+ }
99
+
100
+ config {
101
+ links : [GanttLinksItem] = []
102
+ @label "Links"
103
+ @description "Dependency arrows between task ids"
104
+ @tier "presentation"
105
+ titleField : string = title
106
+ @label "Title Field"
107
+ @description "Row field holding the bar label. Defaults to `title`."
108
+ @tier "presentation"
109
+ startField : string = start
110
+ @label "Start Field"
111
+ @description "Row field holding the start timestamp (ISO or epoch). Defaults to `start`."
112
+ @tier "presentation"
113
+ endField : string = end
114
+ @label "End Field"
115
+ @description "Row field holding the end timestamp (ISO or epoch). Defaults to `end`. When absent, `durationField` (days) is used instead."
116
+ @tier "presentation"
117
+ durationField : string
118
+ @label "Duration Field"
119
+ @description "Row field holding the task length in days, used when the row has no end."
120
+ @tier "presentation"
121
+ statusField : string = status
122
+ @label "Status Field"
123
+ @description "Row field holding the bar status (drives bar colour). Defaults to `status`."
124
+ @tier "presentation"
125
+ groupField : string
126
+ @label "Group Field"
127
+ @description "Row field rows are grouped under header rows by. Empty (default) = flat list."
128
+ @tier "presentation"
129
+ rangeStart : date = "2024-01-01"
130
+ @label "Range Start"
131
+ @description "First visible day (ISO or Date). Defaults to 2 days before the earliest task."
132
+ @tier "presentation"
133
+ rangeEnd : date = "2024-01-01"
134
+ @label "Range End"
135
+ @description "Last visible day (ISO or Date). Defaults to 2 days after the latest task end."
136
+ @tier "presentation"
137
+ showToday : boolean = true
138
+ @label "Show Today"
139
+ @description "Paint the today marker line when today falls inside the range (default true)."
140
+ @tier "presentation"
141
+ dayWidth : number = 28
142
+ @label "Day Width"
143
+ @description "Pixels per day on the axis (default 28)"
144
+ @tier "presentation"
145
+ barClickEvent : event = "BAR_CLICK"
146
+ @label "Bar Click Event"
147
+ @description "Event emitted when a bar is clicked: UI:{barClickEvent} with { id }"
148
+ @tier "presentation"
149
+ className : string
150
+ @label "Class Name"
151
+ @description "Additional CSS classes"
152
+ @tier "presentation"
153
+ isLoading : boolean = false
154
+ @label "Is Loading"
155
+ @description "Loading state"
156
+ @tier "presentation"
157
+ error : GanttError
158
+ @label "Error"
159
+ @description "Error state"
160
+ @tier "presentation"
161
+ selfFetch : boolean = true
162
+ @label "Fetch its own rows on mount?"
163
+ @description "true (default) = standalone mount self-populates on INIT. false = a composing atom owns the fetch and routes its loaded event here — suppresses the wrapper's own un-hydrated fetch so it cannot race the composer's."
164
+ @tier "internal"
165
+ }
166
+ }
167
+
168
+ page "/gantt" as GanttPage -> GanttRender
169
+ }
@@ -137,11 +137,11 @@ orbital Canvas2dOrbital {
137
137
  @tier "presentation"
138
138
  keyMap : Map string string = { item: "INIT" }
139
139
  @label "Key Map"
140
- @description "Maps a keydown `e.code` → the board's SEMANTIC event (device-agnostic input)."
140
+ @description "Maps a keydown `e.code` — optionally prefixed `Mod+` (⌘/Ctrl), `Shift+`, `Alt+` in that order — to the board's SEMANTIC event (device-agnostic input), emitted as `UI:{event}`; keystrokes inside inputs/textareas never route."
141
141
  @tier "presentation"
142
142
  keyUpMap : Map string string = { item: "INIT" }
143
143
  @label "Key Up Map"
144
- @description "Maps a keyup `e.code` → the board's SEMANTIC event."
144
+ @description "Maps a keyup `e.code` — optionally prefixed `Mod+` (⌘/Ctrl), `Shift+`, `Alt+` in that order — to the board's SEMANTIC event, emitted as `UI:{event}`; keystrokes inside inputs/textareas never route."
145
145
  @tier "presentation"
146
146
  editable : boolean = false
147
147
  @label "Editable"
@@ -274,11 +274,11 @@ orbital CanvasOrbital {
274
274
  @tier "presentation"
275
275
  keyMap : Map string string = { item: "INIT" }
276
276
  @label "Key Map"
277
- @description "keyMap prop"
277
+ @description "Maps a keydown `e.code` — optionally prefixed `Mod+` (⌘/Ctrl), `Shift+`, `Alt+` in that order — to the board's SEMANTIC event (device-agnostic input), emitted as `UI:{event}`; keystrokes inside inputs/textareas never route."
278
278
  @tier "presentation"
279
279
  keyUpMap : Map string string = { item: "INIT" }
280
280
  @label "Key Up Map"
281
- @description "keyUpMap prop"
281
+ @description "Maps a keyup `e.code` — optionally prefixed `Mod+` (⌘/Ctrl), `Shift+`, `Alt+` in that order — to the board's SEMANTIC event, emitted as `UI:{event}`; keystrokes inside inputs/textareas never route."
282
282
  @tier "presentation"
283
283
  editable : boolean
284
284
  @label "Editable"
@@ -409,11 +409,11 @@ orbital MathCanvasOrbital {
409
409
  @tier "presentation"
410
410
  keyMap : Map string string = { item: "INIT" }
411
411
  @label "Key Map"
412
- @description "Maps a keydown `e.code` → the board's SEMANTIC event (device-agnostic input), emitted as `UI:{event}` — same contract as the game canvas keyMap."
412
+ @description "Maps a keydown `e.code` — optionally prefixed `Mod+` (⌘/Ctrl), `Shift+`, `Alt+` in that order — to the board's SEMANTIC event (device-agnostic input), emitted as `UI:{event}` — same contract as the game canvas keyMap; keystrokes inside inputs/textareas never route."
413
413
  @tier "presentation"
414
414
  keyUpMap : Map string string = { item: "INIT" }
415
415
  @label "Key Up Map"
416
- @description "Maps a keyup `e.code` → the board's SEMANTIC event."
416
+ @description "Maps a keyup `e.code` — optionally prefixed `Mod+` (⌘/Ctrl), `Shift+`, `Alt+` in that order — to the board's SEMANTIC event, emitted as `UI:{event}`; keystrokes inside inputs/textareas never route."
417
417
  @tier "presentation"
418
418
  isLoading : boolean
419
419
  @label "Is Loading"
@@ -1,5 +1,5 @@
1
1
  {
2
- "description": "std-data-import — staged CSV import run, source-agnostic: upload → staged review → confirm → handoff. UPLOAD_CSV opens a run and persists an ImportBatch row (status uploaded, fieldMappingJson recorded verbatim for provenance); the host's file UI parses the CSV and answers with STAGE_ROWS, one JSON string per raw row, which the atom bulk-persists into import_rows against the batch id and presents as a staged review (row/error counts + Confirm/Discard). CONFIRM_IMPORT marks the batch confirmed and emits IMPORT_CONFIRMED { batchId, sourceName, rowCount } — the mapping+persist half is the CONSUMER organism's: it listens for IMPORT_CONFIRMED, fetches import_rows where batchId matches, maps each rowJson through its own field mapping, and persists into its own target entity (cross-atom persist into a consumer's entity is not expressible from here). This is deliberately the CSV-file path: the atom never calls a source service — sourceName/fieldMappingJson/targetEntity/historyWindowDays are declarative consumer configs, and any persist failure marks the batch row failed and fires IMPORT_FAILED, never a silent swallow. Compose with std-import for the file-picker menu.",
2
+ "description": "std-data-import — staged CSV import run, source-agnostic: upload → staged review → confirm → parse+map → handoff. UPLOAD_CSV opens a run and persists an ImportBatch row (status uploaded, fieldMappingJson recorded verbatim for provenance); the host's file UI parses the CSV and answers with STAGE_ROWS, one JSON string per raw row, which the atom bulk-persists into import_rows against the batch id and presents as a staged review (row/error counts + Confirm/Discard). CONFIRM_IMPORT marks the batch confirmed, emits IMPORT_CONFIRMED { batchId, sourceName, rowCount }, and fetches the batch's staged rows; on fetch success the atom parses each rowJson via json/parse, remaps keys through the batch's mappingJson, and emits IMPORT_ROWS_PARSED { batchId, rows, failed } — the persist half is the CONSUMER organism's: it listens for IMPORT_ROWS_PARSED and persists the mapped rows into its own target entity (cross-atom persist into a consumer's entity is not expressible from here). This is deliberately the CSV-file path: the atom never calls a source service — sourceName/fieldMappingJson/targetEntity/historyWindowDays are declarative consumer configs, and any persist failure marks the batch row failed and fires IMPORT_FAILED, never a silent swallow. Compose with std-import for the file-picker menu.",
3
3
  "ledger": {
4
4
  "entries": {
5
5
  "ent_01M2HEYJ1VH5AKFRTFBSJVKWA9": {
@@ -117,6 +117,24 @@
117
117
  "parent": "trt_01M2HEYJ1V70M3EEV69G6ZRGF9",
118
118
  "renames": []
119
119
  },
120
+ "evt_01M2M6Z8VSNDXCMS9C6CJSNFMV": {
121
+ "bakedName": "IMPORT_ROWS_PARSED",
122
+ "curName": "IMPORT_ROWS_PARSED",
123
+ "id": "evt_01M2M6Z8VSNDXCMS9C6CJSNFMV",
124
+ "kind": "event",
125
+ "owner": "workspace",
126
+ "parent": "trt_01M2HEYJ1V70M3EEV69G6ZRGF9",
127
+ "renames": []
128
+ },
129
+ "evt_01M2M6Z8VSYMZZH7DW4QZMTW1T": {
130
+ "bakedName": "IMPORT_ROWS_FETCHED",
131
+ "curName": "IMPORT_ROWS_FETCHED",
132
+ "id": "evt_01M2M6Z8VSYMZZH7DW4QZMTW1T",
133
+ "kind": "event",
134
+ "owner": "workspace",
135
+ "parent": "trt_01M2HEYJ1V70M3EEV69G6ZRGF9",
136
+ "renames": []
137
+ },
120
138
  "orb_01M2HEYJ1VYFXY2G37WFB431EY": {
121
139
  "bakedName": "DataImportOrbital",
122
140
  "curName": "DataImportOrbital",
@@ -230,7 +248,7 @@
230
248
  },
231
249
  {
232
250
  "default": "{}",
233
- "description": "Source-column → target-field mapping (JSON string), recorded verbatim from config for provenance; the consumer's mapping layer applies it.",
251
+ "description": "Source-column → target-field mapping (JSON string), recorded verbatim from config for provenance; the atom applies it when parsing staged rows on confirm.",
234
252
  "name": "mappingJson",
235
253
  "synonyms": "field mapping, column mapping, mapping",
236
254
  "type": "string"
@@ -270,7 +288,7 @@
270
288
  "config": {
271
289
  "fieldMappingJson": {
272
290
  "default": "{}",
273
- "description": "JSON object mapping source CSV column names to target field names. Stored verbatim on the batch row (mappingJson) so the consumer's mapping layer applies the same mapping when it persists staged rows into the target entity.",
291
+ "description": "JSON object mapping source CSV column names to target field names. Stored verbatim on the batch row (mappingJson); the atom applies it when it parses staged rows into IMPORT_ROWS_PARSED, so the consumer persists already-mapped rows into the target entity.",
274
292
  "label": "Field mapping (JSON)",
275
293
  "synonyms": "field mapping, column mapping, map columns, remap fields, csv columns",
276
294
  "tier": "domain",
@@ -310,6 +328,14 @@
310
328
  "kind": "emit",
311
329
  "resource": "IMPORT_FAILED"
312
330
  },
331
+ {
332
+ "kind": "emit",
333
+ "resource": "IMPORT_ROWS_PARSED"
334
+ },
335
+ {
336
+ "kind": "fetch",
337
+ "resource": "ImportRow"
338
+ },
313
339
  {
314
340
  "kind": "persist",
315
341
  "resource": "ImportBatch"
@@ -385,7 +411,7 @@
385
411
  "tier": "secondary"
386
412
  },
387
413
  {
388
- "description": "The user confirmed the staged review and the batch row is persisted as confirmed. Consumers listen, fetch import_rows where batchId matches, map each rowJson through their field mapping, and persist into their own target entity.",
414
+ "description": "The user confirmed the staged review and the batch row is persisted as confirmed. The atom then fetches the batch's staged rows, parses and key-maps them, and emits IMPORT_ROWS_PARSED for the consumer's persist half.",
389
415
  "event": "IMPORT_CONFIRMED",
390
416
  "eventId": "evt_01M2HEYJ1VS7SGS9STXQGS2V2J",
391
417
  "payloadSchema": [
@@ -407,6 +433,61 @@
407
433
  "synonyms": "import confirmed, commit import, accept staged rows, approve import",
408
434
  "tier": "domain"
409
435
  },
436
+ {
437
+ "description": "import_rows collection-fetch success on confirm — payload carries every staged row flat; the atom filters to @entity.id by batchId in the IMPORT_ROWS_PARSED arm (self-loop only)",
438
+ "event": "IMPORT_ROWS_FETCHED",
439
+ "eventId": "evt_01M2M6Z8VSYMZZH7DW4QZMTW1T",
440
+ "payloadSchema": [
441
+ {
442
+ "entity": "ImportRow",
443
+ "name": "data",
444
+ "properties": [
445
+ {
446
+ "name": "id",
447
+ "required": true,
448
+ "type": "string"
449
+ },
450
+ {
451
+ "name": "batchId",
452
+ "type": "string"
453
+ },
454
+ {
455
+ "name": "rowJson",
456
+ "type": "string"
457
+ },
458
+ {
459
+ "name": "status",
460
+ "type": "string"
461
+ }
462
+ ],
463
+ "type": "[object]"
464
+ }
465
+ ],
466
+ "tier": "secondary"
467
+ },
468
+ {
469
+ "description": "Confirmed rows parsed and key-mapped: each staged row's rowJson was parsed via json/parse and remapped through the batch's mappingJson; failed counts rows whose rowJson did not parse to an object (surfaced, never dropped). The consumer persists rows into its own target entity and marks them imported.",
470
+ "event": "IMPORT_ROWS_PARSED",
471
+ "eventId": "evt_01M2M6Z8VSNDXCMS9C6CJSNFMV",
472
+ "payloadSchema": [
473
+ {
474
+ "name": "batchId",
475
+ "required": true,
476
+ "type": "string"
477
+ },
478
+ {
479
+ "name": "failed",
480
+ "type": "number"
481
+ },
482
+ {
483
+ "name": "rows",
484
+ "type": "[Map<string,scalar>]"
485
+ }
486
+ ],
487
+ "scope": "external",
488
+ "synonyms": "rows parsed, rows mapped, import rows ready",
489
+ "tier": "domain"
490
+ },
410
491
  {
411
492
  "description": "The run failed (persist failure or empty staging); error carries the detail and the batch row was updated to failed",
412
493
  "event": "IMPORT_FAILED",
@@ -533,13 +614,46 @@
533
614
  "tier": "secondary"
534
615
  },
535
616
  {
536
- "description": "The user approved the staged review; the batch is marked confirmed and IMPORT_CONFIRMED fires for the consumer's mapping+persist half.",
617
+ "description": "The user approved the staged review; the batch is marked confirmed, IMPORT_CONFIRMED fires, and the atom parses+maps the staged rows into IMPORT_ROWS_PARSED for the consumer's persist half.",
537
618
  "id": "evt_01M2HEYJ1VBJXS0KEXTX9QWJM5",
538
619
  "key": "CONFIRM_IMPORT",
539
620
  "name": "Confirm Import",
540
621
  "synonyms": "confirm import, approve staged rows, accept import",
541
622
  "tier": "domain"
542
623
  },
624
+ {
625
+ "description": "import_rows collection-fetch success on confirm — payload carries every staged row flat; the atom filters to @entity.id by batchId in the IMPORT_ROWS_PARSED arm (self-loop only)",
626
+ "id": "evt_01M2M6Z8VSYMZZH7DW4QZMTW1T",
627
+ "key": "IMPORT_ROWS_FETCHED",
628
+ "name": "Import Rows Fetched",
629
+ "payloadSchema": [
630
+ {
631
+ "entity": "ImportRow",
632
+ "name": "data",
633
+ "properties": [
634
+ {
635
+ "name": "id",
636
+ "required": true,
637
+ "type": "string"
638
+ },
639
+ {
640
+ "name": "batchId",
641
+ "type": "string"
642
+ },
643
+ {
644
+ "name": "rowJson",
645
+ "type": "string"
646
+ },
647
+ {
648
+ "name": "status",
649
+ "type": "string"
650
+ }
651
+ ],
652
+ "type": "[object]"
653
+ }
654
+ ],
655
+ "tier": "secondary"
656
+ },
543
657
  {
544
658
  "description": "Returns the atom to idle from a terminal state",
545
659
  "id": "evt_01M2HEYJ1VEEH4DZZ4ETPGP44F",
@@ -548,7 +662,7 @@
548
662
  "tier": "internal"
549
663
  },
550
664
  {
551
- "description": "The user confirmed the staged review and the batch row is persisted as confirmed. Consumers listen, fetch import_rows where batchId matches, map each rowJson through their field mapping, and persist into their own target entity.",
665
+ "description": "The user confirmed the staged review and the batch row is persisted as confirmed. The atom then fetches the batch's staged rows, parses and key-maps them, and emits IMPORT_ROWS_PARSED for the consumer's persist half.",
552
666
  "id": "evt_01M2HEYJ1VS7SGS9STXQGS2V2J",
553
667
  "key": "IMPORT_CONFIRMED",
554
668
  "name": "Import Confirmed",
@@ -570,6 +684,29 @@
570
684
  "synonyms": "import confirmed, commit import, accept staged rows, approve import",
571
685
  "tier": "domain"
572
686
  },
687
+ {
688
+ "description": "Confirmed rows parsed and key-mapped: each staged row's rowJson was parsed via json/parse and remapped through the batch's mappingJson; failed counts rows whose rowJson did not parse to an object (surfaced, never dropped). The consumer persists rows into its own target entity and marks them imported.",
689
+ "id": "evt_01M2M6Z8VSNDXCMS9C6CJSNFMV",
690
+ "key": "IMPORT_ROWS_PARSED",
691
+ "name": "Import Rows Parsed",
692
+ "payloadSchema": [
693
+ {
694
+ "name": "batchId",
695
+ "required": true,
696
+ "type": "string"
697
+ },
698
+ {
699
+ "name": "failed",
700
+ "type": "number"
701
+ },
702
+ {
703
+ "name": "rows",
704
+ "type": "[Map<string,scalar>]"
705
+ }
706
+ ],
707
+ "synonyms": "rows parsed, rows mapped, import rows ready",
708
+ "tier": "domain"
709
+ },
573
710
  {
574
711
  "description": "The run failed (persist failure or empty staging); error carries the detail and the batch row was updated to failed",
575
712
  "id": "evt_01M2HEYJ1VWSGJ1ZZXZSZ4Q023",
@@ -1005,6 +1142,16 @@
1005
1142
  "rowCount": "@entity.rowCount",
1006
1143
  "sourceName": "@entity.sourceName"
1007
1144
  }
1145
+ ],
1146
+ [
1147
+ "fetch",
1148
+ "ImportRow",
1149
+ {
1150
+ "emit": {
1151
+ "failure": "IMPORT_PERSIST_FAILED",
1152
+ "success": "IMPORT_ROWS_FETCHED"
1153
+ }
1154
+ }
1008
1155
  ]
1009
1156
  ],
1010
1157
  "event": "CONFIRM_IMPORT",
@@ -1085,6 +1232,176 @@
1085
1232
  "from": "staged",
1086
1233
  "to": "idle"
1087
1234
  },
1235
+ {
1236
+ "effects": [
1237
+ [
1238
+ "emit",
1239
+ "IMPORT_ROWS_PARSED",
1240
+ {
1241
+ "batchId": "@entity.id",
1242
+ "failed": [
1243
+ "array/len",
1244
+ [
1245
+ "array/filter",
1246
+ "@payload.data",
1247
+ [
1248
+ "fn",
1249
+ "row",
1250
+ [
1251
+ "and",
1252
+ [
1253
+ "=",
1254
+ [
1255
+ "object/get",
1256
+ "@row",
1257
+ "batchId"
1258
+ ],
1259
+ "@entity.id"
1260
+ ],
1261
+ [
1262
+ "not",
1263
+ [
1264
+ "validate/object",
1265
+ [
1266
+ "json/parse",
1267
+ [
1268
+ "object/get",
1269
+ "@row",
1270
+ "rowJson"
1271
+ ]
1272
+ ]
1273
+ ]
1274
+ ]
1275
+ ]
1276
+ ]
1277
+ ]
1278
+ ],
1279
+ "rows": [
1280
+ "array/map",
1281
+ [
1282
+ "array/filter",
1283
+ "@payload.data",
1284
+ [
1285
+ "fn",
1286
+ "row",
1287
+ [
1288
+ "and",
1289
+ [
1290
+ "=",
1291
+ [
1292
+ "object/get",
1293
+ "@row",
1294
+ "batchId"
1295
+ ],
1296
+ "@entity.id"
1297
+ ],
1298
+ [
1299
+ "validate/object",
1300
+ [
1301
+ "json/parse",
1302
+ [
1303
+ "object/get",
1304
+ "@row",
1305
+ "rowJson"
1306
+ ]
1307
+ ]
1308
+ ]
1309
+ ]
1310
+ ]
1311
+ ],
1312
+ [
1313
+ "fn",
1314
+ "row",
1315
+ [
1316
+ "object/mapKeys",
1317
+ [
1318
+ "json/parse",
1319
+ [
1320
+ "object/get",
1321
+ "@row",
1322
+ "rowJson"
1323
+ ]
1324
+ ],
1325
+ [
1326
+ "fn",
1327
+ "k",
1328
+ [
1329
+ "if",
1330
+ [
1331
+ "object/has",
1332
+ [
1333
+ "json/parse",
1334
+ "@entity.mappingJson"
1335
+ ],
1336
+ "@k"
1337
+ ],
1338
+ [
1339
+ "object/get",
1340
+ [
1341
+ "json/parse",
1342
+ "@entity.mappingJson"
1343
+ ],
1344
+ "@k"
1345
+ ],
1346
+ "@k"
1347
+ ]
1348
+ ]
1349
+ ]
1350
+ ]
1351
+ ]
1352
+ }
1353
+ ]
1354
+ ],
1355
+ "event": "IMPORT_ROWS_FETCHED",
1356
+ "eventId": "evt_01M2M6Z8VSYMZZH7DW4QZMTW1T",
1357
+ "from": "confirmed",
1358
+ "to": "confirmed"
1359
+ },
1360
+ {
1361
+ "effects": [
1362
+ [
1363
+ "set",
1364
+ "@entity.status",
1365
+ "failed"
1366
+ ],
1367
+ [
1368
+ "set",
1369
+ "@entity.errorCount",
1370
+ 1.0
1371
+ ],
1372
+ [
1373
+ "persist",
1374
+ "update",
1375
+ "ImportBatch",
1376
+ {
1377
+ "errorCount": 1.0,
1378
+ "id": "@entity.id",
1379
+ "status": "failed"
1380
+ }
1381
+ ],
1382
+ [
1383
+ "emit",
1384
+ "IMPORT_FAILED",
1385
+ {
1386
+ "batchId": "@entity.id",
1387
+ "error": "@payload.error"
1388
+ }
1389
+ ],
1390
+ [
1391
+ "render-ui",
1392
+ "main",
1393
+ {
1394
+ "message": "@payload.error",
1395
+ "type": "alert",
1396
+ "variant": "error"
1397
+ }
1398
+ ]
1399
+ ],
1400
+ "event": "IMPORT_PERSIST_FAILED",
1401
+ "eventId": "evt_01M2HEYJ1V7M6C8DP88H0Z23F1",
1402
+ "from": "confirmed",
1403
+ "to": "confirmed"
1404
+ },
1088
1405
  {
1089
1406
  "effects": [
1090
1407
  [
@@ -1119,6 +1436,12 @@
1119
1436
  "from": "confirmed",
1120
1437
  "to": "confirmed"
1121
1438
  },
1439
+ {
1440
+ "event": "IMPORT_ROWS_PARSED",
1441
+ "eventId": "evt_01M2M6Z8VSNDXCMS9C6CJSNFMV",
1442
+ "from": "confirmed",
1443
+ "to": "confirmed"
1444
+ },
1122
1445
  {
1123
1446
  "effects": [
1124
1447
  [