@things-factory/station 10.1.42 → 10.1.44

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@things-factory/station",
3
- "version": "10.1.42",
3
+ "version": "10.1.44",
4
4
  "main": "dist-server/index.js",
5
5
  "browser": "dist-client/index.js",
6
6
  "things-factory": true,
@@ -33,12 +33,12 @@
33
33
  "@hatiolab/things-scene": "^10.0.0",
34
34
  "@operato/i18n": "^10.0.0",
35
35
  "@operato/styles": "^10.0.0",
36
- "@things-factory/auth-base": "^10.1.40",
37
- "@things-factory/board-service": "^10.1.42",
38
- "@things-factory/ops-master": "^10.1.41",
39
- "@things-factory/shell": "^10.1.40",
40
- "@things-factory/worklist": "^10.1.42",
36
+ "@things-factory/auth-base": "^10.1.44",
37
+ "@things-factory/board-service": "^10.1.44",
38
+ "@things-factory/ops-master": "^10.1.44",
39
+ "@things-factory/shell": "^10.1.44",
40
+ "@things-factory/worklist": "^10.1.44",
41
41
  "i18next": "^24.2.1"
42
42
  },
43
- "gitHead": "982aa01403b979c4e348fd207a3fb4d1a7b5f521"
43
+ "gitHead": "7c95c4e75e155e51c714409e865d96f317232c23"
44
44
  }
@@ -1,9 +1,9 @@
1
- /** The closed set of context axis names a station screen may read. Declared once; see the .js. */
1
+ /** The machine's own context axes — the core. A product adds its own by declaration; see the .js. */
2
2
  export const STATION_AXES: readonly string[]
3
3
  /** The axes any product's bench answers - the module owns them (ADR-0083 ruling ①). */
4
4
  export const STATION_CORE_AXES: readonly string[]
5
- /** operato-plant's own axes, held here until plant declares them itself. */
6
- export const STATION_TRANSITIONAL_AXES: readonly string[]
5
+ /** What is wrong with a product's axis declaration — bad shape, already the machine's, repeated. Empty when right. */
6
+ export function wrongDeclaredAxes(axes?: readonly string[]): string[]
7
7
  /** `declared` is what the product added in `StationProduct.axes`. */
8
8
  export function isStationAxis(axis: string, declared?: readonly string[]): boolean
9
9
  export function nearestAxes(said: string, howMany?: number, declared?: readonly string[]): string[]
@@ -35,10 +35,15 @@
35
35
  /*
36
36
  * ── The core, and what a product adds (ADR-0083 ruling ①, 2026-09-24) ─────────────────────────
37
37
  * The core is what any product's bench answers: where the station stands and when, who scanned in,
38
- * where it is, the task in hand, how many this window, and the order at its plainest. The module
39
- * owns it. Anything else is a product's word and the product declares it (`StationProduct.axes`);
40
- * the closed set a screen is checked against is the core, the transitional list below, and that
41
- * declaration.
38
+ * where it is, the task in hand, how many this window, the order at its plainest, and whether it is
39
+ * paused. The module owns it. Anything else is a product's word and the product declares it — on
40
+ * the server in `StationProduct.axes`, in the browser with `registerStationAxes`, both from one list
41
+ * the product keeps. The closed set a screen is checked against is the core and that declaration.
42
+ *
43
+ * ── The transitional list is gone (2026-09-24, ruling on moving it) ──────────────────────────
44
+ * operato-plant's thirty-two words sat here until plant could declare them. Plant declares them now.
45
+ * Three of them came back to the core instead: `paused.at` · `paused.reason` · `paused.detail`. The
46
+ * pause ledger is the machine's (3-da condition 4), so the axes that show it are too.
42
47
  */
