@things-factory/worklist 10.1.40 → 10.1.41

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 (38) hide show
  1. package/dist-client/components/activity-starter-form.js +1 -1
  2. package/dist-client/components/activity-starter-form.js.map +1 -1
  3. package/dist-client/pages/work-door/door-sentence.d.ts +63 -0
  4. package/dist-client/pages/work-door/door-sentence.js +109 -0
  5. package/dist-client/pages/work-door/door-sentence.js.map +1 -0
  6. package/dist-client/pages/work-door/door-words.d.ts +7 -0
  7. package/dist-client/pages/work-door/door-words.js +30 -0
  8. package/dist-client/pages/work-door/door-words.js.map +1 -1
  9. package/dist-client/pages/work-door/work-doors-page.d.ts +1 -1
  10. package/dist-client/pages/work-door/work-doors-page.js +49 -17
  11. package/dist-client/pages/work-door/work-doors-page.js.map +1 -1
  12. package/dist-client/route.d.ts +1 -1
  13. package/dist-client/tsconfig.tsbuildinfo +1 -1
  14. package/dist-server/service/index.d.ts +3 -3
  15. package/dist-server/service/work-door/work-door-declaration.d.ts +11 -5
  16. package/dist-server/service/work-door/work-door-declaration.js +5 -3
  17. package/dist-server/service/work-door/work-door-declaration.js.map +1 -1
  18. package/dist-server/service/work-door/work-door-resolver.js +1 -1
  19. package/dist-server/service/work-door/work-door-resolver.js.map +1 -1
  20. package/dist-server/service/work-door/work-door-service.js +3 -14
  21. package/dist-server/service/work-door/work-door-service.js.map +1 -1
  22. package/dist-server/service/work-door/work-door-type.d.ts +43 -6
  23. package/dist-server/service/work-door/work-door-type.js +102 -26
  24. package/dist-server/service/work-door/work-door-type.js.map +1 -1
  25. package/dist-server/service/work-door/work-door.d.ts +65 -16
  26. package/dist-server/service/work-door/work-door.js +53 -90
  27. package/dist-server/service/work-door/work-door.js.map +1 -1
  28. package/dist-server/tsconfig.tsbuildinfo +1 -1
  29. package/package.json +3 -3
  30. package/tests/work-door-door-words.test.ts +35 -1
  31. package/tests/work-door-not-used-refuse.test.ts +6 -6
  32. package/tests/work-door-sentence.test.ts +199 -0
  33. package/tests/work-door.test.ts +63 -56
  34. package/translations/en.json +26 -1
  35. package/translations/ja.json +26 -1
  36. package/translations/ko.json +26 -1
  37. package/translations/ms.json +26 -1
  38. package/translations/zh.json +26 -1
@@ -16,8 +16,8 @@ export * from './work-door/work-door-not-used.js';
16
16
  export * from './work-door/work-door-journal.js';
17
17
  export * from './work-door/work-door-journal-service.js';
18
18
  export * from './work-door/work-door-service.js';
19
- export declare const entities: (typeof import("./activity/activity.js").Activity | typeof import("./activity-approval/activity-approval.js").ActivityApproval | typeof import("./activity-thread/activity-thread.js").ActivityThread | typeof import("./activity-instance/activity-instance.js").ActivityInstance | typeof import("./installable-activity/installable-activity.js").InstallableActivity | typeof import("./activity-template/activity-template.js").ActivityTemplate | typeof import("./activity-summary/activity-summary.js").ActivitySummary | typeof import("./activity/activity-history.js").ActivityHistory | typeof import("./activity-instance/activity-instance-history.js").ActivityInstanceHistory | typeof import("./activity-template/activity-template-history.js").ActivityTemplateHistory | typeof import("./activity-thread/activity-thread-history.js").ActivityThreadHistory | typeof import("./work-door/work-door-not-used.js").WorkDoorNotUsed | typeof import("./work-door/work-door-journal.js").WorkDoorJournal)[];
20
- export declare const subscribers: (typeof import("./activity-approval/event-subscriber.js").ActivityApprovalSubscriber | typeof import("./activity/event-subscriber.js").ActivityHistoryEntitySubscriber | typeof import("./activity/declaration-guard-subscriber.js").ActivityDeclarationGuardSubscriber | typeof import("./activity-instance/event-subscriber.js").ActivityInstanceSubscriber | typeof import("./activity-instance/event-subscriber.js").ActivityInstanceHistoryEntitySubscriber | typeof import("./activity-thread/event-subscriber.js").ActivityThreadSubscriber | typeof import("./activity-thread/event-subscriber.js").ActivityThreadHistoryEntitySubscriber)[];
19
+ export declare const entities: (typeof import("./activity-approval/activity-approval.js").ActivityApproval | typeof import("./activity/activity.js").Activity | typeof import("./activity/activity-history.js").ActivityHistory | typeof import("./activity-instance/activity-instance.js").ActivityInstance | typeof import("./activity-instance/activity-instance-history.js").ActivityInstanceHistory | typeof import("./activity-template/activity-template.js").ActivityTemplate | typeof import("./activity-template/activity-template-history.js").ActivityTemplateHistory | typeof import("./activity-thread/activity-thread.js").ActivityThread | typeof import("./activity-thread/activity-thread-history.js").ActivityThreadHistory | typeof import("./installable-activity/installable-activity.js").InstallableActivity | typeof import("./activity-summary/activity-summary.js").ActivitySummary | typeof import("./work-door/work-door-not-used.js").WorkDoorNotUsed | typeof import("./work-door/work-door-journal.js").WorkDoorJournal)[];
20
+ export declare const subscribers: (typeof import("./activity-approval/event-subscriber.js").ActivityApprovalSubscriber | typeof import("./activity-instance/event-subscriber.js").ActivityInstanceSubscriber | typeof import("./activity-instance/event-subscriber.js").ActivityInstanceHistoryEntitySubscriber | typeof import("./activity/event-subscriber.js").ActivityHistoryEntitySubscriber | typeof import("./activity/declaration-guard-subscriber.js").ActivityDeclarationGuardSubscriber | typeof import("./activity-thread/event-subscriber.js").ActivityThreadSubscriber | typeof import("./activity-thread/event-subscriber.js").ActivityThreadHistoryEntitySubscriber)[];
21
21
  export declare const schema: {
22
- resolverClasses: (typeof import("./activity-stats/activity-stats-query.js").ActivityStatsQuery | typeof import("./activity-approval/activity-approval-query.js").ActivityApprovalQuery | typeof import("./activity-approval/activity-approval-mutation.js").ActivityApprovalMutation | typeof import("./activity-approval/activity-approval-subscription.js").ActivityApprovalSubscription | typeof import("./activity/activity-mutation.js").ActivityMutation | typeof import("./activity/activity-query.js").ActivityQuery | typeof import("./activity-instance/activity-instance-mutation.js").ActivityInstanceMutation | typeof import("./activity-instance/activity-instance-query.js").ActivityInstanceQuery | typeof import("./activity-instance/activity-instance-subscription.js").ActivityInstanceSubscription | typeof import("./activity-template/activity-template-mutation.js").ActivityTemplateMutation | typeof import("./activity-template/activity-template-query.js").ActivityTemplateQuery | typeof import("./activity-thread/activity-thread-mutation.js").ActivityThreadMutation | typeof import("./activity-thread/activity-thread-query.js").ActivityThreadQuery | typeof import("./activity-thread/activity-thread-history-query.js").ActivityThreadHistoryQuery | typeof import("./activity-thread/activity-thread-subscription.js").ActivityThreadSubscription | typeof import("./installable-activity/installable-activity-query.js").InstallableActivityQuery | typeof import("./installable-activity/installable-activity-mutation.js").InstallableActivityMutation | typeof import("./activity-summary/activity-summary-query.js").ActivitySummaryQuery | typeof import("./work-door/work-door-resolver.js").WorkDoorResolver)[];
22
+ resolverClasses: (typeof import("./activity-stats/activity-stats-query.js").ActivityStatsQuery | typeof import("./activity-approval/activity-approval-query.js").ActivityApprovalQuery | typeof import("./activity-approval/activity-approval-mutation.js").ActivityApprovalMutation | typeof import("./activity-approval/activity-approval-subscription.js").ActivityApprovalSubscription | typeof import("./activity/activity-query.js").ActivityQuery | typeof import("./activity/activity-mutation.js").ActivityMutation | typeof import("./activity-instance/activity-instance-query.js").ActivityInstanceQuery | typeof import("./activity-instance/activity-instance-mutation.js").ActivityInstanceMutation | typeof import("./activity-instance/activity-instance-subscription.js").ActivityInstanceSubscription | typeof import("./activity-template/activity-template-query.js").ActivityTemplateQuery | typeof import("./activity-template/activity-template-mutation.js").ActivityTemplateMutation | typeof import("./activity-thread/activity-thread-query.js").ActivityThreadQuery | typeof import("./activity-thread/activity-thread-history-query.js").ActivityThreadHistoryQuery | typeof import("./activity-thread/activity-thread-mutation.js").ActivityThreadMutation | typeof import("./activity-thread/activity-thread-subscription.js").ActivityThreadSubscription | typeof import("./installable-activity/installable-activity-query.js").InstallableActivityQuery | typeof import("./installable-activity/installable-activity-mutation.js").InstallableActivityMutation | typeof import("./activity-summary/activity-summary-query.js").ActivitySummaryQuery | typeof import("./work-door/work-door-resolver.js").WorkDoorResolver)[];
23
23
  };
@@ -19,9 +19,11 @@
19
19
  * Not on a bootstrap hook. A hook runs after the schema is built, which is too late for anything the
20
20
  * schema or a boot-time install reads - `declareAxes` in auth-base carries the same warning.
21
21
  *
22
- * ── Labels travel as keys, never as words ─────────────────────────────────
23
- * `labelKey` names a translation in the declaring module's own namespace. A word here would put one
24
- * product's vocabulary ("plant", "factory floor") on every other product's screen.
22
+ * ── Nobody declares a label ───────────────────────────────────────────────
23
+ * The screen's label key is computed from `name` (`activityTKey(name, 'label')`), the same key the
24
+ * activity catalog reads. A declared key used to ride here, and operato-plant built it from the
25
+ * PascalCase name while its translations are kebab-cased - the door screen printed twenty-four raw
26
+ * keys (:4000, 2026-09-24). A key nobody picks is a key nobody can pick wrong.
25
27
  * ═══════════════════════════════════════════════════════════════════════════
26
28
  */
27
29
  /**
@@ -52,8 +54,12 @@ export type WhenNoLineBecause = 'irreversible' | 'approval-is-the-act' | 'lowers
52
54
  interface DoorBase {
53
55
  /** The form name - the same key the callback is found by. Labels change; this does not. */
54
56
  name: string;
55
- /** A translation key in the declaring module's namespace. Never a word. */
56
- labelKey: string;
57
+ /**
58
+ * @deprecated **Not read.** The label key is computed from `name`. Kept optional only so a
59
+ * declaration built against the previous release still compiles; operato-plant and
60
+ * operato-warehouse drop it, and then this line goes (worklist-ux-handoff, 2026-09-24).
61
+ */
62
+ labelKey?: string;
57
63
  /**
58
64
  * This door borrows another door's approval line. Written once, in one place: two doors carrying
59
65
  * the same line as a value drift, and then turning a control on and off are approved by different
@@ -20,9 +20,11 @@
20
20
  * Not on a bootstrap hook. A hook runs after the schema is built, which is too late for anything the
21
21
  * schema or a boot-time install reads - `declareAxes` in auth-base carries the same warning.
22
22
  *
23
- * ── Labels travel as keys, never as words ─────────────────────────────────
24
- * `labelKey` names a translation in the declaring module's own namespace. A word here would put one
25
- * product's vocabulary ("plant", "factory floor") on every other product's screen.
23
+ * ── Nobody declares a label ───────────────────────────────────────────────
24
+ * The screen's label key is computed from `name` (`activityTKey(name, 'label')`), the same key the
25
+ * activity catalog reads. A declared key used to ride here, and operato-plant built it from the
26
+ * PascalCase name while its translations are kebab-cased - the door screen printed twenty-four raw
27
+ * keys (:4000, 2026-09-24). A key nobody picks is a key nobody can pick wrong.
26
28
  * ═══════════════════════════════════════════════════════════════════════════
27
29
  */
28
30
  Object.defineProperty(exports, "__esModule", { value: true });