43
48
  const STATION_CORE_AXES = [
44
49
  'station.equipment',
@@ -63,52 +68,14 @@ const STATION_CORE_AXES = [
63
68
  'order.status',
64
69
  'task.name',
65
70
  'task.since',
66
- 'window.tasks'
67
- ]
68
-
69
- /*
70
- * ── Transitional: operato-plant's own words, still held here ─────────────────────────────────
71
- * These are a factory's - its line, its directives and routes, good and scrap, its equipment state.
72
- * They stay in the module until plant declares them in its own `axes` (ruling ①: taking them out
73
- * first would break plant's screens). Until then a second product's authoring tool offers them too,
74
- * which the ruling accepts as noise. When plant has moved them this list is empty and goes.
75
- */
76
- const STATION_TRANSITIONAL_AXES = [
77
- 'productionLine.name',
78
- 'order.material',
79
- 'order.directive',
80
- 'order.directiveVersion',
81
- 'order.route',
82
- 'materialDefinition.name',
83
- 'materialDefinition.description',
84
- 'materialDefinition.materialClassIds',
85
- 'route.key',
86
- 'route.steps',
87
- 'route.stepNumber',
88
- 'route.stepCount',
89
- 'route.nextStep',
90
- 'directive.name',
91
- 'directive.version',
92
- 'directive.description',
93
- 'directive.steps',
94
- 'directive.stepCount',
95
- 'operation.name',
96
- 'operation.description',
97
- 'operation.duration',
71
+ 'window.tasks',
98
72
  'paused.at',
99
73
  'paused.reason',
100
- 'paused.detail',
101
- 'window.good',
102
- 'window.scrap',
103
- 'state.status',
104
- 'state.knownUntil',
105
- 'state.reason',
106
- 'staged.count',
107
- 'candidates.count',
108
- 'maintenance.count'
74
+ 'paused.detail'
109
75
  ]
110
76
 
111
- const STATION_AXES = [...STATION_CORE_AXES, ...STATION_TRANSITIONAL_AXES]
77
+ /** The machine's own axes. A product's are added to these, never mixed into them. */
78
+ const STATION_AXES = STATION_CORE_AXES
112
79
 
113
80
  const KNOWN = new Set(STATION_AXES)
114
81
 
@@ -117,11 +84,25 @@ function isStationAxis(axis, declared = []) {
117
84
  return KNOWN.has(axis) || declared.includes(axis)
118
85
  }
119
86
 
87
+ /*
88
+ * What is wrong with a product's axis declaration — **one rule, for the server and the browser.**
89
+ *
90
+ * A misspelt axis would pass every screen that reads it and stay blank on the bench; one the machine
91
+ * already has is two owners of one name; a repeat is harmless but says the list was not read. The
92
+ * server refuses at boot. The browser has to refuse the same things, or it offers a name the server
93
+ * will turn away and says nothing (ruling on moving the axes, 2026-09-24).
94
+ */
95
+ const AXIS_SHAPE = /^[a-z][A-Za-z0-9]*\.[a-z][A-Za-z0-9]*$/
96
+
97
+ function wrongDeclaredAxes(axes = []) {
98
+ return axes.filter((axis, at) => !AXIS_SHAPE.test(axis ?? '') || KNOWN.has(axis) || axes.indexOf(axis) !== at)
99
+ }
100
+
120
101
  /*
121
102
  * The closest names to one that was not recognised.
122
103
  *
123
104
  * A screen that says «no such axis» and stops has told the author they are wrong without telling
124
- * them what is right, and with fifty-five names that is most of the work left undone. One
105
+ * them what is right, and with dozens of names that is most of the work left undone. One
125
106
  * character out is the common case, so it is the case worth answering.
126
107
  */
127
108
  function nearestAxes(said, howMany = 3, declared = []) {
@@ -139,7 +120,7 @@ function nearestAxes(said, howMany = 3, declared = []) {
139
120
  .map(one => one.axis)
140
121
  }
141
122
 
142
- /* Levenshtein, two rows. Fifty-five short strings — nothing here needs to be cleverer. */
123
+ /* Levenshtein, two rows. A few dozen short strings — nothing here needs to be cleverer. */
143
124
  function distance(a, b) {
144
125
  let previous = Array.from({ length: b.length + 1 }, (_, at) => at)
145
126
 
@@ -158,6 +139,6 @@ function distance(a, b) {
158
139
 
159
140
  exports.STATION_AXES = STATION_AXES
160
141
  exports.STATION_CORE_AXES = STATION_CORE_AXES
161
- exports.STATION_TRANSITIONAL_AXES = STATION_TRANSITIONAL_AXES
142
+ exports.wrongDeclaredAxes = wrongDeclaredAxes
162
143
  exports.isStationAxis = isStationAxis
163
144
  exports.nearestAxes = nearestAxes