@@ -1 +1 @@
1
- {"version":3,"file":"work-door-declaration.js","sourceRoot":"","sources":["../../../server/service/work-door/work-door-declaration.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;;AAgFH,8CAEC;AAGD,kDAEC;AAGD,sCAEC;AAGD,4CAEC;AAGD,oCAUC;AAgBD,gEAEC;AAzDD,MAAM,UAAU,GAAG,IAAI,GAAG,EAAgC,CAAA;AAE1D;;;;;;GAMG;AACH,SAAgB,iBAAiB,CAAC,YAAkC;IAClE,UAAU,CAAC,GAAG,CAAC,YAAY,CAAC,MAAM,EAAE,YAAY,CAAC,CAAA;AACnD,CAAC;AAED,sEAAsE;AACtE,SAAgB,mBAAmB;IACjC,OAAO,CAAC,GAAG,UAAU,CAAC,MAAM,EAAE,CAAC,CAAA;AACjC,CAAC;AAED,6DAA6D;AAC7D,SAAgB,aAAa;IAC3B,OAAO,CAAC,GAAG,UAAU,CAAC,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,CAAA;AAChE,CAAC;AAED,6EAA6E;AAC7E,SAAgB,gBAAgB;IAC9B,OAAO,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,UAAU,CAAC,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;AACtF,CAAC;AAED,2DAA2D;AAC3D,SAAgB,YAAY,CAAC,IAAY;IACvC,KAAK,MAAM,YAAY,IAAI,UAAU,CAAC,MAAM,EAAE,EAAE,CAAC;QAC/C,MAAM,KAAK,GAAG,YAAY,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,KAAK,IAAI,CAAC,CAAA;QAEjE,IAAI,KAAK,EAAE,CAAC;YACV,OAAO,KAAK,CAAA;QACd,CAAC;IACH,CAAC;IAED,OAAO,SAAS,CAAA;AAClB,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,SAAgB,0BAA0B,CAAC,IAAyB;IAClE,OAAO,IAAI,CAAC,IAAI,KAAK,UAAU,IAAI,IAAI,CAAC,UAAU,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAA;AAC5F,CAAC","sourcesContent":["/**\n * **What an application declares about its own work doors** (ADR-0069 decision 3, ADR-0077).\n *\n * ═══════════════════════════════════════════════════════════════════════════\n * ── Who says what ─────────────────────────────────────────────────────────\n * The form is installed by the product at boot, and the plant fills in who approves it. Filling it\n * in is the declaration that this factory uses that step. So two facts have two owners:\n *\n * ```\n * the list of doors, and what each one is the application - it declares them here\n * whether a door stands in this domain the worklist - it reads the activity and its line\n * ```\n *\n * An application registers its list; the screens and the judgement live here. Without that split the\n * worklist would have to know operato-plant's activities, and every application would carry its own\n * copy of the same screen (operato-warehouse asked for it second, which is what moved this).\n *\n * ── Registration happens at module import ─────────────────────────────────\n * Not on a bootstrap hook. A hook runs after the schema is built, which is too late for anything the\n * schema or a boot-time install reads - `declareAxes` in auth-base carries the same warning.\n *\n * ── Labels travel as keys, never as words ─────────────────────────────────\n * `labelKey` names a translation in the declaring module's own namespace. A word here would put one\n * product's vocabulary (\"plant\", \"factory floor\") on every other product's screen.\n * ═══════════════════════════════════════════════════════════════════════════\n */\n\n/**\n * What happens when the approval line is empty. **This records what each door already does** - it is\n * not a new policy (ADR-0077 decision 1).\n *\n * - `run` - the work happens and the approval step is skipped. The worklist writes a line saying so;\n * until that record existed there was no way to tell it from a factory that approved it.\n * - `refuse` - the caller is refused and told why the line does not stand.\n * - `as-established` - the branch depends on **the row's own history**: a period declared through\n * approval is withdrawn through approval, and one that stood without approval is simply removed.\n * Only a door whose branch turns on the row may use this, and both branches are tested. The\n * judgement stays with the door; the worklist asks it which way this row goes.\n */\nexport type WhenNoLine = 'refuse' | 'run' | 'as-established'\n\n/**\n * Why a door refuses rather than running. Two doors that both refuse can refuse for different\n * reasons, and an administrator reads the reason to know whether filling the line is the fix.\n *\n * - `irreversible` - what the door does cannot be taken back (`mutation-reversibility`).\n * - `approval-is-the-act` - the door's whole work **is** the approval. Letting it through with no\n * line would put an unsigned controlled document on the floor.\n * - `lowers-a-control` - the door lowers a control that stands: turning document control off, or\n * moving a maintenance due date. Such a door refuses whatever its reversibility says, because the\n * time that passed under the lowered control does not come back.\n */\nexport type WhenNoLineBecause = 'irreversible' | 'approval-is-the-act' | 'lowers-a-control'\n\ninterface DoorBase {\n /** The form name - the same key the callback is found by. Labels change; this does not. */\n name: string\n /** A translation key in the declaring module's namespace. Never a word. */\n labelKey: string\n /**\n * This door borrows another door's approval line. Written once, in one place: two doors carrying\n * the same line as a value drift, and then turning a control on and off are approved by different\n * people (ADR-0047 ⑨).\n */\n lineFollows?: string\n}\n\ntype ApprovalDoor = DoorBase & { kind: 'approval' } & (\n | { whenNoLine: 'run'; because?: never }\n | { whenNoLine: 'refuse' | 'as-established'; because: WhenNoLineBecause }\n )\n\n/**\n * `whenNoLine?: never` is the point of this type: an assignment door has no approval line, so the\n * field cannot be written here at all. Left optional it would be filled in one day with a value that\n * reads true and means nothing.\n */\ntype AssignmentDoor = DoorBase & { kind: 'assignment'; whenNoLine?: never; because?: never }\n\nexport type WorkDoorDeclaration = ApprovalDoor | AssignmentDoor\n\nexport interface WorkDoorRegistration {\n /** The declaring module, as its package's short name. It groups the screen and names the source. */\n module: string\n doors: readonly WorkDoorDeclaration[]\n /**\n * The role seats this module offers, by name.\n *\n * An approval line points at roles, so a domain with none of them cannot build one - and today\n * that is found out on the form screen, too late. The door screen counts how many of these stand\n * here and says what to do first. A domain's own roles are not counted: this number answers \"of\n * what the product offered\", and a line built from other roles works just as well.\n */\n seats?: readonly string[]\n}\n\nconst registered = new Map<string, WorkDoorRegistration>()\n\n/**\n * An application declares its doors. Called at module import time, once per module.\n *\n * Re-registering the same module replaces it rather than appending: a module imported twice under a\n * watch rebuild would otherwise show every door twice, and a screen that double-counts the doors\n * that need attention is one nobody trusts.\n */\nexport function registerWorkDoors(registration: WorkDoorRegistration): void {\n registered.set(registration.module, registration)\n}\n\n/** Every registration in this installation, in registration order. */\nexport function registeredWorkDoors(): WorkDoorRegistration[] {\n return [...registered.values()]\n}\n\n/** Every declared door, flattened, in registration order. */\nexport function declaredDoors(): WorkDoorDeclaration[] {\n return [...registered.values()].flatMap(one => [...one.doors])\n}\n\n/** Every role seat the declaring modules offer, by name, without repeats. */\nexport function offeredSeatNames(): string[] {\n return [...new Set([...registered.values()].flatMap(one => [...(one.seats ?? [])]))]\n}\n\n/** One door by form name, whichever module declared it. */\nexport function declaredDoor(name: string): WorkDoorDeclaration | undefined {\n for (const registration of registered.values()) {\n const found = registration.doors.find(door => door.name === name)\n\n if (found) {\n return found\n }\n }\n\n return undefined\n}\n\n/**\n * **Can this door be declared unused?** Not when it refuses on an empty line (ADR-0077 decision 3).\n *\n * \"We do not use this door\" means the work runs with no approval step (`ran-not-used`). `refuse`\n * means the work cannot stand without an approval line. The two cannot hold for one door, and each of\n * the three reasons says why: what an `irreversible` door does cannot be taken back, an\n * `approval-is-the-act` door's work is the approval, and a `lowers-a-control` door lowers a control\n * that stands. Accepting the declaration would leave the door screen showing the door as off while\n * the product still demands approval - or, if the product honoured it, run those three kinds of work\n * unapproved.\n *\n * Answers the reason, so the refusal can say which one. `run`, `as-established` and assignment doors\n * answer nothing - they can be declared unused as before.\n */\nexport function whyNotUsedCannotBeDeclared(door: WorkDoorDeclaration): WhenNoLineBecause | undefined {\n return door.kind === 'approval' && door.whenNoLine === 'refuse' ? door.because : undefined\n}\n"]}
1
+ {"version":3,"file":"work-door-declaration.js","sourceRoot":"","sources":["../../../server/service/work-door/work-door-declaration.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;;AAoFH,8CAEC;AAGD,kDAEC;AAGD,sCAEC;AAGD,4CAEC;AAGD,oCAUC;AAgBD,gEAEC;AAzDD,MAAM,UAAU,GAAG,IAAI,GAAG,EAAgC,CAAA;AAE1D;;;;;;GAMG;AACH,SAAgB,iBAAiB,CAAC,YAAkC;IAClE,UAAU,CAAC,GAAG,CAAC,YAAY,CAAC,MAAM,EAAE,YAAY,CAAC,CAAA;AACnD,CAAC;AAED,sEAAsE;AACtE,SAAgB,mBAAmB;IACjC,OAAO,CAAC,GAAG,UAAU,CAAC,MAAM,EAAE,CAAC,CAAA;AACjC,CAAC;AAED,6DAA6D;AAC7D,SAAgB,aAAa;IAC3B,OAAO,CAAC,GAAG,UAAU,CAAC,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,CAAA;AAChE,CAAC;AAED,6EAA6E;AAC7E,SAAgB,gBAAgB;IAC9B,OAAO,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,UAAU,CAAC,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;AACtF,CAAC;AAED,2DAA2D;AAC3D,SAAgB,YAAY,CAAC,IAAY;IACvC,KAAK,MAAM,YAAY,IAAI,UAAU,CAAC,MAAM,EAAE,EAAE,CAAC;QAC/C,MAAM,KAAK,GAAG,YAAY,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,KAAK,IAAI,CAAC,CAAA;QAEjE,IAAI,KAAK,EAAE,CAAC;YACV,OAAO,KAAK,CAAA;QACd,CAAC;IACH,CAAC;IAED,OAAO,SAAS,CAAA;AAClB,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,SAAgB,0BAA0B,CAAC,IAAyB;IAClE,OAAO,IAAI,CAAC,IAAI,KAAK,UAAU,IAAI,IAAI,CAAC,UAAU,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAA;AAC5F,CAAC","sourcesContent":["/**\n * **What an application declares about its own work doors** (ADR-0069 decision 3, ADR-0077).\n *\n * ═══════════════════════════════════════════════════════════════════════════\n * ── Who says what ─────────────────────────────────────────────────────────\n * The form is installed by the product at boot, and the plant fills in who approves it. Filling it\n * in is the declaration that this factory uses that step. So two facts have two owners:\n *\n * ```\n * the list of doors, and what each one is the application - it declares them here\n * whether a door stands in this domain the worklist - it reads the activity and its line\n * ```\n *\n * An application registers its list; the screens and the judgement live here. Without that split the\n * worklist would have to know operato-plant's activities, and every application would carry its own\n * copy of the same screen (operato-warehouse asked for it second, which is what moved this).\n *\n * ── Registration happens at module import ─────────────────────────────────\n * Not on a bootstrap hook. A hook runs after the schema is built, which is too late for anything the\n * schema or a boot-time install reads - `declareAxes` in auth-base carries the same warning.\n *\n * ── Nobody declares a label ───────────────────────────────────────────────\n * The screen's label key is computed from `name` (`activityTKey(name, 'label')`), the same key the\n * activity catalog reads. A declared key used to ride here, and operato-plant built it from the\n * PascalCase name while its translations are kebab-cased - the door screen printed twenty-four raw\n * keys (:4000, 2026-09-24). A key nobody picks is a key nobody can pick wrong.\n * ═══════════════════════════════════════════════════════════════════════════\n */\n\n/**\n * What happens when the approval line is empty. **This records what each door already does** - it is\n * not a new policy (ADR-0077 decision 1).\n *\n * - `run` - the work happens and the approval step is skipped. The worklist writes a line saying so;\n * until that record existed there was no way to tell it from a factory that approved it.\n * - `refuse` - the caller is refused and told why the line does not stand.\n * - `as-established` - the branch depends on **the row's own history**: a period declared through\n * approval is withdrawn through approval, and one that stood without approval is simply removed.\n * Only a door whose branch turns on the row may use this, and both branches are tested. The\n * judgement stays with the door; the worklist asks it which way this row goes.\n */\nexport type WhenNoLine = 'refuse' | 'run' | 'as-established'\n\n/**\n * Why a door refuses rather than running. Two doors that both refuse can refuse for different\n * reasons, and an administrator reads the reason to know whether filling the line is the fix.\n *\n * - `irreversible` - what the door does cannot be taken back (`mutation-reversibility`).\n * - `approval-is-the-act` - the door's whole work **is** the approval. Letting it through with no\n * line would put an unsigned controlled document on the floor.\n * - `lowers-a-control` - the door lowers a control that stands: turning document control off, or\n * moving a maintenance due date. Such a door refuses whatever its reversibility says, because the\n * time that passed under the lowered control does not come back.\n */\nexport type WhenNoLineBecause = 'irreversible' | 'approval-is-the-act' | 'lowers-a-control'\n\ninterface DoorBase {\n /** The form name - the same key the callback is found by. Labels change; this does not. */\n name: string\n /**\n * @deprecated **Not read.** The label key is computed from `name`. Kept optional only so a\n * declaration built against the previous release still compiles; operato-plant and\n * operato-warehouse drop it, and then this line goes (worklist-ux-handoff, 2026-09-24).\n */\n labelKey?: string\n /**\n * This door borrows another door's approval line. Written once, in one place: two doors carrying\n * the same line as a value drift, and then turning a control on and off are approved by different\n * people (ADR-0047 ⑨).\n */\n lineFollows?: string\n}\n\ntype ApprovalDoor = DoorBase & { kind: 'approval' } & (\n | { whenNoLine: 'run'; because?: never }\n | { whenNoLine: 'refuse' | 'as-established'; because: WhenNoLineBecause }\n )\n\n/**\n * `whenNoLine?: never` is the point of this type: an assignment door has no approval line, so the\n * field cannot be written here at all. Left optional it would be filled in one day with a value that\n * reads true and means nothing.\n */\ntype AssignmentDoor = DoorBase & { kind: 'assignment'; whenNoLine?: never; because?: never }\n\nexport type WorkDoorDeclaration = ApprovalDoor | AssignmentDoor\n\nexport interface WorkDoorRegistration {\n /** The declaring module, as its package's short name. It groups the screen and names the source. */\n module: string\n doors: readonly WorkDoorDeclaration[]\n /**\n * The role seats this module offers, by name.\n *\n * An approval line points at roles, so a domain with none of them cannot build one - and today\n * that is found out on the form screen, too late. The door screen counts how many of these stand\n * here and says what to do first. A domain's own roles are not counted: this number answers \"of\n * what the product offered\", and a line built from other roles works just as well.\n */\n seats?: readonly string[]\n}\n\nconst registered = new Map<string, WorkDoorRegistration>()\n\n/**\n * An application declares its doors. Called at module import time, once per module.\n *\n * Re-registering the same module replaces it rather than appending: a module imported twice under a\n * watch rebuild would otherwise show every door twice, and a screen that double-counts the doors\n * that need attention is one nobody trusts.\n */\nexport function registerWorkDoors(registration: WorkDoorRegistration): void {\n registered.set(registration.module, registration)\n}\n\n/** Every registration in this installation, in registration order. */\nexport function registeredWorkDoors(): WorkDoorRegistration[] {\n return [...registered.values()]\n}\n\n/** Every declared door, flattened, in registration order. */\nexport function declaredDoors(): WorkDoorDeclaration[] {\n return [...registered.values()].flatMap(one => [...one.doors])\n}\n\n/** Every role seat the declaring modules offer, by name, without repeats. */\nexport function offeredSeatNames(): string[] {\n return [...new Set([...registered.values()].flatMap(one => [...(one.seats ?? [])]))]\n}\n\n/** One door by form name, whichever module declared it. */\nexport function declaredDoor(name: string): WorkDoorDeclaration | undefined {\n for (const registration of registered.values()) {\n const found = registration.doors.find(door => door.name === name)\n\n if (found) {\n return found\n }\n }\n\n return undefined\n}\n\n/**\n * **Can this door be declared unused?** Not when it refuses on an empty line (ADR-0077 decision 3).\n *\n * \"We do not use this door\" means the work runs with no approval step (`ran-not-used`). `refuse`\n * means the work cannot stand without an approval line. The two cannot hold for one door, and each of\n * the three reasons says why: what an `irreversible` door does cannot be taken back, an\n * `approval-is-the-act` door's work is the approval, and a `lowers-a-control` door lowers a control\n * that stands. Accepting the declaration would leave the door screen showing the door as off while\n * the product still demands approval - or, if the product honoured it, run those three kinds of work\n * unapproved.\n *\n * Answers the reason, so the refusal can say which one. `run`, `as-established` and assignment doors\n * answer nothing - they can be declared unused as before.\n */\nexport function whyNotUsedCannotBeDeclared(door: WorkDoorDeclaration): WhenNoLineBecause | undefined {\n return door.kind === 'approval' && door.whenNoLine === 'refuse' ? door.because : undefined\n}\n"]}
@@ -32,8 +32,8 @@ let WorkDoorResolver = class WorkDoorResolver {
32
32
  const doors = await (0, work_door_service_js_1.workDoorsFor)(manager, domain);
33
33
  return {
34
34
  doors: doors.map(work_door_type_js_1.toDoorType),
35
- summary: (0, work_door_js_1.summarizeDoors)(doors),
36
35
  todo: (0, work_door_js_1.doorsToFill)(doors),
36
+ ...(0, work_door_js_1.countDoors)(doors),
37
37
  seatsStanding: await (0, work_door_service_js_1.seatsStanding)(manager, domain),
38
38
  seatsOffered: (0, work_door_service_js_1.seatsOffered)()
39
39
  };
@@ -1 +1 @@
1
- {"version":3,"file":"work-door-resolver.js","sourceRoot":"","sources":["../../../server/service/work-door/work-door-resolver.ts"],"names":[],"mappings":";;;AAyJA,0DAEC;;AAzJD,+CAA4F;AAG5F,iDAA6D;AAC7D,yDAAkD;AAElD,iDAA8D;AAG9D,iDAAmF;AACnF,iEAAkF;AAClF,yEAA6G;AAC7G,mEAAyD;AACzD,2DAA+D;AAC/D,iEAAsD;AACtD,iFAA6D;AAE7D;;;;;;;;;;GAUG;AAGI,IAAM,gBAAgB,GAAtB,MAAM,gBAAgB;IAKrB,AAAN,KAAK,CAAC,SAAS,CAAQ,OAAwB;QAC7C,MAAM,OAAO,GAAG,IAAA,qBAAa,EAAC,sBAAQ,CAAC,CAAC,OAAO,CAAA;QAC/C,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,KAAK,CAAA;QAChC,MAAM,KAAK,GAAG,MAAM,IAAA,mCAAY,EAAC,OAAO,EAAE,MAAM,CAAC,CAAA;QAEjD,OAAO;YACL,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,8BAAU,CAAC;YAC5B,OAAO,EAAE,IAAA,6BAAc,EAAC,KAAK,CAAC;YAC9B,IAAI,EAAE,IAAA,0BAAW,EAAC,KAAK,CAAC;YACxB,aAAa,EAAE,MAAM,IAAA,oCAAa,EAAC,OAAO,EAAE,MAAM,CAAC;YACnD,YAAY,EAAE,IAAA,mCAAY,GAAE;SAC7B,CAAA;IACH,CAAC;IAED;;;;;;;;;;OAUG;IAIG,AAAN,KAAK,CAAC,sBAAsB,CACT,QAAgB,EACyC,OAAe,EAClF,OAAwB;QAE/B,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,GAAG,OAAO,CAAC,KAAK,CAAA;QAE1C,MAAM,IAAI,GAAG,IAAA,uCAAY,EAAC,QAAQ,CAAC,CAAA;QAEnC,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,MAAM,IAAI,eAAO,CACf,mBAAmB,EACnB,EAAE,IAAI,EAAE,QAAQ,EAAE,EAClB,gEAAgE,CACjE,CAAA;QACH,CAAC;QAED;;;;WAIG;QACH,MAAM,MAAM,GAAG,IAAA,qDAA0B,EAAC,IAAI,CAAC,CAAA;QAE/C,IAAI,MAAM,EAAE,CAAC;YACX,MAAM,uBAAuB,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAA;QACjD,CAAC;QAED,IAAI,CAAC,OAAO,EAAE,IAAI,EAAE,EAAE,CAAC;YACrB,MAAM,IAAI,eAAO,CAAC,mCAAmC,EAAE,EAAE,EAAE,kCAAkC,CAAC,CAAA;QAChG,CAAC;QAED,MAAM,UAAU,GAAG,EAAE,CAAC,aAAa,CAAC,uCAAe,CAAC,CAAA;QACpD,MAAM,QAAQ,GAAG,MAAM,UAAU,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,CAAC,CAAA;QAE7F,MAAM,UAAU,CAAC,IAAI,CAAC;YACpB,GAAG,CAAC,QAAQ,IAAI,EAAE,CAAC;YACnB,MAAM;YACN,QAAQ;YACR,OAAO,EAAE,OAAO,CAAC,IAAI,EAAE;YACvB,UAAU,EAAE,IAAI;SACV,CAAC,CAAA;QAET,OAAO,MAAM,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAA;IACtC,CAAC;IAED,qDAAqD;IAI/C,AAAN,KAAK,CAAC,gBAAgB,CAAkB,QAAgB,EAAS,OAAwB;QACvF,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,EAAE,EAAE,GAAG,OAAO,CAAC,KAAK,CAAA;QAC1C,MAAM,UAAU,GAAG,EAAE,CAAC,aAAa,CAAC,uCAAe,CAAC,CAAA;QACpD,MAAM,QAAQ,GAAG,MAAM,UAAU,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,SAAS,EAAE,CAAC,YAAY,CAAC,EAAE,CAAC,CAAA;QAExH,uDAAuD;QACvD,IAAI,QAAQ,EAAE,CAAC;YACb;;;eAGG;YACH,MAAM,IAAA,2CAAY,EAChB,EAAE,EACF,MAAM,EACN;gBACE,IAAI,EAAE,oCAAa,CAAC,gBAAgB;gBACpC,IAAI,EAAE,QAAQ,CAAC,QAAQ;gBACvB,MAAM,EAAE,IAAA,oCAAqB,EAAC,QAAQ,CAAC;aACxC,EACD,IAAI,CACL,CAAA;YAED,MAAM,UAAU,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAA;QACnC,CAAC;QAED,OAAO,MAAM,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAA;IACtC,CAAC;CACF,CAAA;AA9GY,4CAAgB;AAKrB;IAJL,IAAA,wBAAS,EAAC,gFAAgF,CAAC;IAC3F,IAAA,oBAAK,EAAC,OAAO,CAAC,EAAE,CAAC,iCAAa,EAAE;QAC/B,WAAW,EAAE,kGAAkG;KAChH,CAAC;IACe,mBAAA,IAAA,kBAAG,GAAE,CAAA;;;;iDAYrB;AAgBK;IAHL,IAAA,wBAAS,EAAC,mFAAmF,CAAC;IAC9F,IAAA,wBAAS,EAAC,cAAc,CAAC;IACzB,IAAA,uBAAQ,EAAC,OAAO,CAAC,EAAE,CAAC,iCAAa,EAAE,EAAE,WAAW,EAAE,uDAAuD,EAAE,CAAC;IAE1G,mBAAA,IAAA,kBAAG,EAAC,UAAU,CAAC,CAAA;IACf,mBAAA,IAAA,kBAAG,EAAC,SAAS,EAAE,EAAE,WAAW,EAAE,uCAAuC,EAAE,CAAC,CAAA;IACxE,mBAAA,IAAA,kBAAG,GAAE,CAAA;;;;8DAyCP;AAMK;IAHL,IAAA,wBAAS,EAAC,mFAAmF,CAAC;IAC9F,IAAA,wBAAS,EAAC,cAAc,CAAC;IACzB,IAAA,uBAAQ,EAAC,OAAO,CAAC,EAAE,CAAC,iCAAa,EAAE,EAAE,WAAW,EAAE,qDAAqD,EAAE,CAAC;IACnF,mBAAA,IAAA,kBAAG,EAAC,UAAU,CAAC,CAAA;IAAoB,mBAAA,IAAA,kBAAG,GAAE,CAAA;;;;wDA0B/D;2BA7GU,gBAAgB;IAF5B,IAAA,uBAAQ,GAAE;IACV,IAAA,4BAAa,EAAC,qBAAa,CAAC;GAChB,gBAAgB,CA8G5B;AAED,qGAAqG;AACrG,MAAM,iBAAiB,GAAsC;IAC3D,YAAY,EACV,gIAAgI;IAClI,qBAAqB,EAAE,gGAAgG;IACvH,kBAAkB,EAChB,kIAAkI;CACrI,CAAA;AAED,SAAgB,uBAAuB,CAAC,IAAY,EAAE,OAA0B;IAC9E,OAAO,IAAI,eAAO,CAAC,8BAA8B,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,EAAE,iBAAiB,CAAC,OAAO,CAAC,CAAC,CAAA;AACnG,CAAC","sourcesContent":["import { EntityManager } from 'typeorm'\n\nimport { Arg, Ctx, Directive, Mutation, Query, Resolver, UseMiddleware } from 'type-graphql'\n\nimport { Role } from '@things-factory/auth-base'\nimport { Domain, getRepository } from '@things-factory/shell'\nimport { Activity } from '../activity/activity.js'\n\nimport { Refusal, SpeakRefusals } from '@things-factory/shell'\n\nimport { activityNamedIn } from './activity-lookup.js'\nimport { doorsToFill, summarizeDoors, workDoorNotUsedBefore } from './work-door.js'\nimport { seatsOffered, seatsStanding, workDoorsFor } from './work-door-service.js'\nimport { declaredDoor, whyNotUsedCannotBeDeclared, type WhenNoLineBecause } from './work-door-declaration.js'\nimport { WorkDoorNotUsed } from './work-door-not-used.js'\nimport { WorkDoorsType, toDoorType } from './work-door-type.js'\nimport { WorkDoorEvent } from './work-door-journal.js'\nimport { noteWorkDoor } from './work-door-journal-service.js'\n\n/**\n * 이 공장의 결재 문 현황을 묻는 자리.\n *\n * ── 왜 질의인가 ────────────────────────────────────────────────────────────\n * 기동할 때 로그로 한 번 지나가는 사실이었다(`install-boot.ts`). 사람이 조치해야 하는 사실은\n * **물어볼 수 있어야** 한다 — 지나간 로그를 찾아 올라가게 하면 아무도 안 본다.\n *\n * ── 부모까지 보는 것은 `activityNamedIn` 의 규칙을 그대로 쓴다 ─────────────\n * 상신하는 쪽이 그 규칙으로 양식을 찾으므로, 보는 쪽이 다른 규칙으로 찾으면 **화면이 「없다」고 하는데\n * 실제로는 서는** 상태가 생긴다. 같은 함수를 부른다.\n */\n@Resolver()\n@UseMiddleware(SpeakRefusals)\nexport class WorkDoorResolver {\n @Directive('@privilege(category: \"activity\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(returns => WorkDoorsType, {\n description: 'To see which approval steps this plant actually has standing, and which pass with nobody on them'\n })\n async workDoors(@Ctx() context: ResolverContext): Promise<WorkDoorsType> {\n const manager = getRepository(Activity).manager\n const { domain } = context.state\n const doors = await workDoorsFor(manager, domain)\n\n return {\n doors: doors.map(toDoorType),\n summary: summarizeDoors(doors),\n todo: doorsToFill(doors),\n seatsStanding: await seatsStanding(manager, domain),\n seatsOffered: seatsOffered()\n }\n }\n\n /**\n * **이 공장은 이 문을 안 쓴다**고 선언한다.\n *\n * ── 왜 사유를 받나 ──────────────────────────────────────────────────────\n * 이 선언은 승인 단계를 끄는 것이다. 반년 뒤 「왜 폐기 승인이 없나」를 묻는 사람이 나오고, 그때\n * 사유가 없으면 누가 왜 껐는지 알 수 없다. 사람과 시각은 표가 들고 이유는 사람이 적는다.\n *\n * ── 모르는 이름은 받지 않는다 ───────────────────────────────────────────\n * 오타로 선언하면 그 줄은 아무 문에도 안 걸리고, 정작 끄려던 문은 계속 조치 대상으로 남는다.\n * 끈 줄 알고 있는데 화면은 그대로다 — 화면이 그 오타를 말해 주지 않는다.\n */\n @Directive('@privilege(category: \"activity\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Directive('@transaction')\n @Mutation(returns => WorkDoorsType, { description: 'To declare that this plant does not use one work door' })\n async declareWorkDoorNotUsed(\n @Arg('doorName') doorName: string,\n @Arg('because', { description: 'why — somebody will ask in six months' }) because: string,\n @Ctx() context: ResolverContext\n ): Promise<WorkDoorsType> {\n const { domain, user, tx } = context.state\n\n const door = declaredDoor(doorName)\n\n if (!door) {\n throw new Refusal(\n 'work-door-unknown',\n { name: doorName },\n 'unknown task \"{name}\". use a task name from the status screen.'\n )\n }\n\n /*\n * **결재선 없이는 설 수 없는 문은 끌 수 없다**(ADR-0077 결정 3). 받아 두면 이 화면은 「꺼짐」이라\n * 말하는데 제품은 계속 결재를 요구하고, 제품이 이 선언을 따르면 되돌릴 수 없는 일이 결재 없이 돈다.\n * 까닭마다 문장이 다르다 — 관리자가 읽고 「결재선을 세우면 된다」까지 알아야 한다.\n */\n const cannot = whyNotUsedCannotBeDeclared(door)\n\n if (cannot) {\n throw notUsedCannotBeDeclared(doorName, cannot)\n }\n\n if (!because?.trim()) {\n throw new Refusal('work-door-not-used-reason-missing', {}, 'enter why this task is not used.')\n }\n\n const repository = tx.getRepository(WorkDoorNotUsed)\n const existing = await repository.findOne({ where: { domain: { id: domain.id }, doorName } })\n\n await repository.save({\n ...(existing ?? {}),\n domain,\n doorName,\n because: because.trim(),\n declaredBy: user\n } as any)\n\n return await this.workDoors(context)\n }\n\n /** 다시 쓰기로 했다. 선언을 지운다 — 지우면 그 문은 다시 조치 대상으로 돌아온다. */\n @Directive('@privilege(category: \"activity\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Directive('@transaction')\n @Mutation(returns => WorkDoorsType, { description: 'To withdraw the declaration and use that door again' })\n async useWorkDoorAgain(@Arg('doorName') doorName: string, @Ctx() context: ResolverContext): Promise<WorkDoorsType> {\n const { domain, user, tx } = context.state\n const repository = tx.getRepository(WorkDoorNotUsed)\n const existing = await repository.findOne({ where: { domain: { id: domain.id }, doorName }, relations: ['declaredBy'] })\n\n /* 없는 것을 지우라는 것은 이미 쓰고 있다는 뜻이다. 오류로 막지 않고 지금 현황을 낸다. */\n if (existing) {\n /*\n * **지우는 선언을 이력에 남긴다.** 왜 안 쓰기로 했고 누가 정했는지가 지우면 어디에도 없어진다\n * (ADR-0064 ③, 2026-09-17). 다시 선언할 때 그 내용을 여기서 읽는다.\n */\n await noteWorkDoor(\n tx,\n domain,\n {\n kind: WorkDoorEvent.NotUsedWithdrawn,\n door: existing.doorName,\n detail: workDoorNotUsedBefore(existing)\n },\n user\n )\n\n await repository.remove(existing)\n }\n\n return await this.workDoors(context)\n }\n}\n\n/** The refusal for each reason. One code per reason, so each language has its own whole sentence. */\nconst NOT_USED_REFUSALS: Record<WhenNoLineBecause, string> = {\n irreversible:\n '\"{name}\" cannot be turned off. what it does cannot be undone, so it only runs through approval. set its approval line instead.',\n 'approval-is-the-act': '\"{name}\" cannot be turned off. its approval is the work itself. set its approval line instead.',\n 'lowers-a-control':\n '\"{name}\" cannot be turned off. it lowers a control that stands, so it only runs through approval. set its approval line instead.'\n}\n\nexport function notUsedCannotBeDeclared(name: string, because: WhenNoLineBecause): Refusal {\n return new Refusal(`work-door-not-used-refused-${because}`, { name }, NOT_USED_REFUSALS[because])\n}\n"]}
1
+ {"version":3,"file":"work-door-resolver.js","sourceRoot":"","sources":["../../../server/service/work-door/work-door-resolver.ts"],"names":[],"mappings":"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","sourcesContent":["import { EntityManager } from 'typeorm'\n\nimport { Arg, Ctx, Directive, Mutation, Query, Resolver, UseMiddleware } from 'type-graphql'\n\nimport { Role } from '@things-factory/auth-base'\nimport { Domain, getRepository } from '@things-factory/shell'\nimport { Activity } from '../activity/activity.js'\n\nimport { Refusal, SpeakRefusals } from '@things-factory/shell'\n\nimport { activityNamedIn } from './activity-lookup.js'\nimport { countDoors, doorsToFill, workDoorNotUsedBefore } from './work-door.js'\nimport { seatsOffered, seatsStanding, workDoorsFor } from './work-door-service.js'\nimport { declaredDoor, whyNotUsedCannotBeDeclared, type WhenNoLineBecause } from './work-door-declaration.js'\nimport { WorkDoorNotUsed } from './work-door-not-used.js'\nimport { WorkDoorsType, toDoorType } from './work-door-type.js'\nimport { WorkDoorEvent } from './work-door-journal.js'\nimport { noteWorkDoor } from './work-door-journal-service.js'\n\n/**\n * 이 공장의 결재 문 현황을 묻는 자리.\n *\n * ── 왜 질의인가 ────────────────────────────────────────────────────────────\n * 기동할 때 로그로 한 번 지나가는 사실이었다(`install-boot.ts`). 사람이 조치해야 하는 사실은\n * **물어볼 수 있어야** 한다 — 지나간 로그를 찾아 올라가게 하면 아무도 안 본다.\n *\n * ── 부모까지 보는 것은 `activityNamedIn` 의 규칙을 그대로 쓴다 ─────────────\n * 상신하는 쪽이 그 규칙으로 양식을 찾으므로, 보는 쪽이 다른 규칙으로 찾으면 **화면이 「없다」고 하는데\n * 실제로는 서는** 상태가 생긴다. 같은 함수를 부른다.\n */\n@Resolver()\n@UseMiddleware(SpeakRefusals)\nexport class WorkDoorResolver {\n @Directive('@privilege(category: \"activity\", privilege: \"query\", domainOwnerGranted: true)')\n @Query(returns => WorkDoorsType, {\n description: 'To see which approval steps this plant actually has standing, and which pass with nobody on them'\n })\n async workDoors(@Ctx() context: ResolverContext): Promise<WorkDoorsType> {\n const manager = getRepository(Activity).manager\n const { domain } = context.state\n const doors = await workDoorsFor(manager, domain)\n\n return {\n doors: doors.map(toDoorType),\n todo: doorsToFill(doors),\n ...countDoors(doors),\n seatsStanding: await seatsStanding(manager, domain),\n seatsOffered: seatsOffered()\n }\n }\n\n /**\n * **이 공장은 이 문을 안 쓴다**고 선언한다.\n *\n * ── 왜 사유를 받나 ──────────────────────────────────────────────────────\n * 이 선언은 승인 단계를 끄는 것이다. 반년 뒤 「왜 폐기 승인이 없나」를 묻는 사람이 나오고, 그때\n * 사유가 없으면 누가 왜 껐는지 알 수 없다. 사람과 시각은 표가 들고 이유는 사람이 적는다.\n *\n * ── 모르는 이름은 받지 않는다 ───────────────────────────────────────────\n * 오타로 선언하면 그 줄은 아무 문에도 안 걸리고, 정작 끄려던 문은 계속 조치 대상으로 남는다.\n * 끈 줄 알고 있는데 화면은 그대로다 — 화면이 그 오타를 말해 주지 않는다.\n */\n @Directive('@privilege(category: \"activity\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Directive('@transaction')\n @Mutation(returns => WorkDoorsType, { description: 'To declare that this plant does not use one work door' })\n async declareWorkDoorNotUsed(\n @Arg('doorName') doorName: string,\n @Arg('because', { description: 'why — somebody will ask in six months' }) because: string,\n @Ctx() context: ResolverContext\n ): Promise<WorkDoorsType> {\n const { domain, user, tx } = context.state\n\n const door = declaredDoor(doorName)\n\n if (!door) {\n throw new Refusal(\n 'work-door-unknown',\n { name: doorName },\n 'unknown task \"{name}\". use a task name from the status screen.'\n )\n }\n\n /*\n * **결재선 없이는 설 수 없는 문은 끌 수 없다**(ADR-0077 결정 3). 받아 두면 이 화면은 「꺼짐」이라\n * 말하는데 제품은 계속 결재를 요구하고, 제품이 이 선언을 따르면 되돌릴 수 없는 일이 결재 없이 돈다.\n * 까닭마다 문장이 다르다 — 관리자가 읽고 「결재선을 세우면 된다」까지 알아야 한다.\n */\n const cannot = whyNotUsedCannotBeDeclared(door)\n\n if (cannot) {\n throw notUsedCannotBeDeclared(doorName, cannot)\n }\n\n if (!because?.trim()) {\n throw new Refusal('work-door-not-used-reason-missing', {}, 'enter why this task is not used.')\n }\n\n const repository = tx.getRepository(WorkDoorNotUsed)\n const existing = await repository.findOne({ where: { domain: { id: domain.id }, doorName } })\n\n await repository.save({\n ...(existing ?? {}),\n domain,\n doorName,\n because: because.trim(),\n declaredBy: user\n } as any)\n\n return await this.workDoors(context)\n }\n\n /** 다시 쓰기로 했다. 선언을 지운다 — 지우면 그 문은 다시 조치 대상으로 돌아온다. */\n @Directive('@privilege(category: \"activity\", privilege: \"mutation\", domainOwnerGranted: true)')\n @Directive('@transaction')\n @Mutation(returns => WorkDoorsType, { description: 'To withdraw the declaration and use that door again' })\n async useWorkDoorAgain(@Arg('doorName') doorName: string, @Ctx() context: ResolverContext): Promise<WorkDoorsType> {\n const { domain, user, tx } = context.state\n const repository = tx.getRepository(WorkDoorNotUsed)\n const existing = await repository.findOne({ where: { domain: { id: domain.id }, doorName }, relations: ['declaredBy'] })\n\n /* 없는 것을 지우라는 것은 이미 쓰고 있다는 뜻이다. 오류로 막지 않고 지금 현황을 낸다. */\n if (existing) {\n /*\n * **지우는 선언을 이력에 남긴다.** 왜 안 쓰기로 했고 누가 정했는지가 지우면 어디에도 없어진다\n * (ADR-0064 ③, 2026-09-17). 다시 선언할 때 그 내용을 여기서 읽는다.\n */\n await noteWorkDoor(\n tx,\n domain,\n {\n kind: WorkDoorEvent.NotUsedWithdrawn,\n door: existing.doorName,\n detail: workDoorNotUsedBefore(existing)\n },\n user\n )\n\n await repository.remove(existing)\n }\n\n return await this.workDoors(context)\n }\n}\n\n/** The refusal for each reason. One code per reason, so each language has its own whole sentence. */\nconst NOT_USED_REFUSALS: Record<WhenNoLineBecause, string> = {\n irreversible:\n '\"{name}\" cannot be turned off. what it does cannot be undone, so it only runs through approval. set its approval line instead.',\n 'approval-is-the-act': '\"{name}\" cannot be turned off. its approval is the work itself. set its approval line instead.',\n 'lowers-a-control':\n '\"{name}\" cannot be turned off. it lowers a control that stands, so it only runs through approval. set its approval line instead.'\n}\n\nexport function notUsedCannotBeDeclared(name: string, because: WhenNoLineBecause): Refusal {\n return new Refusal(`work-door-not-used-refused-${because}`, { name }, NOT_USED_REFUSALS[because])\n}\n"]}
@@ -45,10 +45,11 @@ async function workDoorsFor(manager, domain) {
45
45
  const lineOwner = follows ? await (0, activity_lookup_js_1.activityNamedIn)(manager, domain, follows) : activity;
46
46
  doors.push((0, work_door_1.judgeDoor)({
47
47
  name,
48
- label: labelOf(door),
49
48
  kind: door.kind,
49
+ whenNoLine: door.whenNoLine,
50
+ whenNoLineBecause: door.because,
50
51
  notUsedBecause: declared.get(name),
51
- follows: follows ? labelOfDoorNamed(follows) : undefined,
52
+ follows,
52
53
  /* 상신하는 쪽과 **같은 함수**로 센다. 여기서 따로 세면 화면과 관문이 갈린다. */
53
54
  approvalStanding: activity
54
55
  ? await (0, approval_line_standing_js_1.judgeApprovalLine)(lineOwner?.approvalLine, reader)
@@ -111,16 +112,4 @@ async function seatsStanding(manager, domain) {
111
112
  function seatsOffered() {
112
113
  return (0, work_door_declaration_js_1.offeredSeatNames)().length;
113
114
  }
114
- /**
115
- * 화면에 낼 이름. **선언은 키를 주고 글자는 각 모듈의 번역이 답한다** — 여기서 글자를 들고 있으면
116
- * 한 제품의 낱말이 다른 제품 화면에 선다. 번역이 없으면 양식 이름을 그대로 보인다(키를 찍는 것보다
117
- * 낫다 — 양식 이름은 적어도 사람이 찾을 수 있는 이름이다).
118
- */
119
- function labelOf(door) {
120
- return door.labelKey || door.name;
121
- }
122
- function labelOfDoorNamed(name) {
123
- const found = (0, work_door_declaration_js_1.declaredDoors)().find(one => one.name === name);
124
- return found ? labelOf(found) : name;
125
- }
126
115
  //# sourceMappingURL=work-door-service.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"work-door-service.js","sourceRoot":"","sources":["../../../server/service/work-door/work-door-service.ts"],"names":[],"mappings":"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","sourcesContent":["import { EntityManager, In } from 'typeorm'\n\nimport { Role } from '@things-factory/auth-base'\nimport { Domain } from '@things-factory/shell'\n\nimport { activityNamedIn, seatReader } from './activity-lookup.js'\nimport { judgeApprovalLine } from './approval-line-standing.js'\nimport { declaredDoors, offeredSeatNames } from './work-door-declaration.js'\nimport { WorkDoor, judgeDoor } from './work-door'\nimport { WorkDoorNotUsed } from './work-door-not-used'\n\n/**\n * 이 공장의 문 열둘을 읽어 판정한다 — **한 벌.**\n *\n * ── 왜 resolver 밖에 두나 ──────────────────────────────────────────────────\n * 이 판정을 묻는 자리가 둘이 됐다. 화면(`workDoors`)과 성숙도 표다. 각자 읽으면 둘이 다른 수를\n * 말하게 되고, **어느 쪽이 맞는지 보는 사람이 알 길이 없다.** 성숙도 표는 이 제품이 스스로에 대해\n * 하는 말이라 화면과 어긋나면 그 표 전체를 못 믿는다.\n *\n * ── 부모까지 보는 규칙은 상신하는 쪽의 것을 그대로 쓴다 ────────────────────\n * 상신이 `activityNamedIn` 으로 양식을 찾으므로, 보는 쪽이 다른 규칙으로 찾으면 **「없다」고\n * 말하는데 실제로는 서는** 상태가 생긴다.\n */\nexport async function workDoorsFor(manager: EntityManager, domain: Domain): Promise<WorkDoor[]> {\n /* 공장이 「안 쓴다」고 말한 것을 한 번에 읽는다. 문마다 읽으면 열두 번 묻는다. */\n const declared = new Map(\n (await manager.find(WorkDoorNotUsed, { where: { domain: { id: domain.id } } })).map(one => [\n one.doorName,\n one.because\n ])\n )\n\n const doors: WorkDoor[] = []\n\n /*\n * **포트 하나를 문 열일곱이 함께 쓴다.** 포트가 읽은 역할을 기억하므로 같은 역할을 여러 번 묻지\n * 않는다 — 결재선 대부분이 공장장으로 끝난다. 이 판정 동안만 산다.\n */\n const reader = seatReader(manager)\n\n for (const door of declaredDoors()) {\n const name = door.name\n const activity = await activityNamedIn(manager, domain, name)\n /*\n * **결재선을 빌려 쓰는 문**은 따르는 문의 결재선으로 센다(§`LINE_FOLLOWS`). 자기 칸을 세면, 올리는\n * 쪽은 발행 승인의 결재선으로 서는데 현황은 「결재자 없음」이라고 말한다.\n */\n const follows = door.lineFollows\n const lineOwner = follows ? await activityNamedIn(manager, domain, follows) : activity\n\n doors.push(\n judgeDoor({\n name,\n label: labelOf(door),\n kind: door.kind,\n notUsedBecause: declared.get(name),\n follows: follows ? labelOfDoorNamed(follows) : undefined,\n /* 상신하는 쪽과 **같은 함수**로 센다. 여기서 따로 세면 화면과 관문이 갈린다. */\n approvalStanding: activity\n ? await judgeApprovalLine(lineOwner?.approvalLine as Parameters<typeof judgeApprovalLine>[0], reader)\n : undefined,\n installed: activity\n ? {\n approvalLine: lineOwner?.approvalLine as unknown[] | undefined,\n assigneeRole: await roleNameOf(manager, (activity as { assigneeRoleId?: string }).assigneeRoleId),\n /*\n * **빌려 온 것일 때만** 채운다. `Activity` 는 `domainId` 만 들고 있어 이름은 따로\n * 읽는데, 자기 도메인이면 읽지도 않고 비운다 — 자기 이름을 채우면 화면이 모든 줄에\n * 「우리 공장에서 정했습니다」를 달고, 「결재자 없음」 옆에서 그 말이 거짓이 된다.\n */\n borrowedFrom:\n activity.domainId === domain.id ? undefined : await subdomainOf(manager, activity.domainId)\n }\n : undefined\n })\n )\n }\n\n return doors\n}\n\n/**\n * 그 역할의 이름. 없으면 없다 — 지어내지 않는다.\n *\n * 문마다 부르지 않고 배정 문에서만 부른다. 배정 문이 하나도 없는 지금은 한 번도 안 돈다.\n */\nasync function roleNameOf(manager: EntityManager, roleId?: string): Promise<string | undefined> {\n if (!roleId) {\n return undefined\n }\n\n const found = await manager.findOne(Role, { where: { id: roleId } })\n\n return found?.name\n}\n\n/**\n * 부모 도메인의 이름. 못 읽으면 지어내지 않고 비운다 — 「본사」라고 적으면 없는 이름이 화면에 남는다.\n *\n * 원문 SQL 로 안 읽는다. 자리표시자 철자가 드라이버마다 달라(`?` · `$1`) sqlite 에서는 통과하고\n * postgres 에서만 죽는다 — 그 부류는 여기서 절대 못 잡는다.\n */\nasync function subdomainOf(manager: EntityManager, domainId: string): Promise<string | undefined> {\n const found = await manager.findOne(Domain, { where: { id: domainId } })\n\n return found?.subdomain\n}\n\n/**\n * 이 제품이 내놓는 역할 자리 중 이 공장에 선 것이 몇인가.\n *\n * 결재선은 **역할을 가리킨다**(`[{type:'Role', value:<역할 id>}]`). 그러니 역할이 없는 공장에서는\n * 결재선을 만들 수 없다 — 양식 화면까지 가서야 그걸 알게 된다. 문 현황과 함께 내서 화면이 순서를\n * 말하게 한다.\n *\n * 공장이 스스로 만든 다른 역할은 안 센다. 이 수가 말하는 것은 「이 제품이 내놓은 자리」이고,\n * 그 밖의 역할로 결재선을 짜는 것도 물론 된다.\n */\nexport async function seatsStanding(manager: EntityManager, domain: Domain): Promise<number> {\n const offered = offeredSeatNames()\n const here = await manager.find(Role, { where: { domain: { id: domain.id } } })\n const names = new Set(here.map(one => one.name))\n\n return offered.filter(one => names.has(one)).length\n}\n\n/** 이 설치본의 앱들이 내놓는 자리의 수 — 화면이 목록을 따로 들지 않게 여기서 센다. */\nexport function seatsOffered(): number {\n return offeredSeatNames().length\n}\n\n/**\n * 화면에 낼 이름. **선언은 키를 주고 글자는 각 모듈의 번역이 답한다** — 여기서 글자를 들고 있으면\n * 한 제품의 낱말이 다른 제품 화면에 선다. 번역이 없으면 양식 이름을 그대로 보인다(키를 찍는 것보다\n * 낫다 — 양식 이름은 적어도 사람이 찾을 수 있는 이름이다).\n */\nfunction labelOf(door: { labelKey: string; name: string }): string {\n return door.labelKey || door.name\n}\n\nfunction labelOfDoorNamed(name: string): string {\n const found = declaredDoors().find(one => one.name === name)\n\n return found ? labelOf(found) : name\n}\n"]}
1
+ {"version":3,"file":"work-door-service.js","sourceRoot":"","sources":["../../../server/service/work-door/work-door-service.ts"],"names":[],"mappings":"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","sourcesContent":["import { EntityManager, In } from 'typeorm'\n\nimport { Role } from '@things-factory/auth-base'\nimport { Domain } from '@things-factory/shell'\n\nimport { activityNamedIn, seatReader } from './activity-lookup.js'\nimport { judgeApprovalLine } from './approval-line-standing.js'\nimport { declaredDoors, offeredSeatNames } from './work-door-declaration.js'\nimport { WorkDoor, judgeDoor } from './work-door'\nimport { WorkDoorNotUsed } from './work-door-not-used'\n\n/**\n * 이 공장의 문 열둘을 읽어 판정한다 — **한 벌.**\n *\n * ── 왜 resolver 밖에 두나 ──────────────────────────────────────────────────\n * 이 판정을 묻는 자리가 둘이 됐다. 화면(`workDoors`)과 성숙도 표다. 각자 읽으면 둘이 다른 수를\n * 말하게 되고, **어느 쪽이 맞는지 보는 사람이 알 길이 없다.** 성숙도 표는 이 제품이 스스로에 대해\n * 하는 말이라 화면과 어긋나면 그 표 전체를 못 믿는다.\n *\n * ── 부모까지 보는 규칙은 상신하는 쪽의 것을 그대로 쓴다 ────────────────────\n * 상신이 `activityNamedIn` 으로 양식을 찾으므로, 보는 쪽이 다른 규칙으로 찾으면 **「없다」고\n * 말하는데 실제로는 서는** 상태가 생긴다.\n */\nexport async function workDoorsFor(manager: EntityManager, domain: Domain): Promise<WorkDoor[]> {\n /* 공장이 「안 쓴다」고 말한 것을 한 번에 읽는다. 문마다 읽으면 열두 번 묻는다. */\n const declared = new Map(\n (await manager.find(WorkDoorNotUsed, { where: { domain: { id: domain.id } } })).map(one => [\n one.doorName,\n one.because\n ])\n )\n\n const doors: WorkDoor[] = []\n\n /*\n * **포트 하나를 문 열일곱이 함께 쓴다.** 포트가 읽은 역할을 기억하므로 같은 역할을 여러 번 묻지\n * 않는다 — 결재선 대부분이 공장장으로 끝난다. 이 판정 동안만 산다.\n */\n const reader = seatReader(manager)\n\n for (const door of declaredDoors()) {\n const name = door.name\n const activity = await activityNamedIn(manager, domain, name)\n /*\n * **결재선을 빌려 쓰는 문**은 따르는 문의 결재선으로 센다(§`LINE_FOLLOWS`). 자기 칸을 세면, 올리는\n * 쪽은 발행 승인의 결재선으로 서는데 현황은 「결재자 없음」이라고 말한다.\n */\n const follows = door.lineFollows\n const lineOwner = follows ? await activityNamedIn(manager, domain, follows) : activity\n\n doors.push(\n judgeDoor({\n name,\n kind: door.kind,\n whenNoLine: door.whenNoLine,\n whenNoLineBecause: door.because,\n notUsedBecause: declared.get(name),\n follows,\n /* 상신하는 쪽과 **같은 함수**로 센다. 여기서 따로 세면 화면과 관문이 갈린다. */\n approvalStanding: activity\n ? await judgeApprovalLine(lineOwner?.approvalLine as Parameters<typeof judgeApprovalLine>[0], reader)\n : undefined,\n installed: activity\n ? {\n approvalLine: lineOwner?.approvalLine as unknown[] | undefined,\n assigneeRole: await roleNameOf(manager, (activity as { assigneeRoleId?: string }).assigneeRoleId),\n /*\n * **빌려 온 것일 때만** 채운다. `Activity` 는 `domainId` 만 들고 있어 이름은 따로\n * 읽는데, 자기 도메인이면 읽지도 않고 비운다 — 자기 이름을 채우면 화면이 모든 줄에\n * 「우리 공장에서 정했습니다」를 달고, 「결재자 없음」 옆에서 그 말이 거짓이 된다.\n */\n borrowedFrom:\n activity.domainId === domain.id ? undefined : await subdomainOf(manager, activity.domainId)\n }\n : undefined\n })\n )\n }\n\n return doors\n}\n\n/**\n * 그 역할의 이름. 없으면 없다 — 지어내지 않는다.\n *\n * 문마다 부르지 않고 배정 문에서만 부른다. 배정 문이 하나도 없는 지금은 한 번도 안 돈다.\n */\nasync function roleNameOf(manager: EntityManager, roleId?: string): Promise<string | undefined> {\n if (!roleId) {\n return undefined\n }\n\n const found = await manager.findOne(Role, { where: { id: roleId } })\n\n return found?.name\n}\n\n/**\n * 부모 도메인의 이름. 못 읽으면 지어내지 않고 비운다 — 「본사」라고 적으면 없는 이름이 화면에 남는다.\n *\n * 원문 SQL 로 안 읽는다. 자리표시자 철자가 드라이버마다 달라(`?` · `$1`) sqlite 에서는 통과하고\n * postgres 에서만 죽는다 — 그 부류는 여기서 절대 못 잡는다.\n */\nasync function subdomainOf(manager: EntityManager, domainId: string): Promise<string | undefined> {\n const found = await manager.findOne(Domain, { where: { id: domainId } })\n\n return found?.subdomain\n}\n\n/**\n * 이 제품이 내놓는 역할 자리 중 이 공장에 선 것이 몇인가.\n *\n * 결재선은 **역할을 가리킨다**(`[{type:'Role', value:<역할 id>}]`). 그러니 역할이 없는 공장에서는\n * 결재선을 만들 수 없다 — 양식 화면까지 가서야 그걸 알게 된다. 문 현황과 함께 내서 화면이 순서를\n * 말하게 한다.\n *\n * 공장이 스스로 만든 다른 역할은 안 센다. 이 수가 말하는 것은 「이 제품이 내놓은 자리」이고,\n * 그 밖의 역할로 결재선을 짜는 것도 물론 된다.\n */\nexport async function seatsStanding(manager: EntityManager, domain: Domain): Promise<number> {\n const offered = offeredSeatNames()\n const here = await manager.find(Role, { where: { domain: { id: domain.id } } })\n const names = new Set(here.map(one => one.name))\n\n return offered.filter(one => names.has(one)).length\n}\n\n/** 이 설치본의 앱들이 내놓는 자리의 수 — 화면이 목록을 따로 들지 않게 여기서 센다. */\nexport function seatsOffered(): number {\n return offeredSeatNames().length\n}\n"]}
@@ -2,8 +2,10 @@ import type { WorkDoor } from './work-door.js';
2
2
  /**
3
3
  * 「이 공장에서 어느 결재 문이 서 있나」를 화면에 낼 모양.
4
4
  *
5
- * 문자열 하나로 안 낸다 — 화면이 상태별로 다르게 그려야 하고, 그러려면 상태가 값이어야 한다.
6
- * 문장만 주면 화면이 그 문장을 다시 뜯어 읽는다.
5
+ * **사실만 낸다. 문장은 화면이 번역으로 짓는다.** 서버가 한국어 문장을 지어 보내던 때 영어·일본어·
6
+ * 중국어·말레이어 사용자도 한국어를 읽었고, 화면이 상태별로 달리 그리려면 그 문장을 다시 뜯어 읽어야
7
+ * 했다. 열거형 값 셋은 `registerEnumType` 이 **멤버 이름**을 싣는다 — 화면이 `door-words.ts` 한 곳에서
8
+ * 옮긴다.
7
9
  */
8
10
  export declare enum DoorStandingValue {
9
11
  /** 채워졌다. */
@@ -20,19 +22,50 @@ export declare enum DoorKindValue {
20
22
  Approval = "approval",
21
23
  Assignment = "assignment"
22
24
  }
25
+ /** 왜 그 상태인가 — 까닭마다 관리자가 할 일이 다르다(§`DoorReason`). */
26
+ export declare enum DoorReasonValue {
27
+ NotUsed = "not-used",
28
+ Missing = "missing",
29
+ LineEmpty = "line-empty",
30
+ RoleEmpty = "role-empty",
31
+ PersonAbsent = "person-absent",
32
+ NoAssignee = "no-assignee",
33
+ LineFilled = "line-filled",
34
+ AssigneeBound = "assignee-bound"
35
+ }
36
+ /** 결재선이 비면 이 문이 어떻게 하나 — 선언 그대로(ADR-0077 결정 1). */
37
+ export declare enum WhenNoLineValue {
38
+ Run = "run",
39
+ Refuse = "refuse",
40
+ AsEstablished = "as-established"
41
+ }
42
+ /** 결재를 요구하는 까닭 — `refuse` · `as-established` 문만. */
43
+ export declare enum WhenNoLineBecauseValue {
44
+ Irreversible = "irreversible",
45
+ ApprovalIsTheAct = "approval-is-the-act",
46
+ LowersAControl = "lowers-a-control"
47
+ }
23
48
  export declare class WorkDoorType {
24
49
  name: string;
25
- label: string;
50
+ labelKey: string;
26
51
  kind: DoorKindValue;
27
52
  standing: DoorStandingValue;
53
+ reason: DoorReasonValue;
28
54
  approvers: number;
29
55
  from?: string;
30
- because: string;
56
+ whenNoLine?: WhenNoLineValue;
57
+ whenNoLineBecause?: WhenNoLineBecauseValue;
58
+ followsLabelKey?: string;
59
+ assigneeRole?: string;
60
+ notUsedBecause?: string;
31
61
  }
32
62
  export declare class WorkDoorsType {
33
63
  doors: WorkDoorType[];
34
- summary: string;
35
64
  todo: number;
65
+ on: number;
66
+ off: number;
67
+ missing: number;
68
+ notUsed: number;
36
69
  /**
37
70
  * 이 제품이 내놓는 역할 자리 중 **이 공장에 실제로 선 것.**
38
71
  *
@@ -45,5 +78,9 @@ export declare class WorkDoorsType {
45
78
  seatsStanding: number;
46
79
  seatsOffered: number;
47
80
  }
48
- /** 판단의 모양을 화면의 모양으로. 열거형 철자는 `@Field` 가 정하므로 값으로 옮긴다. */
81
+ /**
82
+ * 판단의 모양을 화면의 모양으로. 판단부의 낱말(`'line-empty'`)을 열거형 **값**으로 찾아 멤버를 싣는다 —
83
+ * 값과 낱말이 같게 적혀 있으니 표를 따로 들지 않는다. 못 찾으면 던진다: 새 낱말이 전선에서 조용히
84
+ * 사라지면 화면이 「모른다」조차 못 그린다.
85
+ */
49
86
  export declare function toDoorType(door: WorkDoor): WorkDoorType;
@@ -1,14 +1,16 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.WorkDoorsType = exports.WorkDoorType = exports.DoorKindValue = exports.DoorStandingValue = void 0;
3
+ exports.WorkDoorsType = exports.WorkDoorType = exports.WhenNoLineBecauseValue = exports.WhenNoLineValue = exports.DoorReasonValue = exports.DoorKindValue = exports.DoorStandingValue = void 0;
4
4
  exports.toDoorType = toDoorType;
5
5
  const tslib_1 = require("tslib");
6
6
  const type_graphql_1 = require("type-graphql");
7
7
  /**
8
8
  * 「이 공장에서 어느 결재 문이 서 있나」를 화면에 낼 모양.
9
9
  *
10
- * 문자열 하나로 안 낸다 — 화면이 상태별로 다르게 그려야 하고, 그러려면 상태가 값이어야 한다.
11
- * 문장만 주면 화면이 그 문장을 다시 뜯어 읽는다.
10
+ * **사실만 낸다. 문장은 화면이 번역으로 짓는다.** 서버가 한국어 문장을 지어 보내던 때 영어·일본어·
11
+ * 중국어·말레이어 사용자도 한국어를 읽었고, 화면이 상태별로 달리 그리려면 그 문장을 다시 뜯어 읽어야
12
+ * 했다. 열거형 값 셋은 `registerEnumType` 이 **멤버 이름**을 싣는다 — 화면이 `door-words.ts` 한 곳에서
13
+ * 옮긴다.
12
14
  */
13
15
  var DoorStandingValue;
14
16
  (function (DoorStandingValue) {
@@ -27,6 +29,44 @@ var DoorKindValue;
27
29
  DoorKindValue["Approval"] = "approval";
28
30
  DoorKindValue["Assignment"] = "assignment";
29
31
  })(DoorKindValue || (exports.DoorKindValue = DoorKindValue = {}));
32
+ /** 왜 그 상태인가 — 까닭마다 관리자가 할 일이 다르다(§`DoorReason`). */
33
+ var DoorReasonValue;
34
+ (function (DoorReasonValue) {
35
+ DoorReasonValue["NotUsed"] = "not-used";
36
+ DoorReasonValue["Missing"] = "missing";
37
+ DoorReasonValue["LineEmpty"] = "line-empty";
38
+ DoorReasonValue["RoleEmpty"] = "role-empty";
39
+ DoorReasonValue["PersonAbsent"] = "person-absent";
40
+ DoorReasonValue["NoAssignee"] = "no-assignee";
41
+ DoorReasonValue["LineFilled"] = "line-filled";
42
+ DoorReasonValue["AssigneeBound"] = "assignee-bound";
43
+ })(DoorReasonValue || (exports.DoorReasonValue = DoorReasonValue = {}));
44
+ /** 결재선이 비면 이 문이 어떻게 하나 — 선언 그대로(ADR-0077 결정 1). */
45
+ var WhenNoLineValue;
46
+ (function (WhenNoLineValue) {
47
+ WhenNoLineValue["Run"] = "run";
48
+ WhenNoLineValue["Refuse"] = "refuse";
49
+ WhenNoLineValue["AsEstablished"] = "as-established";
50
+ })(WhenNoLineValue || (exports.WhenNoLineValue = WhenNoLineValue = {}));
51
+ /** 결재를 요구하는 까닭 — `refuse` · `as-established` 문만. */
52
+ var WhenNoLineBecauseValue;
53
+ (function (WhenNoLineBecauseValue) {
54
+ WhenNoLineBecauseValue["Irreversible"] = "irreversible";
55
+ WhenNoLineBecauseValue["ApprovalIsTheAct"] = "approval-is-the-act";
56
+ WhenNoLineBecauseValue["LowersAControl"] = "lowers-a-control";
57
+ })(WhenNoLineBecauseValue || (exports.WhenNoLineBecauseValue = WhenNoLineBecauseValue = {}));
58
+ (0, type_graphql_1.registerEnumType)(DoorReasonValue, {
59
+ name: 'DoorReason',
60
+ description: 'Why a door stands where it does — each reason asks for a different fix'
61
+ });
62
+ (0, type_graphql_1.registerEnumType)(WhenNoLineValue, {
63
+ name: 'WhenNoLine',
64
+ description: 'What an approval door does when its approval line is empty, as the application declared it'
65
+ });
66
+ (0, type_graphql_1.registerEnumType)(WhenNoLineBecauseValue, {
67
+ name: 'WhenNoLineBecause',
68
+ description: 'Why an approval door will not run without an approval line'
69
+ });
30
70
  (0, type_graphql_1.registerEnumType)(DoorKindValue, {
31
71
  name: 'DoorKind',
32
72
  description: 'Whether the plant fills an approval line or an assignee role on this door'
@@ -43,9 +83,9 @@ tslib_1.__decorate([
43
83
  tslib_1.__metadata("design:type", String)
44
84
  ], WorkDoorType.prototype, "name", void 0);
45
85
  tslib_1.__decorate([
46
- (0, type_graphql_1.Field)({ description: 'what to show a person' }),
86
+ (0, type_graphql_1.Field)({ description: 'the translation key of its label — computed from the name, never declared' }),
47
87
  tslib_1.__metadata("design:type", String)
48
- ], WorkDoorType.prototype, "label", void 0);
88
+ ], WorkDoorType.prototype, "labelKey", void 0);
49
89
  tslib_1.__decorate([
50
90
  (0, type_graphql_1.Field)(type => DoorKindValue),
51
91
  tslib_1.__metadata("design:type", String)
@@ -54,6 +94,10 @@ tslib_1.__decorate([
54
94
  (0, type_graphql_1.Field)(type => DoorStandingValue),
55
95
  tslib_1.__metadata("design:type", String)
56
96
  ], WorkDoorType.prototype, "standing", void 0);
97
+ tslib_1.__decorate([
98
+ (0, type_graphql_1.Field)(type => DoorReasonValue),
99
+ tslib_1.__metadata("design:type", String)
100
+ ], WorkDoorType.prototype, "reason", void 0);
57
101
  tslib_1.__decorate([
58
102
  (0, type_graphql_1.Field)(type => type_graphql_1.Int, { description: 'how many approvers — zero for an assignment door or an empty line' }),
59
103
  tslib_1.__metadata("design:type", Number)
@@ -63,9 +107,25 @@ tslib_1.__decorate([
63
107
  tslib_1.__metadata("design:type", String)
64
108
  ], WorkDoorType.prototype, "from", void 0);
65
109
  tslib_1.__decorate([
66
- (0, type_graphql_1.Field)({ description: 'what this standing means, in the words the screen shows' }),
110
+ (0, type_graphql_1.Field)(type => WhenNoLineValue, { nullable: true, description: 'approval doors only' }),
111
+ tslib_1.__metadata("design:type", String)
112
+ ], WorkDoorType.prototype, "whenNoLine", void 0);
113
+ tslib_1.__decorate([
114
+ (0, type_graphql_1.Field)(type => WhenNoLineBecauseValue, { nullable: true, description: 'refuse and as-established doors only' }),
115
+ tslib_1.__metadata("design:type", String)
116
+ ], WorkDoorType.prototype, "whenNoLineBecause", void 0);
117
+ tslib_1.__decorate([
118
+ (0, type_graphql_1.Field)({ nullable: true, description: 'the label key of the door whose approval line this one follows' }),
119
+ tslib_1.__metadata("design:type", String)
120
+ ], WorkDoorType.prototype, "followsLabelKey", void 0);
121
+ tslib_1.__decorate([
122
+ (0, type_graphql_1.Field)({ nullable: true, description: 'the role an assignment door is bound to' }),
123
+ tslib_1.__metadata("design:type", String)
124
+ ], WorkDoorType.prototype, "assigneeRole", void 0);
125
+ tslib_1.__decorate([
126
+ (0, type_graphql_1.Field)({ nullable: true, description: 'what the plant wrote when it said it does not use this door — shown as written' }),
67
127
  tslib_1.__metadata("design:type", String)
68
- ], WorkDoorType.prototype, "because", void 0);
128
+ ], WorkDoorType.prototype, "notUsedBecause", void 0);
69
129
  exports.WorkDoorType = WorkDoorType = tslib_1.__decorate([
70
130
  (0, type_graphql_1.ObjectType)({ description: 'One approval form and whether this plant has approvers on it' })
71
131
  ], WorkDoorType);
@@ -76,14 +136,26 @@ tslib_1.__decorate([
76
136
  (0, type_graphql_1.Field)(type => [WorkDoorType]),
77
137
  tslib_1.__metadata("design:type", Array)
78
138
  ], WorkDoorsType.prototype, "doors", void 0);
79
- tslib_1.__decorate([
80
- (0, type_graphql_1.Field)({ description: 'the whole plant in one sentence — a list alone makes the reader count rows' }),
81
- tslib_1.__metadata("design:type", String)
82
- ], WorkDoorsType.prototype, "summary", void 0);
83
139
  tslib_1.__decorate([
84
140
  (0, type_graphql_1.Field)(type => type_graphql_1.Int, { description: 'how many still need filling — not-used doors are never counted' }),
85
141
  tslib_1.__metadata("design:type", Number)
86
142
  ], WorkDoorsType.prototype, "todo", void 0);
143
+ tslib_1.__decorate([
144
+ (0, type_graphql_1.Field)(type => type_graphql_1.Int, { description: 'doors standing' }),
145
+ tslib_1.__metadata("design:type", Number)
146
+ ], WorkDoorsType.prototype, "on", void 0);
147
+ tslib_1.__decorate([
148
+ (0, type_graphql_1.Field)(type => type_graphql_1.Int, { description: 'forms installed with nobody on them' }),
149
+ tslib_1.__metadata("design:type", Number)
150
+ ], WorkDoorsType.prototype, "off", void 0);
151
+ tslib_1.__decorate([
152
+ (0, type_graphql_1.Field)(type => type_graphql_1.Int, { description: 'forms that never installed' }),
153
+ tslib_1.__metadata("design:type", Number)
154
+ ], WorkDoorsType.prototype, "missing", void 0);
155
+ tslib_1.__decorate([
156
+ (0, type_graphql_1.Field)(type => type_graphql_1.Int, { description: 'doors this plant said it does not use' }),
157
+ tslib_1.__metadata("design:type", Number)
158
+ ], WorkDoorsType.prototype, "notUsed", void 0);
87
159
  tslib_1.__decorate([
88
160
  (0, type_graphql_1.Field)(type => type_graphql_1.Int, { description: 'how many of the role seats this product offers actually exist here' }),
89
161
  tslib_1.__metadata("design:type", Number)
@@ -95,28 +167,32 @@ tslib_1.__decorate([
95
167
  exports.WorkDoorsType = WorkDoorsType = tslib_1.__decorate([
96
168
  (0, type_graphql_1.ObjectType)({ description: 'Every approval form this product installs, and where this plant stands on each' })
97
169
  ], WorkDoorsType);
98
- /** 판단의 모양을 화면의 모양으로. 열거형 철자는 `@Field` 가 정하므로 값으로 옮긴다. */
170
+ /**
171
+ * 판단의 모양을 화면의 모양으로. 판단부의 낱말(`'line-empty'`)을 열거형 **값**으로 찾아 멤버를 싣는다 —
172
+ * 값과 낱말이 같게 적혀 있으니 표를 따로 들지 않는다. 못 찾으면 던진다: 새 낱말이 전선에서 조용히
173
+ * 사라지면 화면이 「모른다」조차 못 그린다.
174
+ */
99
175
  function toDoorType(door) {
100
176
  return {
101
177
  name: door.name,
102
- label: door.label,
103
- kind: door.kind === 'assignment' ? DoorKindValue.Assignment : DoorKindValue.Approval,
104
- standing: standingValue(door.standing),
178
+ labelKey: door.labelKey,
179
+ kind: member(DoorKindValue, door.kind),
180
+ standing: member(DoorStandingValue, door.standing),
181
+ reason: member(DoorReasonValue, door.reason),
105
182
  approvers: door.approvers,
106
183
  from: door.from,
107
- because: door.because
184
+ whenNoLine: door.whenNoLine ? member(WhenNoLineValue, door.whenNoLine) : undefined,
185
+ whenNoLineBecause: door.whenNoLineBecause ? member(WhenNoLineBecauseValue, door.whenNoLineBecause) : undefined,
186
+ followsLabelKey: door.followsLabelKey,
187
+ assigneeRole: door.assigneeRole,
188
+ notUsedBecause: door.notUsedBecause
108
189
  };
109
190
  }
110
- function standingValue(standing) {
111
- switch (standing) {
112
- case 'on':
113
- return DoorStandingValue.On;
114
- case 'off':
115
- return DoorStandingValue.Off;
116
- case 'missing':
117
- return DoorStandingValue.Missing;
118
- default:
119
- return DoorStandingValue.NotUsed;
191
+ function member(values, word) {
192
+ const found = Object.values(values).find(one => one === word);
193
+ if (!found) {
194
+ throw new Error(`work door: no wire value for "${word}"`);
120
195
  }
196
+ return found;
121
197
  }
122
198
  //# sourceMappingURL=work-door-type.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"work-door-type.js","sourceRoot":"","sources":["../../../server/service/work-door/work-door-type.ts"],"names":[],"mappings":";;;AA0FA,gCAUC;;AApGD,+CAAuE;AAIvE;;;;;GAKG;AAEH,IAAY,iBASX;AATD,WAAY,iBAAiB;IAC3B,YAAY;IACZ,8BAAS,CAAA;IACT,gCAAgC;IAChC,gCAAW,CAAA;IACX,yCAAyC;IACzC,wCAAmB,CAAA;IACnB,kCAAkC;IAClC,yCAAoB,CAAA;AACtB,CAAC,EATW,iBAAiB,iCAAjB,iBAAiB,QAS5B;AAED,8CAA8C;AAC9C,IAAY,aAGX;AAHD,WAAY,aAAa;IACvB,sCAAqB,CAAA;IACrB,0CAAyB,CAAA;AAC3B,CAAC,EAHW,aAAa,6BAAb,aAAa,QAGxB;AAED,IAAA,+BAAgB,EAAC,aAAa,EAAE;IAC9B,IAAI,EAAE,UAAU;IAChB,WAAW,EAAE,2EAA2E;CACzF,CAAC,CAAA;AAEF,IAAA,+BAAgB,EAAC,iBAAiB,EAAE;IAClC,IAAI,EAAE,cAAc;IACpB,WAAW,EAAE,gEAAgE;CAC9E,CAAC,CAAA;AAGK,IAAM,YAAY,GAAlB,MAAM,YAAY;CAqBxB,CAAA;AArBY,oCAAY;AAEvB;IADC,IAAA,oBAAK,EAAC,EAAE,WAAW,EAAE,kDAAkD,EAAE,CAAC;;0CAC/D;AAGZ;IADC,IAAA,oBAAK,EAAC,EAAE,WAAW,EAAE,uBAAuB,EAAE,CAAC;;2CACnC;AAGb;IADC,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,aAAa,CAAC;;0CACV;AAGnB;IADC,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,iBAAiB,CAAC;;8CACN;AAG3B;IADC,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,kBAAG,EAAE,EAAE,WAAW,EAAE,mEAAmE,EAAE,CAAC;;+CACxF;AAGjB;IADC,IAAA,oBAAK,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,WAAW,EAAE,0EAA0E,EAAE,CAAC;;0CACtG;AAGb;IADC,IAAA,oBAAK,EAAC,EAAE,WAAW,EAAE,yDAAyD,EAAE,CAAC;;6CACnE;uBApBJ,YAAY;IADxB,IAAA,yBAAU,EAAC,EAAE,WAAW,EAAE,8DAA8D,EAAE,CAAC;GAC/E,YAAY,CAqBxB;AAGM,IAAM,aAAa,GAAnB,MAAM,aAAa;CAwBzB,CAAA;AAxBY,sCAAa;AAExB;IADC,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,CAAC,YAAY,CAAC,CAAC;;4CACT;AAGrB;IADC,IAAA,oBAAK,EAAC,EAAE,WAAW,EAAE,4EAA4E,EAAE,CAAC;;8CACtF;AAGf;IADC,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,kBAAG,EAAE,EAAE,WAAW,EAAE,gEAAgE,EAAE,CAAC;;2CAC1F;AAYZ;IADC,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,kBAAG,EAAE,EAAE,WAAW,EAAE,oEAAoE,EAAE,CAAC;;oDACrF;AAGrB;IADC,IAAA,oBAAK,EAAC,IAAI,CAAC,EAAE,CAAC,kBAAG,EAAE,EAAE,WAAW,EAAE,yCAAyC,EAAE,CAAC;;mDAC3D;wBAvBT,aAAa;IADzB,IAAA,yBAAU,EAAC,EAAE,WAAW,EAAE,gFAAgF,EAAE,CAAC;GACjG,aAAa,CAwBzB;AAED,yDAAyD;AACzD,SAAgB,UAAU,CAAC,IAAc;IACvC,OAAO;QACL,IAAI,EAAE,IAAI,CAAC,IAAI;QACf,KAAK,EAAE,IAAI,CAAC,KAAK;QACjB,IAAI,EAAE,IAAI,CAAC,IAAI,KAAK,YAAY,CAAC,CAAC,CAAC,aAAa,CAAC,UAAU,CAAC,CAAC,CAAC,aAAa,CAAC,QAAQ;QACpF,QAAQ,EAAE,aAAa,CAAC,IAAI,CAAC,QAAQ,CAAC;QACtC,SAAS,EAAE,IAAI,CAAC,SAAS;QACzB,IAAI,EAAE,IAAI,CAAC,IAAI;QACf,OAAO,EAAE,IAAI,CAAC,OAAO;KACtB,CAAA;AACH,CAAC;AAED,SAAS,aAAa,CAAC,QAAsB;IAC3C,QAAQ,QAAQ,EAAE,CAAC;QACjB,KAAK,IAAI;YACP,OAAO,iBAAiB,CAAC,EAAE,CAAA;QAC7B,KAAK,KAAK;YACR,OAAO,iBAAiB,CAAC,GAAG,CAAA;QAC9B,KAAK,SAAS;YACZ,OAAO,iBAAiB,CAAC,OAAO,CAAA;QAClC;YACE,OAAO,iBAAiB,CAAC,OAAO,CAAA;IACpC,CAAC;AACH,CAAC","sourcesContent":["import { Field, Int, ObjectType, registerEnumType } from 'type-graphql'\n\nimport type { WorkDoor, DoorStanding } from './work-door.js'\n\n/**\n * 「이 공장에서 어느 결재 문이 서 있나」를 화면에 낼 모양.\n *\n * 문자열 하나로 안 낸다 — 화면이 상태별로 다르게 그려야 하고, 그러려면 상태가 값이어야 한다.\n * 문장만 주면 화면이 그 문장을 다시 뜯어 읽는다.\n */\n\nexport enum DoorStandingValue {\n /** 채워졌다. */\n On = 'on',\n /** 양식은 섰는데 받을 사람이 없다. 조치 대상. */\n Off = 'off',\n /** 양식이 아예 안 섰다 — 설정이 아니라 결함이다. 조치 대상. */\n Missing = 'missing',\n /** 이 공장이 안 쓰기로 했다. 조치 대상이 아니다. */\n NotUsed = 'not-used'\n}\n\n/** 결재냐 배정이냐 — 비었을 때 결과가 반대라 화면이 갈라 그려야 한다. */\nexport enum DoorKindValue {\n Approval = 'approval',\n Assignment = 'assignment'\n}\n\nregisterEnumType(DoorKindValue, {\n name: 'DoorKind',\n description: 'Whether the plant fills an approval line or an assignee role on this door'\n})\n\nregisterEnumType(DoorStandingValue, {\n name: 'DoorStanding',\n description: 'Whether this plant has an approval step standing for that form'\n})\n\n@ObjectType({ description: 'One approval form and whether this plant has approvers on it' })\nexport class WorkDoorType {\n @Field({ description: 'the form name — the key the callback is found by' })\n name: string\n\n @Field({ description: 'what to show a person' })\n label: string\n\n @Field(type => DoorKindValue)\n kind: DoorKindValue\n\n @Field(type => DoorStandingValue)\n standing: DoorStandingValue\n\n @Field(type => Int, { description: 'how many approvers — zero for an assignment door or an empty line' })\n approvers: number\n\n @Field({ nullable: true, description: 'which domain the approval line came from — a plant may use its parent\\'s' })\n from?: string\n\n @Field({ description: 'what this standing means, in the words the screen shows' })\n because: string\n}\n\n@ObjectType({ description: 'Every approval form this product installs, and where this plant stands on each' })\nexport class WorkDoorsType {\n @Field(type => [WorkDoorType])\n doors: WorkDoorType[]\n\n @Field({ description: 'the whole plant in one sentence — a list alone makes the reader count rows' })\n summary: string\n\n @Field(type => Int, { description: 'how many still need filling — not-used doors are never counted' })\n todo: number\n\n /**\n * 이 제품이 내놓는 역할 자리 중 **이 공장에 실제로 선 것.**\n *\n * ── 왜 문 목록에 자리 수가 붙나 ────────────────────────────────────────\n * 문이 비었다는 말만 하면 사람은 양식 화면으로 간다. 그런데 **고를 역할이 없으면 그 화면의\n * 결재선 칸이 빈 목록**이다. 헛걸음하고 돌아와서, 무엇이 잘못됐는지 모른 채 이 화면을 다시 본다.\n *\n * 두 수를 함께 내면 화면이 순서를 말할 수 있다 — 자리부터 세우고, 그다음에 결재선을 건다.\n */\n @Field(type => Int, { description: 'how many of the role seats this product offers actually exist here' })\n seatsStanding: number\n\n @Field(type => Int, { description: 'how many role seats this product offers' })\n seatsOffered: number\n}\n\n/** 판단의 모양을 화면의 모양으로. 열거형 철자는 `@Field` 가 정하므로 값으로 옮긴다. */\nexport function toDoorType(door: WorkDoor): WorkDoorType {\n return {\n name: door.name,\n label: door.label,\n kind: door.kind === 'assignment' ? DoorKindValue.Assignment : DoorKindValue.Approval,\n standing: standingValue(door.standing),\n approvers: door.approvers,\n from: door.from,\n because: door.because\n }\n}\n\nfunction standingValue(standing: DoorStanding): DoorStandingValue {\n switch (standing) {\n case 'on':\n return DoorStandingValue.On\n case 'off':\n return DoorStandingValue.Off\n case 'missing':\n return DoorStandingValue.Missing\n default:\n return DoorStandingValue.NotUsed\n }\n}\n"]}
1
+ {"version":3,"file":"work-door-type.js","sourceRoot":"","sources":["../../../server/service/work-door/work-door-type.ts"],"names":[],"mappings":"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","sourcesContent":["import { Field, Int, ObjectType, registerEnumType } from 'type-graphql'\n\nimport type { WorkDoor } from './work-door.js'\n\n/**\n * 「이 공장에서 어느 결재 문이 서 있나」를 화면에 낼 모양.\n *\n * **사실만 낸다. 문장은 화면이 번역으로 짓는다.** 서버가 한국어 문장을 지어 보내던 때 영어·일본어·\n * 중국어·말레이어 사용자도 한국어를 읽었고, 화면이 상태별로 달리 그리려면 그 문장을 다시 뜯어 읽어야\n * 했다. 열거형 값 셋은 `registerEnumType` 이 **멤버 이름**을 싣는다 — 화면이 `door-words.ts` 한 곳에서\n * 옮긴다.\n */\n\nexport enum DoorStandingValue {\n /** 채워졌다. */\n On = 'on',\n /** 양식은 섰는데 받을 사람이 없다. 조치 대상. */\n Off = 'off',\n /** 양식이 아예 안 섰다 — 설정이 아니라 결함이다. 조치 대상. */\n Missing = 'missing',\n /** 이 공장이 안 쓰기로 했다. 조치 대상이 아니다. */\n NotUsed = 'not-used'\n}\n\n/** 결재냐 배정이냐 — 비었을 때 결과가 반대라 화면이 갈라 그려야 한다. */\nexport enum DoorKindValue {\n Approval = 'approval',\n Assignment = 'assignment'\n}\n\n/** 왜 그 상태인가 — 까닭마다 관리자가 할 일이 다르다(§`DoorReason`). */\nexport enum DoorReasonValue {\n NotUsed = 'not-used',\n Missing = 'missing',\n LineEmpty = 'line-empty',\n RoleEmpty = 'role-empty',\n PersonAbsent = 'person-absent',\n NoAssignee = 'no-assignee',\n LineFilled = 'line-filled',\n AssigneeBound = 'assignee-bound'\n}\n\n/** 결재선이 비면 이 문이 어떻게 하나 — 선언 그대로(ADR-0077 결정 1). */\nexport enum WhenNoLineValue {\n Run = 'run',\n Refuse = 'refuse',\n AsEstablished = 'as-established'\n}\n\n/** 결재를 요구하는 까닭 — `refuse` · `as-established` 문만. */\nexport enum WhenNoLineBecauseValue {\n Irreversible = 'irreversible',\n ApprovalIsTheAct = 'approval-is-the-act',\n LowersAControl = 'lowers-a-control'\n}\n\nregisterEnumType(DoorReasonValue, {\n name: 'DoorReason',\n description: 'Why a door stands where it does — each reason asks for a different fix'\n})\n\nregisterEnumType(WhenNoLineValue, {\n name: 'WhenNoLine',\n description: 'What an approval door does when its approval line is empty, as the application declared it'\n})\n\nregisterEnumType(WhenNoLineBecauseValue, {\n name: 'WhenNoLineBecause',\n description: 'Why an approval door will not run without an approval line'\n})\n\nregisterEnumType(DoorKindValue, {\n name: 'DoorKind',\n description: 'Whether the plant fills an approval line or an assignee role on this door'\n})\n\nregisterEnumType(DoorStandingValue, {\n name: 'DoorStanding',\n description: 'Whether this plant has an approval step standing for that form'\n})\n\n@ObjectType({ description: 'One approval form and whether this plant has approvers on it' })\nexport class WorkDoorType {\n @Field({ description: 'the form name — the key the callback is found by' })\n name: string\n\n @Field({ description: 'the translation key of its label — computed from the name, never declared' })\n labelKey: string\n\n @Field(type => DoorKindValue)\n kind: DoorKindValue\n\n @Field(type => DoorStandingValue)\n standing: DoorStandingValue\n\n @Field(type => DoorReasonValue)\n reason: DoorReasonValue\n\n @Field(type => Int, { description: 'how many approvers — zero for an assignment door or an empty line' })\n approvers: number\n\n @Field({ nullable: true, description: 'which domain the approval line came from — a plant may use its parent\\'s' })\n from?: string\n\n @Field(type => WhenNoLineValue, { nullable: true, description: 'approval doors only' })\n whenNoLine?: WhenNoLineValue\n\n @Field(type => WhenNoLineBecauseValue, { nullable: true, description: 'refuse and as-established doors only' })\n whenNoLineBecause?: WhenNoLineBecauseValue\n\n @Field({ nullable: true, description: 'the label key of the door whose approval line this one follows' })\n followsLabelKey?: string\n\n @Field({ nullable: true, description: 'the role an assignment door is bound to' })\n assigneeRole?: string\n\n @Field({ nullable: true, description: 'what the plant wrote when it said it does not use this door — shown as written' })\n notUsedBecause?: string\n}\n\n@ObjectType({ description: 'Every approval form this product installs, and where this plant stands on each' })\nexport class WorkDoorsType {\n @Field(type => [WorkDoorType])\n doors: WorkDoorType[]\n\n @Field(type => Int, { description: 'how many still need filling — not-used doors are never counted' })\n todo: number\n\n @Field(type => Int, { description: 'doors standing' })\n on: number\n\n @Field(type => Int, { description: 'forms installed with nobody on them' })\n off: number\n\n @Field(type => Int, { description: 'forms that never installed' })\n missing: number\n\n @Field(type => Int, { description: 'doors this plant said it does not use' })\n notUsed: number\n\n /**\n * 이 제품이 내놓는 역할 자리 중 **이 공장에 실제로 선 것.**\n *\n * ── 왜 문 목록에 자리 수가 붙나 ────────────────────────────────────────\n * 문이 비었다는 말만 하면 사람은 양식 화면으로 간다. 그런데 **고를 역할이 없으면 그 화면의\n * 결재선 칸이 빈 목록**이다. 헛걸음하고 돌아와서, 무엇이 잘못됐는지 모른 채 이 화면을 다시 본다.\n *\n * 두 수를 함께 내면 화면이 순서를 말할 수 있다 — 자리부터 세우고, 그다음에 결재선을 건다.\n */\n @Field(type => Int, { description: 'how many of the role seats this product offers actually exist here' })\n seatsStanding: number\n\n @Field(type => Int, { description: 'how many role seats this product offers' })\n seatsOffered: number\n}\n\n/**\n * 판단의 모양을 화면의 모양으로. 판단부의 낱말(`'line-empty'`)을 열거형 **값**으로 찾아 멤버를 싣는다 —\n * 값과 낱말이 같게 적혀 있으니 표를 따로 들지 않는다. 못 찾으면 던진다: 새 낱말이 전선에서 조용히\n * 사라지면 화면이 「모른다」조차 못 그린다.\n */\nexport function toDoorType(door: WorkDoor): WorkDoorType {\n return {\n name: door.name,\n labelKey: door.labelKey,\n kind: member(DoorKindValue, door.kind),\n standing: member(DoorStandingValue, door.standing),\n reason: member(DoorReasonValue, door.reason),\n approvers: door.approvers,\n from: door.from,\n whenNoLine: door.whenNoLine ? member(WhenNoLineValue, door.whenNoLine) : undefined,\n whenNoLineBecause: door.whenNoLineBecause ? member(WhenNoLineBecauseValue, door.whenNoLineBecause) : undefined,\n followsLabelKey: door.followsLabelKey,\n assigneeRole: door.assigneeRole,\n notUsedBecause: door.notUsedBecause\n }\n}\n\nfunction member<T extends Record<string, string>>(values: T, word: string): T[keyof T] {\n const found = Object.values(values).find(one => one === word)\n\n if (!found) {\n throw new Error(`work door: no wire value for \"${word}\"`)\n }\n\n return found as T[keyof T]\n}\n"]}