@entifix/core 0.5.0 → 0.7.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 (80) hide show
  1. package/dist/command-palette/command-matching.d.ts +1 -1
  2. package/dist/command-palette/command-matching.js +1 -1
  3. package/dist/command-palette/command-matching.js.map +1 -1
  4. package/dist/command-palette/command-source.d.ts +3 -3
  5. package/dist/command-palette/command-source.js.map +1 -1
  6. package/dist/entity-definition/decorators/use-case/use-case.d.ts +1 -1
  7. package/dist/entity-definition/decorators/use-case/use-case.js +1 -1
  8. package/dist/entity-definition/decorators/use-case/use-case.js.map +1 -1
  9. package/dist/entity-definition/describe/describe-child-columns.d.ts +1 -1
  10. package/dist/entity-definition/describe/describe-child-columns.js +1 -1
  11. package/dist/entity-definition/describe/describe-child-columns.js.map +1 -1
  12. package/dist/entity-definition/describe/describe-entity-columns.d.ts +24 -0
  13. package/dist/entity-definition/describe/describe-entity-columns.d.ts.map +1 -1
  14. package/dist/entity-definition/describe/describe-entity-columns.js +36 -2
  15. package/dist/entity-definition/describe/describe-entity-columns.js.map +1 -1
  16. package/dist/entity-definition/describe/describe-entity-use-cases.d.ts +2 -1
  17. package/dist/entity-definition/describe/describe-entity-use-cases.d.ts.map +1 -1
  18. package/dist/entity-definition/describe/describe-entity-use-cases.js +2 -1
  19. package/dist/entity-definition/describe/describe-entity-use-cases.js.map +1 -1
  20. package/dist/entity-definition/helpers/entity-definition-helpers.d.ts +2 -2
  21. package/dist/entity-definition/helpers/entity-definition-helpers.js +2 -2
  22. package/dist/entity-definition/helpers/entity-definition-helpers.js.map +1 -1
  23. package/dist/entity-definition/meta-entities/meta-accessor/meta-accessor.d.ts +17 -4
  24. package/dist/entity-definition/meta-entities/meta-accessor/meta-accessor.d.ts.map +1 -1
  25. package/dist/entity-definition/meta-entities/meta-accessor/meta-accessor.js +1 -0
  26. package/dist/entity-definition/meta-entities/meta-accessor/meta-accessor.js.map +1 -1
  27. package/dist/entity-definition/meta-entities/meta-use-case/meta-use-case.d.ts +1 -1
  28. package/dist/entity-definition/meta-entities/meta-use-case/meta-use-case.js.map +1 -1
  29. package/dist/entity-definition/reconstruct/clone-entity-draft.d.ts +1 -1
  30. package/dist/entity-definition/reconstruct/clone-entity-draft.d.ts.map +1 -1
  31. package/dist/entity-definition/reconstruct/clone-entity-draft.js +5 -2
  32. package/dist/entity-definition/reconstruct/clone-entity-draft.js.map +1 -1
  33. package/dist/entity-definition/reconstruct/reconstruct-entity.js +1 -1
  34. package/dist/entity-definition/reconstruct/reconstruct-entity.js.map +1 -1
  35. package/dist/envelope/types.d.ts +5 -7
  36. package/dist/envelope/types.d.ts.map +1 -1
  37. package/dist/envelope/types.js.map +1 -1
  38. package/dist/hierarchy/hierarchy.d.ts +77 -0
  39. package/dist/hierarchy/hierarchy.d.ts.map +1 -0
  40. package/dist/hierarchy/hierarchy.js +165 -0
  41. package/dist/hierarchy/hierarchy.js.map +1 -0
  42. package/dist/hierarchy/index.d.ts +2 -0
  43. package/dist/hierarchy/index.d.ts.map +1 -0
  44. package/dist/hierarchy/index.js +3 -0
  45. package/dist/hierarchy/index.js.map +1 -0
  46. package/dist/http/session-cookies.d.ts +2 -3
  47. package/dist/http/session-cookies.d.ts.map +1 -1
  48. package/dist/http/session-cookies.js +2 -3
  49. package/dist/http/session-cookies.js.map +1 -1
  50. package/dist/i18n/locales.js +2 -2
  51. package/dist/i18n/locales.js.map +1 -1
  52. package/dist/index.d.ts +1 -0
  53. package/dist/index.d.ts.map +1 -1
  54. package/dist/index.js +1 -0
  55. package/dist/index.js.map +1 -1
  56. package/dist/types/BulkOutcome.d.ts +1 -2
  57. package/dist/types/BulkOutcome.d.ts.map +1 -1
  58. package/dist/types/BulkOutcome.js.map +1 -1
  59. package/dist/types/EntityAction.d.ts +1 -2
  60. package/dist/types/EntityAction.d.ts.map +1 -1
  61. package/dist/types/EntityAction.js +1 -2
  62. package/dist/types/EntityAction.js.map +1 -1
  63. package/dist/types/EntityDraft.d.ts +2 -3
  64. package/dist/types/EntityDraft.d.ts.map +1 -1
  65. package/dist/types/EntityDraft.js.map +1 -1
  66. package/dist/types/EntityRowDraft.d.ts +2 -3
  67. package/dist/types/EntityRowDraft.d.ts.map +1 -1
  68. package/dist/types/EntityRowDraft.js +1 -1
  69. package/dist/types/EntityRowDraft.js.map +1 -1
  70. package/dist/types/EntitySelection.d.ts +2 -3
  71. package/dist/types/EntitySelection.d.ts.map +1 -1
  72. package/dist/types/EntitySelection.js.map +1 -1
  73. package/dist/wizard/wizard-definition.d.ts +5 -8
  74. package/dist/wizard/wizard-definition.d.ts.map +1 -1
  75. package/dist/wizard/wizard-definition.js +3 -5
  76. package/dist/wizard/wizard-definition.js.map +1 -1
  77. package/dist/wizard/wizard-state.d.ts +4 -7
  78. package/dist/wizard/wizard-state.d.ts.map +1 -1
  79. package/dist/wizard/wizard-state.js.map +1 -1
  80. package/package.json +1 -1
@@ -19,7 +19,7 @@ export declare function foldForMatch(text: string): string;
19
19
  *
20
20
  * Subsequence rather than substring, so `prbr` finds "Producto Bruto" and `numa`
21
21
  * finds "Nueva marca" — the thing that makes a palette feel fast. Deliberately
22
- * **not** scored: ADR 0040 fixed group order as the ranking, and a score inside
22
+ * **not** scored: group order is the ranking, and a score inside
23
23
  * a group would reshuffle it on every keystroke for no gain a person can learn.
24
24
  *
25
25
  * An empty term matches everything, which is what makes an unfiltered palette
@@ -20,7 +20,7 @@ import { EntifixLogicError } from '../base-entities/entifix-error/index.js';
20
20
  *
21
21
  * Subsequence rather than substring, so `prbr` finds "Producto Bruto" and `numa`
22
22
  * finds "Nueva marca" — the thing that makes a palette feel fast. Deliberately
23
- * **not** scored: ADR 0040 fixed group order as the ranking, and a score inside
23
+ * **not** scored: group order is the ranking, and a score inside
24
24
  * a group would reshuffle it on every keystroke for no gain a person can learn.
25
25
  *
26
26
  * An empty term matches everything, which is what makes an unfiltered palette
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/command-palette/command-matching.ts"],"sourcesContent":["import { EntifixLogicError } from '../base-entities/entifix-error/index.js';\nimport type { CommandOption } from './command-source.js';\n\n/**\n * Normalize a string for matching: case-folded, and with accents removed.\n *\n * ⚠️ The accent fold is correctness, not polish. The fleet ships Spanish as its\n * default locale, and without it `categoria` does not match `Categoría`, `marron`\n * does not match `Marrón`, and `articulo` does not match `Artículo` — so the\n * palette silently finds nothing for the most natural way to type most of its\n * own copy. Nobody reaches for the accented key in a search box.\n *\n * NFD splits a precomposed letter into its base plus a combining mark, and the\n * `\\p{Diacritic}` class is what then removes only the mark. `ñ` decomposes too,\n * so `nino` matches `Niño` — which is what a person typing quickly wants, even\n * though ñ is a letter of its own in Spanish rather than an accented n.\n */\nexport function foldForMatch(text: string): string {\n return text\n .normalize('NFD')\n .replace(/\\p{Diacritic}/gu, '')\n .toLocaleLowerCase();\n}\n\n/**\n * Does `text` contain every character of `term`, in order?\n *\n * Subsequence rather than substring, so `prbr` finds \"Producto Bruto\" and `numa`\n * finds \"Nueva marca\" — the thing that makes a palette feel fast. Deliberately\n * **not** scored: ADR 0040 fixed group order as the ranking, and a score inside\n * a group would reshuffle it on every keystroke for no gain a person can learn.\n *\n * An empty term matches everything, which is what makes an unfiltered palette\n * the same code path as a filtered one.\n */\nexport function subsequenceMatch(term: string, text: string): boolean {\n const needle = foldForMatch(term);\n if (needle === '') return true;\n\n const haystack = foldForMatch(text);\n let cursor = 0;\n for (const character of haystack) {\n if (character === needle[cursor]) cursor += 1;\n if (cursor === needle.length) return true;\n }\n return false;\n}\n\n/**\n * Does this option answer to `term`, by its label or by any of its keywords?\n *\n * The keywords are what let a Spanish command match an English word someone\n * typed — \"Nuevo producto\" answering to `new` — without translating the label.\n */\nexport function matchesCommand(term: string, option: CommandOption): boolean {\n if (subsequenceMatch(term, option.label)) return true;\n return (option.keywords ?? []).some(keyword =>\n subsequenceMatch(term, keyword),\n );\n}\n\n/**\n * Split a catalog-resolved keyword list into terms.\n *\n * `MetaUseCase.keywordsKey` is a catalog **key** rather than a `string[]` so the\n * terms are per-locale; what the catalog holds is therefore one string, and this\n * is the convention for reading it. Commas, because a keyword may itself be two\n * words (\"cerrar sesión\") and splitting on whitespace would break that into\n * terms that match nothing on their own.\n *\n * Empty entries are dropped rather than kept as `''`, which would match every\n * option through {@link subsequenceMatch}'s empty-term rule.\n */\nexport function parseKeywords(value: string | undefined): string[] {\n if (value === undefined) return [];\n return value\n .split(',')\n .map(keyword => keyword.trim())\n .filter(keyword => keyword !== '');\n}\n\n/** Which sources a typed term is asking for. */\nexport type CommandScope = 'all' | 'commands' | 'records';\n\n/** A term, split into the scope its prefix asked for and the rest. */\nexport interface ParsedCommandTerm {\n readonly scope: CommandScope;\n readonly term: string;\n}\n\n/**\n * `>` narrows to commands, `#` to records, anything else searches both.\n *\n * A grammar for *entry* rather than for depth: descending into a sub-picker is a\n * page stack, not a third prefix, because a prefix a person has to remember is\n * worse than a list they can see. Two characters only, and both are the ones\n * VS Code and GitHub already trained people on.\n *\n * The prefix is stripped and the remainder trimmed at the front only — a\n * trailing space is something the person is still typing, and eating it would\n * make the list jump between keystrokes.\n */\nexport function parseCommandTerm(raw: string): ParsedCommandTerm {\n if (raw.startsWith('>')) {\n return { scope: 'commands', term: raw.slice(1).trimStart() };\n }\n if (raw.startsWith('#')) {\n return { scope: 'records', term: raw.slice(1).trimStart() };\n }\n return { scope: 'all', term: raw };\n}\n\n/**\n * The one thing selecting an option does.\n *\n * An option declaring none is a line that looks selectable and does nothing;\n * one declaring two is a line whose behaviour depends on the order a renderer\n * happens to check its members in. Both are silent, so this throws — at the\n * first render of the palette rather than on the click, which is the posture\n * `surfaceFor` and `assertSearchable` already take for the same reason.\n */\nexport function commandEffectOf(\n option: CommandOption,\n): 'href' | 'run' | 'push' {\n const declared = (['href', 'run', 'push'] as const).filter(\n effect => option[effect] !== undefined,\n );\n\n if (declared.length !== 1) {\n throw new EntifixLogicError(\n `The command \"${option.id}\" declares ${declared.length} effects (${declared.join(', ') || 'none'}), and must declare exactly one. ` +\n 'Give it an `href` to navigate, a `run` to invoke, or a `push` to open another page.',\n undefined,\n { id: option.id, declared },\n );\n }\n\n return declared[0];\n}\n"],"names":["EntifixLogicError","foldForMatch","text","normalize","replace","toLocaleLowerCase","subsequenceMatch","term","needle","haystack","cursor","character","length","matchesCommand","option","label","keywords","some","keyword","parseKeywords","value","undefined","split","map","trim","filter","parseCommandTerm","raw","startsWith","scope","slice","trimStart","commandEffectOf","declared","effect","id","join"],"mappings":"AAAA,SAASA,iBAAiB,QAAQ,0CAA0C;AAG5E;;;;;;;;;;;;;CAaC,GACD,OAAO,SAASC,aAAaC,IAAY;IACvC,OAAOA,KACJC,SAAS,CAAC,OACVC,OAAO,CAAC,q6FAAmB,IAC3BC,iBAAiB;AACtB;AAEA;;;;;;;;;;CAUC,GACD,OAAO,SAASC,iBAAiBC,IAAY,EAAEL,IAAY;IACzD,MAAMM,SAASP,aAAaM;IAC5B,IAAIC,WAAW,IAAI,OAAO;IAE1B,MAAMC,WAAWR,aAAaC;IAC9B,IAAIQ,SAAS;IACb,KAAK,MAAMC,aAAaF,SAAU;QAChC,IAAIE,cAAcH,MAAM,CAACE,OAAO,EAAEA,UAAU;QAC5C,IAAIA,WAAWF,OAAOI,MAAM,EAAE,OAAO;IACvC;IACA,OAAO;AACT;AAEA;;;;;CAKC,GACD,OAAO,SAASC,eAAeN,IAAY,EAAEO,MAAqB;QAExDA;IADR,IAAIR,iBAAiBC,MAAMO,OAAOC,KAAK,GAAG,OAAO;IACjD,OAAO,EAACD,mBAAAA,OAAOE,QAAQ,YAAfF,mBAAmB,EAAE,EAAEG,IAAI,CAACC,CAAAA,UAClCZ,iBAAiBC,MAAMW;AAE3B;AAEA;;;;;;;;;;;CAWC,GACD,OAAO,SAASC,cAAcC,KAAyB;IACrD,IAAIA,UAAUC,WAAW,OAAO,EAAE;IAClC,OAAOD,MACJE,KAAK,CAAC,KACNC,GAAG,CAACL,CAAAA,UAAWA,QAAQM,IAAI,IAC3BC,MAAM,CAACP,CAAAA,UAAWA,YAAY;AACnC;AAWA;;;;;;;;;;;CAWC,GACD,OAAO,SAASQ,iBAAiBC,GAAW;IAC1C,IAAIA,IAAIC,UAAU,CAAC,MAAM;QACvB,OAAO;YAAEC,OAAO;YAAYtB,MAAMoB,IAAIG,KAAK,CAAC,GAAGC,SAAS;QAAG;IAC7D;IACA,IAAIJ,IAAIC,UAAU,CAAC,MAAM;QACvB,OAAO;YAAEC,OAAO;YAAWtB,MAAMoB,IAAIG,KAAK,CAAC,GAAGC,SAAS;QAAG;IAC5D;IACA,OAAO;QAAEF,OAAO;QAAOtB,MAAMoB;IAAI;AACnC;AAEA;;;;;;;;CAQC,GACD,OAAO,SAASK,gBACdlB,MAAqB;IAErB,MAAMmB,WAAW,AAAC;QAAC;QAAQ;QAAO;KAAO,CAAWR,MAAM,CACxDS,CAAAA,SAAUpB,MAAM,CAACoB,OAAO,KAAKb;IAG/B,IAAIY,SAASrB,MAAM,KAAK,GAAG;QACzB,MAAM,IAAIZ,kBACR,CAAC,aAAa,EAAEc,OAAOqB,EAAE,CAAC,WAAW,EAAEF,SAASrB,MAAM,CAAC,UAAU,EAAEqB,SAASG,IAAI,CAAC,SAAS,OAAO,iCAAiC,CAAC,GACjI,uFACFf,WACA;YAAEc,IAAIrB,OAAOqB,EAAE;YAAEF;QAAS;IAE9B;IAEA,OAAOA,QAAQ,CAAC,EAAE;AACpB"}
1
+ {"version":3,"sources":["../../src/command-palette/command-matching.ts"],"sourcesContent":["import { EntifixLogicError } from '../base-entities/entifix-error/index.js';\nimport type { CommandOption } from './command-source.js';\n\n/**\n * Normalize a string for matching: case-folded, and with accents removed.\n *\n * ⚠️ The accent fold is correctness, not polish. The fleet ships Spanish as its\n * default locale, and without it `categoria` does not match `Categoría`, `marron`\n * does not match `Marrón`, and `articulo` does not match `Artículo` — so the\n * palette silently finds nothing for the most natural way to type most of its\n * own copy. Nobody reaches for the accented key in a search box.\n *\n * NFD splits a precomposed letter into its base plus a combining mark, and the\n * `\\p{Diacritic}` class is what then removes only the mark. `ñ` decomposes too,\n * so `nino` matches `Niño` — which is what a person typing quickly wants, even\n * though ñ is a letter of its own in Spanish rather than an accented n.\n */\nexport function foldForMatch(text: string): string {\n return text\n .normalize('NFD')\n .replace(/\\p{Diacritic}/gu, '')\n .toLocaleLowerCase();\n}\n\n/**\n * Does `text` contain every character of `term`, in order?\n *\n * Subsequence rather than substring, so `prbr` finds \"Producto Bruto\" and `numa`\n * finds \"Nueva marca\" — the thing that makes a palette feel fast. Deliberately\n * **not** scored: group order is the ranking, and a score inside\n * a group would reshuffle it on every keystroke for no gain a person can learn.\n *\n * An empty term matches everything, which is what makes an unfiltered palette\n * the same code path as a filtered one.\n */\nexport function subsequenceMatch(term: string, text: string): boolean {\n const needle = foldForMatch(term);\n if (needle === '') return true;\n\n const haystack = foldForMatch(text);\n let cursor = 0;\n for (const character of haystack) {\n if (character === needle[cursor]) cursor += 1;\n if (cursor === needle.length) return true;\n }\n return false;\n}\n\n/**\n * Does this option answer to `term`, by its label or by any of its keywords?\n *\n * The keywords are what let a Spanish command match an English word someone\n * typed — \"Nuevo producto\" answering to `new` — without translating the label.\n */\nexport function matchesCommand(term: string, option: CommandOption): boolean {\n if (subsequenceMatch(term, option.label)) return true;\n return (option.keywords ?? []).some(keyword =>\n subsequenceMatch(term, keyword),\n );\n}\n\n/**\n * Split a catalog-resolved keyword list into terms.\n *\n * `MetaUseCase.keywordsKey` is a catalog **key** rather than a `string[]` so the\n * terms are per-locale; what the catalog holds is therefore one string, and this\n * is the convention for reading it. Commas, because a keyword may itself be two\n * words (\"cerrar sesión\") and splitting on whitespace would break that into\n * terms that match nothing on their own.\n *\n * Empty entries are dropped rather than kept as `''`, which would match every\n * option through {@link subsequenceMatch}'s empty-term rule.\n */\nexport function parseKeywords(value: string | undefined): string[] {\n if (value === undefined) return [];\n return value\n .split(',')\n .map(keyword => keyword.trim())\n .filter(keyword => keyword !== '');\n}\n\n/** Which sources a typed term is asking for. */\nexport type CommandScope = 'all' | 'commands' | 'records';\n\n/** A term, split into the scope its prefix asked for and the rest. */\nexport interface ParsedCommandTerm {\n readonly scope: CommandScope;\n readonly term: string;\n}\n\n/**\n * `>` narrows to commands, `#` to records, anything else searches both.\n *\n * A grammar for *entry* rather than for depth: descending into a sub-picker is a\n * page stack, not a third prefix, because a prefix a person has to remember is\n * worse than a list they can see. Two characters only, and both are the ones\n * VS Code and GitHub already trained people on.\n *\n * The prefix is stripped and the remainder trimmed at the front only — a\n * trailing space is something the person is still typing, and eating it would\n * make the list jump between keystrokes.\n */\nexport function parseCommandTerm(raw: string): ParsedCommandTerm {\n if (raw.startsWith('>')) {\n return { scope: 'commands', term: raw.slice(1).trimStart() };\n }\n if (raw.startsWith('#')) {\n return { scope: 'records', term: raw.slice(1).trimStart() };\n }\n return { scope: 'all', term: raw };\n}\n\n/**\n * The one thing selecting an option does.\n *\n * An option declaring none is a line that looks selectable and does nothing;\n * one declaring two is a line whose behaviour depends on the order a renderer\n * happens to check its members in. Both are silent, so this throws — at the\n * first render of the palette rather than on the click, which is the posture\n * `surfaceFor` and `assertSearchable` already take for the same reason.\n */\nexport function commandEffectOf(\n option: CommandOption,\n): 'href' | 'run' | 'push' {\n const declared = (['href', 'run', 'push'] as const).filter(\n effect => option[effect] !== undefined,\n );\n\n if (declared.length !== 1) {\n throw new EntifixLogicError(\n `The command \"${option.id}\" declares ${declared.length} effects (${declared.join(', ') || 'none'}), and must declare exactly one. ` +\n 'Give it an `href` to navigate, a `run` to invoke, or a `push` to open another page.',\n undefined,\n { id: option.id, declared },\n );\n }\n\n return declared[0];\n}\n"],"names":["EntifixLogicError","foldForMatch","text","normalize","replace","toLocaleLowerCase","subsequenceMatch","term","needle","haystack","cursor","character","length","matchesCommand","option","label","keywords","some","keyword","parseKeywords","value","undefined","split","map","trim","filter","parseCommandTerm","raw","startsWith","scope","slice","trimStart","commandEffectOf","declared","effect","id","join"],"mappings":"AAAA,SAASA,iBAAiB,QAAQ,0CAA0C;AAG5E;;;;;;;;;;;;;CAaC,GACD,OAAO,SAASC,aAAaC,IAAY;IACvC,OAAOA,KACJC,SAAS,CAAC,OACVC,OAAO,CAAC,q6FAAmB,IAC3BC,iBAAiB;AACtB;AAEA;;;;;;;;;;CAUC,GACD,OAAO,SAASC,iBAAiBC,IAAY,EAAEL,IAAY;IACzD,MAAMM,SAASP,aAAaM;IAC5B,IAAIC,WAAW,IAAI,OAAO;IAE1B,MAAMC,WAAWR,aAAaC;IAC9B,IAAIQ,SAAS;IACb,KAAK,MAAMC,aAAaF,SAAU;QAChC,IAAIE,cAAcH,MAAM,CAACE,OAAO,EAAEA,UAAU;QAC5C,IAAIA,WAAWF,OAAOI,MAAM,EAAE,OAAO;IACvC;IACA,OAAO;AACT;AAEA;;;;;CAKC,GACD,OAAO,SAASC,eAAeN,IAAY,EAAEO,MAAqB;QAExDA;IADR,IAAIR,iBAAiBC,MAAMO,OAAOC,KAAK,GAAG,OAAO;IACjD,OAAO,EAACD,mBAAAA,OAAOE,QAAQ,YAAfF,mBAAmB,EAAE,EAAEG,IAAI,CAACC,CAAAA,UAClCZ,iBAAiBC,MAAMW;AAE3B;AAEA;;;;;;;;;;;CAWC,GACD,OAAO,SAASC,cAAcC,KAAyB;IACrD,IAAIA,UAAUC,WAAW,OAAO,EAAE;IAClC,OAAOD,MACJE,KAAK,CAAC,KACNC,GAAG,CAACL,CAAAA,UAAWA,QAAQM,IAAI,IAC3BC,MAAM,CAACP,CAAAA,UAAWA,YAAY;AACnC;AAWA;;;;;;;;;;;CAWC,GACD,OAAO,SAASQ,iBAAiBC,GAAW;IAC1C,IAAIA,IAAIC,UAAU,CAAC,MAAM;QACvB,OAAO;YAAEC,OAAO;YAAYtB,MAAMoB,IAAIG,KAAK,CAAC,GAAGC,SAAS;QAAG;IAC7D;IACA,IAAIJ,IAAIC,UAAU,CAAC,MAAM;QACvB,OAAO;YAAEC,OAAO;YAAWtB,MAAMoB,IAAIG,KAAK,CAAC,GAAGC,SAAS;QAAG;IAC5D;IACA,OAAO;QAAEF,OAAO;QAAOtB,MAAMoB;IAAI;AACnC;AAEA;;;;;;;;CAQC,GACD,OAAO,SAASK,gBACdlB,MAAqB;IAErB,MAAMmB,WAAW,AAAC;QAAC;QAAQ;QAAO;KAAO,CAAWR,MAAM,CACxDS,CAAAA,SAAUpB,MAAM,CAACoB,OAAO,KAAKb;IAG/B,IAAIY,SAASrB,MAAM,KAAK,GAAG;QACzB,MAAM,IAAIZ,kBACR,CAAC,aAAa,EAAEc,OAAOqB,EAAE,CAAC,WAAW,EAAEF,SAASrB,MAAM,CAAC,UAAU,EAAEqB,SAASG,IAAI,CAAC,SAAS,OAAO,iCAAiC,CAAC,GACjI,uFACFf,WACA;YAAEc,IAAIrB,OAAOqB,EAAE;YAAEF;QAAS;IAE9B;IAEA,OAAOA,QAAQ,CAAC,EAAE;AACpB"}
@@ -12,8 +12,8 @@ export type CommandUnavailableSeverity = 'scope' | 'reachability';
12
12
  /**
13
13
  * A source that produced no options, and why.
14
14
  *
15
- * The two severities must not be conflated, which is the same split ADR 0040's
16
- * `RecordSearchUnavailableReason` draws and the reason this member is not a
15
+ * The two severities must not be conflated, which is the same split the record
16
+ * search's `RecordSearchUnavailableReason` draws and the reason this member is not a
17
17
  * bare string. `scope` is the **normal** state for some callers — an operator
18
18
  * holds no membership, so every tenant-plane search answers `409` on every
19
19
  * keystroke — while `reachability` means something is actually wrong. Rendering
@@ -84,7 +84,7 @@ export interface CommandGroup {
84
84
  *
85
85
  * Several groups, not one, because the record search fans out over four entities
86
86
  * and each is its own heading — flattening them here would make the palette
87
- * re-derive what ADR 0040 already grouped.
87
+ * re-derive what the record search already grouped.
88
88
  */
89
89
  export interface CommandSource {
90
90
  readonly key: string;
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/command-palette/command-source.ts"],"sourcesContent":["/**\n * What a command palette renders, as state rather than as a query.\n *\n * The same contract `EntityLinkSource` is, and for the same boundary reason: the\n * hooks that fetch live in the shell layer, the component that renders lives in\n * `@entifix/react-controls`, and those two may not import each other. It carries\n * no Effect, no React and no router — a source hands over resolved copy and a\n * thunk, never a key and never a `push()`.\n */\n\n/** How a group's absence is explained. */\nexport type CommandUnavailableSeverity = 'scope' | 'reachability';\n\n/**\n * A source that produced no options, and why.\n *\n * The two severities must not be conflated, which is the same split ADR 0040's\n * `RecordSearchUnavailableReason` draws and the reason this member is not a\n * bare string. `scope` is the **normal** state for some callers — an operator\n * holds no membership, so every tenant-plane search answers `409` on every\n * keystroke — while `reachability` means something is actually wrong. Rendering\n * a warning for the ordinary case teaches people to ignore the one that matters.\n */\nexport interface CommandUnavailable {\n /** Already-resolved copy. The source owns the catalog lookup. */\n readonly message: string;\n readonly severity: CommandUnavailableSeverity;\n}\n\n/** What selecting an option does. Exactly one arm is populated. */\nexport interface CommandEffect {\n /**\n * Navigate. **Already locale-prefixed** by the source, because the palette is\n * presentational and a control may not reach for a router or a locale.\n */\n readonly href?: string;\n /** Invoke. The source owns the request, the optimism and the error. */\n readonly run?: () => void | Promise<void>;\n /** Descend into another {@link CommandPage} by id — this is the page stack. */\n readonly push?: string;\n}\n\n/** Ask before running. Resolved copy, like everything else that crosses here. */\nexport interface CommandConfirm {\n readonly tone: 'destructive' | 'neutral';\n readonly message: string;\n}\n\n/** One selectable line. */\nexport interface CommandOption extends CommandEffect {\n /** Unique within the whole palette — it is also the recency key. */\n readonly id: string;\n /** Resolved copy. A source that holds a catalog key resolves it before here. */\n readonly label: string;\n /** A second line: a code, a role, the record's entity. */\n readonly sublabel?: string;\n /**\n * Extra terms this option matches on, so a Spanish command still answers to\n * the English word someone typed. Raycast's mechanism, and the reason\n * `MetaUseCase` carries a `keywordsKey` at all.\n */\n readonly keywords?: readonly string[];\n /** Trailing text — a shortcut hint, the source's own name. */\n readonly hint?: string;\n readonly confirm?: CommandConfirm;\n}\n\n/** One heading and the options under it. */\nexport interface CommandGroup {\n readonly key: string;\n /** Resolved copy. */\n readonly label: string;\n readonly options: readonly CommandOption[];\n /**\n * Matches **before** any per-group limit, so \"N más\" is renderable. Omitted\n * when the source did not count — never defaulted to `options.length`, which\n * would claim there is no more when nobody looked.\n */\n readonly total?: number;\n readonly isLoading: boolean;\n /**\n * Set when this group could not be produced. A group carrying this is still\n * rendered: an omitted group reads as \"nothing matched\", which is a confident\n * claim nobody is in a position to make about a source they could not reach.\n */\n readonly unavailable?: CommandUnavailable;\n}\n\n/**\n * One contributor's whole answer.\n *\n * Several groups, not one, because the record search fans out over four entities\n * and each is its own heading — flattening them here would make the palette\n * re-derive what ADR 0040 already grouped.\n */\nexport interface CommandSource {\n readonly key: string;\n readonly groups: readonly CommandGroup[];\n}\n\n/**\n * One level of the palette.\n *\n * Depth is a **stack of pages** rather than nested prefixes: selecting \"Nuevo…\"\n * makes the palette *become* the entity picker, which is Raycast's and Linear's\n * arrangement and reads far better than a second grammar character.\n */\nexport interface CommandPage {\n readonly id: string;\n /** Shown as a chip beside the input on any page but the root. */\n readonly title?: string;\n readonly placeholder: string;\n readonly sources: readonly CommandSource[];\n}\n\n/** The id of the page a palette opens on. */\nexport const ROOT_COMMAND_PAGE = 'root';\n"],"names":["ROOT_COMMAND_PAGE"],"mappings":"AAAA;;;;;;;;CAQC,GAED,wCAAwC,GAyGxC,2CAA2C,GAC3C,OAAO,MAAMA,oBAAoB,OAAO"}
1
+ {"version":3,"sources":["../../src/command-palette/command-source.ts"],"sourcesContent":["/**\n * What a command palette renders, as state rather than as a query.\n *\n * The same contract `EntityLinkSource` is, and for the same boundary reason: the\n * hooks that fetch live in the shell layer, the component that renders lives in\n * `@entifix/react-controls`, and those two may not import each other. It carries\n * no Effect, no React and no router — a source hands over resolved copy and a\n * thunk, never a key and never a `push()`.\n */\n\n/** How a group's absence is explained. */\nexport type CommandUnavailableSeverity = 'scope' | 'reachability';\n\n/**\n * A source that produced no options, and why.\n *\n * The two severities must not be conflated, which is the same split the record\n * search's `RecordSearchUnavailableReason` draws and the reason this member is not a\n * bare string. `scope` is the **normal** state for some callers — an operator\n * holds no membership, so every tenant-plane search answers `409` on every\n * keystroke — while `reachability` means something is actually wrong. Rendering\n * a warning for the ordinary case teaches people to ignore the one that matters.\n */\nexport interface CommandUnavailable {\n /** Already-resolved copy. The source owns the catalog lookup. */\n readonly message: string;\n readonly severity: CommandUnavailableSeverity;\n}\n\n/** What selecting an option does. Exactly one arm is populated. */\nexport interface CommandEffect {\n /**\n * Navigate. **Already locale-prefixed** by the source, because the palette is\n * presentational and a control may not reach for a router or a locale.\n */\n readonly href?: string;\n /** Invoke. The source owns the request, the optimism and the error. */\n readonly run?: () => void | Promise<void>;\n /** Descend into another {@link CommandPage} by id — this is the page stack. */\n readonly push?: string;\n}\n\n/** Ask before running. Resolved copy, like everything else that crosses here. */\nexport interface CommandConfirm {\n readonly tone: 'destructive' | 'neutral';\n readonly message: string;\n}\n\n/** One selectable line. */\nexport interface CommandOption extends CommandEffect {\n /** Unique within the whole palette — it is also the recency key. */\n readonly id: string;\n /** Resolved copy. A source that holds a catalog key resolves it before here. */\n readonly label: string;\n /** A second line: a code, a role, the record's entity. */\n readonly sublabel?: string;\n /**\n * Extra terms this option matches on, so a Spanish command still answers to\n * the English word someone typed. Raycast's mechanism, and the reason\n * `MetaUseCase` carries a `keywordsKey` at all.\n */\n readonly keywords?: readonly string[];\n /** Trailing text — a shortcut hint, the source's own name. */\n readonly hint?: string;\n readonly confirm?: CommandConfirm;\n}\n\n/** One heading and the options under it. */\nexport interface CommandGroup {\n readonly key: string;\n /** Resolved copy. */\n readonly label: string;\n readonly options: readonly CommandOption[];\n /**\n * Matches **before** any per-group limit, so \"N más\" is renderable. Omitted\n * when the source did not count — never defaulted to `options.length`, which\n * would claim there is no more when nobody looked.\n */\n readonly total?: number;\n readonly isLoading: boolean;\n /**\n * Set when this group could not be produced. A group carrying this is still\n * rendered: an omitted group reads as \"nothing matched\", which is a confident\n * claim nobody is in a position to make about a source they could not reach.\n */\n readonly unavailable?: CommandUnavailable;\n}\n\n/**\n * One contributor's whole answer.\n *\n * Several groups, not one, because the record search fans out over four entities\n * and each is its own heading — flattening them here would make the palette\n * re-derive what the record search already grouped.\n */\nexport interface CommandSource {\n readonly key: string;\n readonly groups: readonly CommandGroup[];\n}\n\n/**\n * One level of the palette.\n *\n * Depth is a **stack of pages** rather than nested prefixes: selecting \"Nuevo…\"\n * makes the palette *become* the entity picker, which is Raycast's and Linear's\n * arrangement and reads far better than a second grammar character.\n */\nexport interface CommandPage {\n readonly id: string;\n /** Shown as a chip beside the input on any page but the root. */\n readonly title?: string;\n readonly placeholder: string;\n readonly sources: readonly CommandSource[];\n}\n\n/** The id of the page a palette opens on. */\nexport const ROOT_COMMAND_PAGE = 'root';\n"],"names":["ROOT_COMMAND_PAGE"],"mappings":"AAAA;;;;;;;;CAQC,GAED,wCAAwC,GAyGxC,2CAA2C,GAC3C,OAAO,MAAMA,oBAAoB,OAAO"}
@@ -12,7 +12,7 @@ import { MetaUseCaseOptions } from '../../meta-entities/meta-use-case/index.js';
12
12
  *
13
13
  * The `const` type parameter keeps `key` a literal at the declaration site.
14
14
  * Write it inline — `@useCase({ ...DESCRIPTOR })` would compile and would make
15
- * the source scan in `@r10c/slices` stop seeing the declaration.
15
+ * a host's source scan stop seeing the declaration.
16
16
  */
17
17
  export declare function useCase<const TKey extends string, TEntity extends Entity>(options: MetaUseCaseOptions<TKey, TEntity>): (_target: unknown, context: ClassDecoratorContext) => void;
18
18
  //# sourceMappingURL=use-case.d.ts.map
@@ -12,7 +12,7 @@ import { MetaUseCase } from '../../meta-entities/meta-use-case/index.js';
12
12
  *
13
13
  * The `const` type parameter keeps `key` a literal at the declaration site.
14
14
  * Write it inline — `@useCase({ ...DESCRIPTOR })` would compile and would make
15
- * the source scan in `@r10c/slices` stop seeing the declaration.
15
+ * a host's source scan stop seeing the declaration.
16
16
  */ export function useCase(options) {
17
17
  return (_target, context)=>{
18
18
  appendMetaUseCase(options.entity, new MetaUseCase(options));
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/entity-definition/decorators/use-case/use-case.ts"],"sourcesContent":["import type { Entity } from '../../../types/Entity.js';\nimport {\n appendMetaUseCase,\n setMetaUseCaseBinding,\n} from '../../helpers/index.js';\nimport {\n MetaUseCase,\n MetaUseCaseOptions,\n} from '../../meta-entities/meta-use-case/index.js';\n\n/**\n * Declares a use case: a verb on an entity beyond the `read`/`write`/`delete`\n * triple, with everything a surface needs to render it.\n *\n * It writes twice, and the two writes have different jobs. The descriptor is\n * appended to the **entity's** metadata, because that is what a form, a table or\n * the palette holds. The entity/verb pair is written to the **class's own**\n * metadata, which is what lets `permissionForUseCase(SomeUC)` derive the\n * permission from one argument.\n *\n * The `const` type parameter keeps `key` a literal at the declaration site.\n * Write it inline — `@useCase({ ...DESCRIPTOR })` would compile and would make\n * the source scan in `@r10c/slices` stop seeing the declaration.\n */\nexport function useCase<const TKey extends string, TEntity extends Entity>(\n options: MetaUseCaseOptions<TKey, TEntity>,\n) {\n return (_target: unknown, context: ClassDecoratorContext) => {\n appendMetaUseCase(options.entity, new MetaUseCase(options));\n setMetaUseCaseBinding(context.metadata, {\n entity: options.entity,\n key: options.key,\n });\n };\n}\n"],"names":["appendMetaUseCase","setMetaUseCaseBinding","MetaUseCase","useCase","options","_target","context","entity","metadata","key"],"mappings":"AACA,SACEA,iBAAiB,EACjBC,qBAAqB,QAChB,yBAAyB;AAChC,SACEC,WAAW,QAEN,6CAA6C;AAEpD;;;;;;;;;;;;;CAaC,GACD,OAAO,SAASC,QACdC,OAA0C;IAE1C,OAAO,CAACC,SAAkBC;QACxBN,kBAAkBI,QAAQG,MAAM,EAAE,IAAIL,YAAYE;QAClDH,sBAAsBK,QAAQE,QAAQ,EAAE;YACtCD,QAAQH,QAAQG,MAAM;YACtBE,KAAKL,QAAQK,GAAG;QAClB;IACF;AACF"}
1
+ {"version":3,"sources":["../../../../src/entity-definition/decorators/use-case/use-case.ts"],"sourcesContent":["import type { Entity } from '../../../types/Entity.js';\nimport {\n appendMetaUseCase,\n setMetaUseCaseBinding,\n} from '../../helpers/index.js';\nimport {\n MetaUseCase,\n MetaUseCaseOptions,\n} from '../../meta-entities/meta-use-case/index.js';\n\n/**\n * Declares a use case: a verb on an entity beyond the `read`/`write`/`delete`\n * triple, with everything a surface needs to render it.\n *\n * It writes twice, and the two writes have different jobs. The descriptor is\n * appended to the **entity's** metadata, because that is what a form, a table or\n * the palette holds. The entity/verb pair is written to the **class's own**\n * metadata, which is what lets `permissionForUseCase(SomeUC)` derive the\n * permission from one argument.\n *\n * The `const` type parameter keeps `key` a literal at the declaration site.\n * Write it inline — `@useCase({ ...DESCRIPTOR })` would compile and would make\n * a host's source scan stop seeing the declaration.\n */\nexport function useCase<const TKey extends string, TEntity extends Entity>(\n options: MetaUseCaseOptions<TKey, TEntity>,\n) {\n return (_target: unknown, context: ClassDecoratorContext) => {\n appendMetaUseCase(options.entity, new MetaUseCase(options));\n setMetaUseCaseBinding(context.metadata, {\n entity: options.entity,\n key: options.key,\n });\n };\n}\n"],"names":["appendMetaUseCase","setMetaUseCaseBinding","MetaUseCase","useCase","options","_target","context","entity","metadata","key"],"mappings":"AACA,SACEA,iBAAiB,EACjBC,qBAAqB,QAChB,yBAAyB;AAChC,SACEC,WAAW,QAEN,6CAA6C;AAEpD;;;;;;;;;;;;;CAaC,GACD,OAAO,SAASC,QACdC,OAA0C;IAE1C,OAAO,CAACC,SAAkBC;QACxBN,kBAAkBI,QAAQG,MAAM,EAAE,IAAIL,YAAYE;QAClDH,sBAAsBK,QAAQE,QAAQ,EAAE;YACtCD,QAAQH,QAAQG,MAAM;YACtBE,KAAKL,QAAQK,GAAG;QAClB;IACF;AACF"}
@@ -5,7 +5,7 @@ import { type EntityFieldDescriptor } from './describe-entity-columns.js';
5
5
  *
6
6
  * Thin over {@link describeEntityColumns}, which already accepts a
7
7
  * `ChildConstructor` — a child is described by its accessors and needs no
8
- * `@entity()` ([ADR 0034](../../../../../../../docs/adr/0034-composition-metadata.md)).
8
+ * `@entity()`.
9
9
  * What this adds is the one rule a row has and an entity does not.
10
10
  *
11
11
  * **A child may not declare `$key`.** That name is the row's minted client
@@ -6,7 +6,7 @@ import { describeEntityColumns } from './describe-entity-columns.js';
6
6
  *
7
7
  * Thin over {@link describeEntityColumns}, which already accepts a
8
8
  * `ChildConstructor` — a child is described by its accessors and needs no
9
- * `@entity()` ([ADR 0034](../../../../../../../docs/adr/0034-composition-metadata.md)).
9
+ * `@entity()`.
10
10
  * What this adds is the one rule a row has and an entity does not.
11
11
  *
12
12
  * **A child may not declare `$key`.** That name is the row's minted client
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/entity-definition/describe/describe-child-columns.ts"],"sourcesContent":["import { EntifixBuildError } from '../../base-entities/entifix-error/index.js';\nimport { ROW_KEY } from '../../types/EntityRowDraft.js';\nimport type { ChildConstructor } from '../meta-entities/meta-accessor/index.js';\nimport {\n describeEntityColumns,\n type EntityFieldDescriptor,\n} from './describe-entity-columns.js';\n\n/**\n * The columns of one row of an owned collection.\n *\n * Thin over {@link describeEntityColumns}, which already accepts a\n * `ChildConstructor` — a child is described by its accessors and needs no\n * `@entity()` ([ADR 0034](../../../../../../../docs/adr/0034-composition-metadata.md)).\n * What this adds is the one rule a row has and an entity does not.\n *\n * **A child may not declare `$key`.** That name is the row's minted client\n * identity, and a member of the same name would be overwritten by it — the row\n * would re-key on every keystroke, drop focus mid-word, and address another\n * row's errors. Throwing is the only honest option: silently renaming the\n * member would write the wrong column, and preferring the member over the key\n * would leave the grid with no identity at all.\n *\n * **Nor a `localized` member**, for a reason of the same kind: a row's draft is a\n * flat string map, and a text per locale has no single cell to live in.\n *\n * `EntifixBuildError` rather than a logic error, because this is a declaration\n * fault in the entity — the same class `assertNotQueryable` raises when a\n * collection claims to be sortable.\n */\nexport function describeChildColumns<TChild extends object>(\n childType: ChildConstructor<TChild>,\n sample?: TChild,\n): EntityFieldDescriptor[] {\n const descriptors = describeEntityColumns(childType, sample);\n const reserved = descriptors.find(descriptor => descriptor.name === ROW_KEY);\n\n if (reserved !== undefined) {\n throw new EntifixBuildError(\n `A composition child declares a member named \\`${ROW_KEY}\\`, which is reserved ` +\n 'for the row’s client-side key. Rename the member: the key and the member ' +\n 'cannot share a name, and the row would lose its identity on every edit.',\n undefined,\n { member: ROW_KEY },\n );\n }\n\n // A row's draft is a flat string map, which is what keeps a draft's type\n // bounded for the form engine; a text per locale has no cell to live in.\n const localized = descriptors.find(\n descriptor => descriptor.type === 'localized',\n );\n if (localized !== undefined) {\n throw new EntifixBuildError(\n `A composition child declares \"${localized.name}\" as localized, which a row ` +\n 'cannot hold: a row is a flat string map. Keep the localized text on the ' +\n 'entity, or make the child an entity of its own.',\n undefined,\n { member: localized.name },\n );\n }\n\n return descriptors;\n}\n\n/**\n * The columns a detail grid actually edits.\n *\n * A read-only member still has a column — it is shown, just not written, exactly\n * as a form shows one. What is dropped is a **nested collection**: a child that\n * owns rows of its own would need a grid inside a grid, and `reconstructChild`\n * is one level deep by decision. Dropping it here rather than rendering a\n * disabled cell keeps that decision in one place.\n */\nexport function editableChildColumns(\n descriptors: readonly EntityFieldDescriptor[],\n): EntityFieldDescriptor[] {\n return descriptors.filter(\n descriptor =>\n descriptor.type !== 'composition' &&\n descriptor.type !== 'linkCollection' &&\n descriptor.type !== 'link',\n );\n}\n"],"names":["EntifixBuildError","ROW_KEY","describeEntityColumns","describeChildColumns","childType","sample","descriptors","reserved","find","descriptor","name","undefined","member","localized","type","editableChildColumns","filter"],"mappings":"AAAA,SAASA,iBAAiB,QAAQ,6CAA6C;AAC/E,SAASC,OAAO,QAAQ,gCAAgC;AAExD,SACEC,qBAAqB,QAEhB,+BAA+B;AAEtC;;;;;;;;;;;;;;;;;;;;;CAqBC,GACD,OAAO,SAASC,qBACdC,SAAmC,EACnCC,MAAe;IAEf,MAAMC,cAAcJ,sBAAsBE,WAAWC;IACrD,MAAME,WAAWD,YAAYE,IAAI,CAACC,CAAAA,aAAcA,WAAWC,IAAI,KAAKT;IAEpE,IAAIM,aAAaI,WAAW;QAC1B,MAAM,IAAIX,kBACR,CAAC,8CAA8C,EAAEC,QAAQ,sBAAsB,CAAC,GAC9E,8EACA,2EACFU,WACA;YAAEC,QAAQX;QAAQ;IAEtB;IAEA,yEAAyE;IACzE,yEAAyE;IACzE,MAAMY,YAAYP,YAAYE,IAAI,CAChCC,CAAAA,aAAcA,WAAWK,IAAI,KAAK;IAEpC,IAAID,cAAcF,WAAW;QAC3B,MAAM,IAAIX,kBACR,CAAC,8BAA8B,EAAEa,UAAUH,IAAI,CAAC,4BAA4B,CAAC,GAC3E,6EACA,mDACFC,WACA;YAAEC,QAAQC,UAAUH,IAAI;QAAC;IAE7B;IAEA,OAAOJ;AACT;AAEA;;;;;;;;CAQC,GACD,OAAO,SAASS,qBACdT,WAA6C;IAE7C,OAAOA,YAAYU,MAAM,CACvBP,CAAAA,aACEA,WAAWK,IAAI,KAAK,iBACpBL,WAAWK,IAAI,KAAK,oBACpBL,WAAWK,IAAI,KAAK;AAE1B"}
1
+ {"version":3,"sources":["../../../src/entity-definition/describe/describe-child-columns.ts"],"sourcesContent":["import { EntifixBuildError } from '../../base-entities/entifix-error/index.js';\nimport { ROW_KEY } from '../../types/EntityRowDraft.js';\nimport type { ChildConstructor } from '../meta-entities/meta-accessor/index.js';\nimport {\n describeEntityColumns,\n type EntityFieldDescriptor,\n} from './describe-entity-columns.js';\n\n/**\n * The columns of one row of an owned collection.\n *\n * Thin over {@link describeEntityColumns}, which already accepts a\n * `ChildConstructor` — a child is described by its accessors and needs no\n * `@entity()`.\n * What this adds is the one rule a row has and an entity does not.\n *\n * **A child may not declare `$key`.** That name is the row's minted client\n * identity, and a member of the same name would be overwritten by it — the row\n * would re-key on every keystroke, drop focus mid-word, and address another\n * row's errors. Throwing is the only honest option: silently renaming the\n * member would write the wrong column, and preferring the member over the key\n * would leave the grid with no identity at all.\n *\n * **Nor a `localized` member**, for a reason of the same kind: a row's draft is a\n * flat string map, and a text per locale has no single cell to live in.\n *\n * `EntifixBuildError` rather than a logic error, because this is a declaration\n * fault in the entity — the same class `assertNotQueryable` raises when a\n * collection claims to be sortable.\n */\nexport function describeChildColumns<TChild extends object>(\n childType: ChildConstructor<TChild>,\n sample?: TChild,\n): EntityFieldDescriptor[] {\n const descriptors = describeEntityColumns(childType, sample);\n const reserved = descriptors.find(descriptor => descriptor.name === ROW_KEY);\n\n if (reserved !== undefined) {\n throw new EntifixBuildError(\n `A composition child declares a member named \\`${ROW_KEY}\\`, which is reserved ` +\n 'for the row’s client-side key. Rename the member: the key and the member ' +\n 'cannot share a name, and the row would lose its identity on every edit.',\n undefined,\n { member: ROW_KEY },\n );\n }\n\n // A row's draft is a flat string map, which is what keeps a draft's type\n // bounded for the form engine; a text per locale has no cell to live in.\n const localized = descriptors.find(\n descriptor => descriptor.type === 'localized',\n );\n if (localized !== undefined) {\n throw new EntifixBuildError(\n `A composition child declares \"${localized.name}\" as localized, which a row ` +\n 'cannot hold: a row is a flat string map. Keep the localized text on the ' +\n 'entity, or make the child an entity of its own.',\n undefined,\n { member: localized.name },\n );\n }\n\n return descriptors;\n}\n\n/**\n * The columns a detail grid actually edits.\n *\n * A read-only member still has a column — it is shown, just not written, exactly\n * as a form shows one. What is dropped is a **nested collection**: a child that\n * owns rows of its own would need a grid inside a grid, and `reconstructChild`\n * is one level deep by decision. Dropping it here rather than rendering a\n * disabled cell keeps that decision in one place.\n */\nexport function editableChildColumns(\n descriptors: readonly EntityFieldDescriptor[],\n): EntityFieldDescriptor[] {\n return descriptors.filter(\n descriptor =>\n descriptor.type !== 'composition' &&\n descriptor.type !== 'linkCollection' &&\n descriptor.type !== 'link',\n );\n}\n"],"names":["EntifixBuildError","ROW_KEY","describeEntityColumns","describeChildColumns","childType","sample","descriptors","reserved","find","descriptor","name","undefined","member","localized","type","editableChildColumns","filter"],"mappings":"AAAA,SAASA,iBAAiB,QAAQ,6CAA6C;AAC/E,SAASC,OAAO,QAAQ,gCAAgC;AAExD,SACEC,qBAAqB,QAEhB,+BAA+B;AAEtC;;;;;;;;;;;;;;;;;;;;;CAqBC,GACD,OAAO,SAASC,qBACdC,SAAmC,EACnCC,MAAe;IAEf,MAAMC,cAAcJ,sBAAsBE,WAAWC;IACrD,MAAME,WAAWD,YAAYE,IAAI,CAACC,CAAAA,aAAcA,WAAWC,IAAI,KAAKT;IAEpE,IAAIM,aAAaI,WAAW;QAC1B,MAAM,IAAIX,kBACR,CAAC,8CAA8C,EAAEC,QAAQ,sBAAsB,CAAC,GAC9E,8EACA,2EACFU,WACA;YAAEC,QAAQX;QAAQ;IAEtB;IAEA,yEAAyE;IACzE,yEAAyE;IACzE,MAAMY,YAAYP,YAAYE,IAAI,CAChCC,CAAAA,aAAcA,WAAWK,IAAI,KAAK;IAEpC,IAAID,cAAcF,WAAW;QAC3B,MAAM,IAAIX,kBACR,CAAC,8BAA8B,EAAEa,UAAUH,IAAI,CAAC,4BAA4B,CAAC,GAC3E,6EACA,mDACFC,WACA;YAAEC,QAAQC,UAAUH,IAAI;QAAC;IAE7B;IAEA,OAAOJ;AACT;AAEA;;;;;;;;CAQC,GACD,OAAO,SAASS,qBACdT,WAA6C;IAE7C,OAAOA,YAAYU,MAAM,CACvBP,CAAAA,aACEA,WAAWK,IAAI,KAAK,iBACpBL,WAAWK,IAAI,KAAK,oBACpBL,WAAWK,IAAI,KAAK;AAE1B"}
@@ -51,6 +51,8 @@ export interface EntityFieldDescriptor {
51
51
  maxItems?: number;
52
52
  /** The `date` member this one may not precede. */
53
53
  notBefore?: string;
54
+ /** This member holds the parent record's id: the entity forms a tree. */
55
+ parent?: boolean;
54
56
  }
55
57
  /** Types whose values are scalars a user can meaningfully sort/filter on. */
56
58
  export declare const SCALAR_TYPES: readonly MetaAccessorType[];
@@ -68,6 +70,28 @@ export declare const SCALAR_TYPES: readonly MetaAccessorType[];
68
70
  * to add silently.
69
71
  */
70
72
  export declare const COLLECTION_TYPES: readonly MetaAccessorType[];
73
+ /**
74
+ * One parent member per entity. Two would be two trees over the same records,
75
+ * and which one a table indents by would depend on declaration order.
76
+ */
77
+ export declare function assertOneParent(members: readonly {
78
+ readonly name: string | symbol;
79
+ readonly parent?: boolean;
80
+ }[]): void;
81
+ /**
82
+ * ⚠️ **A parent member must travel.** The serializer skips a `hidden` or
83
+ * `readonly` member in both directions, so the parent key would not survive a
84
+ * single trip over the wire and the tree would arrive flat, with nothing
85
+ * failing. Hide it from a table or a form with a slot instead. Checked before
86
+ * hidden members are dropped, since a hidden parent is exactly the one
87
+ * describing would never see.
88
+ */
89
+ export declare function assertParentVisible(members: readonly {
90
+ readonly name: string | symbol;
91
+ readonly parent?: boolean;
92
+ readonly hidden?: boolean;
93
+ readonly readonly?: boolean;
94
+ }[]): void;
71
95
  /**
72
96
  * Resolves an entity's displayable members from its accessor metadata, sorted by
73
97
  * `order` (declaration order when undeclared).
@@ -1 +1 @@
1
- {"version":3,"file":"describe-entity-columns.d.ts","sourceRoot":"","sources":["../../../src/entity-definition/describe/describe-entity-columns.ts"],"names":[],"mappings":"AAKA,OAAO,EACL,KAAK,gBAAgB,EACrB,KAAK,uBAAuB,EAE5B,gBAAgB,EACjB,MAAM,yCAAyC,CAAC;AAEjD;;;;;GAKG;AACH,MAAM,WAAW,qBAAqB;IACpC,wEAAwE;IACxE,IAAI,EAAE,MAAM,CAAC;IACb,0CAA0C;IAC1C,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,gBAAgB,CAAC;IACvB,QAAQ,EAAE,OAAO,CAAC;IAClB,UAAU,EAAE,OAAO,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,sEAAsE;IACtE,QAAQ,EAAE,OAAO,CAAC;IAClB,kEAAkE;IAClE,QAAQ,EAAE,OAAO,CAAC;IAClB,UAAU,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC/B,0FAA0F;IAC1F,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,6DAA6D;IAC7D,iBAAiB,EAAE,MAAM,CAAC;IAC1B,+EAA+E;IAC/E,kBAAkB,EAAE,MAAM,CAAC;IAC3B,0EAA0E;IAC1E,iBAAiB,EAAE,uBAAuB,CAAC;IAC3C;;;OAGG;IACH,SAAS,CAAC,EAAE,gBAAgB,CAAC;IAC7B;;;OAGG;IACH,YAAY,EAAE,OAAO,CAAC;IACtB,sDAAsD;IACtD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,oDAAoD;IACpD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,kDAAkD;IAClD,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,6EAA6E;AAC7E,eAAO,MAAM,YAAY,EAAE,SAAS,gBAAgB,EAMnD,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,gBAAgB,EAAE,SAAS,gBAAgB,EAIvD,CAAC;AA2HF;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,SAAS,MAAM,EAC1D,iBAAiB,EAAE,gBAAgB,CAAC,OAAO,CAAC,EAC5C,MAAM,CAAC,EAAE,OAAO,GACf,qBAAqB,EAAE,CAezB"}
1
+ {"version":3,"file":"describe-entity-columns.d.ts","sourceRoot":"","sources":["../../../src/entity-definition/describe/describe-entity-columns.ts"],"names":[],"mappings":"AAKA,OAAO,EACL,KAAK,gBAAgB,EACrB,KAAK,uBAAuB,EAE5B,gBAAgB,EACjB,MAAM,yCAAyC,CAAC;AAEjD;;;;;GAKG;AACH,MAAM,WAAW,qBAAqB;IACpC,wEAAwE;IACxE,IAAI,EAAE,MAAM,CAAC;IACb,0CAA0C;IAC1C,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd;;;;;OAKG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,gBAAgB,CAAC;IACvB,QAAQ,EAAE,OAAO,CAAC;IAClB,UAAU,EAAE,OAAO,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,sEAAsE;IACtE,QAAQ,EAAE,OAAO,CAAC;IAClB,kEAAkE;IAClE,QAAQ,EAAE,OAAO,CAAC;IAClB,UAAU,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC/B,0FAA0F;IAC1F,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,6DAA6D;IAC7D,iBAAiB,EAAE,MAAM,CAAC;IAC1B,+EAA+E;IAC/E,kBAAkB,EAAE,MAAM,CAAC;IAC3B,0EAA0E;IAC1E,iBAAiB,EAAE,uBAAuB,CAAC;IAC3C;;;OAGG;IACH,SAAS,CAAC,EAAE,gBAAgB,CAAC;IAC7B;;;OAGG;IACH,YAAY,EAAE,OAAO,CAAC;IACtB,sDAAsD;IACtD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,oDAAoD;IACpD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,kDAAkD;IAClD,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,yEAAyE;IACzE,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,6EAA6E;AAC7E,eAAO,MAAM,YAAY,EAAE,SAAS,gBAAgB,EAMnD,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,gBAAgB,EAAE,SAAS,gBAAgB,EAIvD,CAAC;AAgGF;;;GAGG;AACH,wBAAgB,eAAe,CAC7B,OAAO,EAAE,SAAS;IAChB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IAC/B,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC;CAC3B,EAAE,QAQJ;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,SAAS;IAChB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC;IAC/B,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;CAC7B,EAAE,QAWJ;AA4CD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,SAAS,MAAM,EAC1D,iBAAiB,EAAE,gBAAgB,CAAC,OAAO,CAAC,EAC5C,MAAM,CAAC,EAAE,OAAO,GACf,qBAAqB,EAAE,CAiBzB"}
@@ -84,12 +84,43 @@ import { assertBounds } from '../meta-entities/bounds.js';
84
84
  }
85
85
  }
86
86
  }
87
+ /**
88
+ * A parent is one record's id, so it is an `id` or a `string` member — a
89
+ * number or a date cannot name a record, and a collection would be two
90
+ * parents. Checked per member, since the type is only known once inferred.
91
+ */ function assertParentType(metaAccessor, type) {
92
+ if (metaAccessor.parent !== true) return;
93
+ if (type === 'id' || type === 'string') return;
94
+ throw new EntifixBuildError(`"${String(metaAccessor.name)}" is a ${type} and cannot be the parent member — a parent is one record's id, so it is an id or a string.`);
95
+ }
96
+ /**
97
+ * One parent member per entity. Two would be two trees over the same records,
98
+ * and which one a table indents by would depend on declaration order.
99
+ */ export function assertOneParent(members) {
100
+ const parents = members.filter((member)=>member.parent === true);
101
+ if (parents.length > 1) {
102
+ throw new EntifixBuildError(`An entity declares one parent member at most; found ${parents.map((member)=>`"${String(member.name)}"`).join(', ')}.`);
103
+ }
104
+ }
105
+ /**
106
+ * ⚠️ **A parent member must travel.** The serializer skips a `hidden` or
107
+ * `readonly` member in both directions, so the parent key would not survive a
108
+ * single trip over the wire and the tree would arrive flat, with nothing
109
+ * failing. Hide it from a table or a form with a slot instead. Checked before
110
+ * hidden members are dropped, since a hidden parent is exactly the one
111
+ * describing would never see.
112
+ */ export function assertParentVisible(members) {
113
+ const stuck = members.find((member)=>member.parent === true && (member.hidden === true || member.readonly === true));
114
+ if (stuck === undefined) return;
115
+ throw new EntifixBuildError(`"${String(stuck.name)}" is the parent member and is ${stuck.hidden === true ? 'hidden' : 'readonly'} — such a member is never serialized, so the tree would arrive flat. Hide it with a slot instead.`);
116
+ }
87
117
  function toDescriptor(metaAccessor, index, sample) {
88
- var _metaAccessor_type, _metaAccessor_linkLabelProperty, _metaAccessor_alias, _metaAccessor_label, _metaAccessor_sortable, _metaAccessor_filterable, _metaAccessor_order, _metaAccessor_readonly, _metaAccessor_required, _metaAccessor_linkSearchProperty, _metaAccessor_linkSerialization, _metaAccessor_resetOnClone;
118
+ var _metaAccessor_type, _metaAccessor_linkLabelProperty, _metaAccessor_alias, _metaAccessor_label, _metaAccessor_sortable, _metaAccessor_filterable, _metaAccessor_order, _metaAccessor_readonly, _metaAccessor_required, _metaAccessor_linkSearchProperty, _metaAccessor_linkSerialization, _metaAccessor_resetOnClone, _metaAccessor_parent;
89
119
  const name = String(metaAccessor.name);
90
120
  const type = (_metaAccessor_type = metaAccessor.type) != null ? _metaAccessor_type : inferType(name, sample == null ? void 0 : sample[name]);
91
121
  assertNotQueryable(metaAccessor, type);
92
122
  assertItemCounts(metaAccessor, type);
123
+ assertParentType(metaAccessor, type);
93
124
  // A localized member is queried one locale at a time (`name.en`), which
94
125
  // makes it as queryable as the text each locale holds.
95
126
  const isScalar = SCALAR_TYPES.includes(type) || type === 'localized';
@@ -114,7 +145,8 @@ function toDescriptor(metaAccessor, index, sample) {
114
145
  resetOnClone: (_metaAccessor_resetOnClone = metaAccessor.resetOnClone) != null ? _metaAccessor_resetOnClone : false,
115
146
  minItems: metaAccessor.minItems,
116
147
  maxItems: metaAccessor.maxItems,
117
- notBefore: metaAccessor.notBefore
148
+ notBefore: metaAccessor.notBefore,
149
+ parent: (_metaAccessor_parent = metaAccessor.parent) != null ? _metaAccessor_parent : false
118
150
  };
119
151
  }
120
152
  /**
@@ -135,6 +167,7 @@ function toDescriptor(metaAccessor, index, sample) {
135
167
  * from one implementation.
136
168
  */ export function describeEntityColumns(entityConstructor, sample) {
137
169
  const seen = new Set();
170
+ assertParentVisible(extractMetaAccessors(entityConstructor));
138
171
  const descriptors = extractMetaAccessors(entityConstructor).filter((metaAccessor)=>{
139
172
  if (metaAccessor.hidden || metaAccessor.kind !== 'getter') return false;
140
173
  const name = String(metaAccessor.name);
@@ -143,6 +176,7 @@ function toDescriptor(metaAccessor, index, sample) {
143
176
  return true;
144
177
  }).map((metaAccessor, index)=>toDescriptor(metaAccessor, index, sample)).sort((left, right)=>left.order - right.order);
145
178
  assertNotBefore(descriptors);
179
+ assertOneParent(descriptors);
146
180
  return descriptors;
147
181
  }
148
182
 
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/entity-definition/describe/describe-entity-columns.ts"],"sourcesContent":["import { EntifixBuildError } from '../../base-entities/entifix-error/index.js';\nimport { extractMetaAccessors } from '../helpers/index.js';\nimport { EntityCollectionLink } from '../links/entity-collection-link/index.js';\nimport { EntityLink } from '../links/entity-link/index.js';\nimport { assertBounds } from '../meta-entities/bounds.js';\nimport {\n type ChildConstructor,\n type EntityLinkSerialization,\n MetaAccessor,\n MetaAccessorType,\n} from '../meta-entities/meta-accessor/index.js';\n\n/**\n * A displayable member of an entity, resolved from its accessor metadata. This\n * is the contract generic UI builds itself from — a table's columns today, a\n * form's fields later — so it stays framework-free and lives next to the\n * metadata it derives from.\n */\nexport interface EntityFieldDescriptor {\n /** Accessor name. The stable identity used by preferences and slots. */\n name: string;\n /** Wire/property key: `alias ?? name`. */\n key: string;\n label: string;\n /**\n * Catalog key for {@link label}, when the entity declared one. Resolution is\n * the presentation layer's job — core has no locale and no catalogs, and the\n * same descriptor feeds the server-side filter allowlist, where a translated\n * label would be meaningless.\n */\n labelKey?: string;\n type: MetaAccessorType;\n sortable: boolean;\n filterable: boolean;\n order: number;\n /** Cannot be written back. A form shows it but disables its input. */\n readonly: boolean;\n /** Must hold a value. A form rejects submit while it is empty. */\n required: boolean;\n enumValues?: readonly string[];\n /** Catalog prefix for {@link enumValues}; a value reads as `${enumLabelKey}.${value}`. */\n enumLabelKey?: string;\n /** Property of a `link` target used as its display label. */\n linkLabelProperty: string;\n /** Property of a `link` target a picker searches on. Defaults to the label. */\n linkSearchProperty: string;\n /** Whether a `link` writes back its foreign key or the inlined target. */\n linkSerialization: EntityLinkSerialization;\n /**\n * The class describing one row of a `composition` member, resolved from the\n * accessor's thunk. Absent on every other type.\n */\n childType?: ChildConstructor;\n /**\n * The member is dropped when the record is copied — a unique code, an audit\n * stamp. The identity member is always reset and is not flagged.\n */\n resetOnClone: boolean;\n /** The fewest values a collection member may hold. */\n minItems?: number;\n /** The most values a collection member may hold. */\n maxItems?: number;\n /** The `date` member this one may not precede. */\n notBefore?: string;\n}\n\n/** Types whose values are scalars a user can meaningfully sort/filter on. */\nexport const SCALAR_TYPES: readonly MetaAccessorType[] = [\n 'string',\n 'number',\n 'boolean',\n 'date',\n 'enum',\n];\n\n/**\n * Types whose value is a collection. None of them may ever be sorted or\n * filtered: this descriptor is also the **server-side RSQL allowlist**, and an\n * array compared as a scalar matches nothing — so the query would not fail, it\n * would quietly return an empty page.\n *\n * Exported so `describe-entity-columns.spec.ts` can assert that this list,\n * {@link SCALAR_TYPES}, the two reference types and `localized` partition\n * `MetaAccessorTypes` exactly. Nothing in the repo guards `MetaAccessorType`\n * exhaustively — every switch over it has a `default` that treats the value as\n * a string — so that partition spec is what makes an eleventh type impossible\n * to add silently.\n */\nexport const COLLECTION_TYPES: readonly MetaAccessorType[] = [\n 'linkCollection',\n 'composition',\n 'scalarCollection',\n];\n\n/** `productCode` / `product-code` / `product_code` → `Product Code`. */\nfunction humanize(name: string): string {\n return name\n .replace(/[-_]+/g, ' ')\n .replace(/([a-z0-9])([A-Z])/g, '$1 $2')\n .replace(/\\s+/g, ' ')\n .trim()\n .replace(/^./, first => first.toUpperCase());\n}\n\n/**\n * Best-effort type for accessors that did not declare one. Entities are not\n * required to annotate every member, so a sample row keeps a bare `@accessor()`\n * usable; a declared `type` always wins over this.\n */\nfunction inferType(name: string, value: unknown): MetaAccessorType {\n if (value instanceof EntityLink) return 'link';\n if (value instanceof EntityCollectionLink) return 'linkCollection';\n if (value instanceof Date) return 'date';\n if (typeof value === 'number') return 'number';\n if (typeof value === 'boolean') return 'boolean';\n if (name === 'id') return 'id';\n return 'string';\n}\n\n/**\n * A collection member declared queryable is always a mistake, so it breaks the\n * developer rather than the query — the same posture as `applyEntityLinks`\n * throwing on an `embedded` link it cannot build.\n *\n * Silently clamping to `false` was the alternative and is worse: the author\n * keeps a declaration that reads as honoured, and the symptom is an empty\n * result page rather than an error anyone can act on.\n */\nfunction assertNotQueryable(\n metaAccessor: MetaAccessor,\n type: MetaAccessorType,\n) {\n if (!COLLECTION_TYPES.includes(type)) return;\n if (metaAccessor.sortable !== true && metaAccessor.filterable !== true)\n return;\n throw new EntifixBuildError(\n `\"${String(metaAccessor.name)}\" is a ${type} and cannot be sortable or filterable — member metadata is the server-side query allowlist, and a collection compared as a scalar matches nothing.`,\n );\n}\n\n/**\n * Item counts belong to a collection, and must be counts. A `minItems` on a\n * string would read as honoured and constrain nothing.\n */\nfunction assertItemCounts(metaAccessor: MetaAccessor, type: MetaAccessorType) {\n const { minItems, maxItems } = metaAccessor;\n if (minItems === undefined && maxItems === undefined) return;\n const name = String(metaAccessor.name);\n if (!COLLECTION_TYPES.includes(type)) {\n throw new EntifixBuildError(\n `\"${name}\" is a ${type}, and only a collection can declare minItems or maxItems.`,\n );\n }\n assertBounds(`\"${name}\"`, { min: minItems, max: maxItems });\n}\n\n/**\n * `notBefore` compares two instants, so both ends must be `date` members of\n * the same entity. Checked once every member is described, since the one it\n * names may be declared after it.\n */\nfunction assertNotBefore(descriptors: readonly EntityFieldDescriptor[]) {\n for (const descriptor of descriptors) {\n if (descriptor.notBefore === undefined) continue;\n const earlier = descriptors.find(\n other => other.name === descriptor.notBefore,\n );\n if (descriptor.type !== 'date' || earlier?.type !== 'date') {\n throw new EntifixBuildError(\n `\"${descriptor.name}\" declares notBefore \"${descriptor.notBefore}\", and both must be date members of the entity.`,\n );\n }\n }\n}\n\nfunction toDescriptor(\n metaAccessor: MetaAccessor,\n index: number,\n sample: unknown,\n): EntityFieldDescriptor {\n const name = String(metaAccessor.name);\n const type =\n metaAccessor.type ??\n inferType(name, (sample as Record<string, unknown> | undefined)?.[name]);\n assertNotQueryable(metaAccessor, type);\n assertItemCounts(metaAccessor, type);\n // A localized member is queried one locale at a time (`name.en`), which\n // makes it as queryable as the text each locale holds.\n const isScalar = SCALAR_TYPES.includes(type) || type === 'localized';\n const linkLabelProperty = metaAccessor.linkLabelProperty ?? 'name';\n\n return {\n name,\n key: String(metaAccessor.alias ?? metaAccessor.name),\n label: metaAccessor.label ?? humanize(name),\n labelKey: metaAccessor.labelKey,\n type,\n sortable: metaAccessor.sortable ?? isScalar,\n filterable: metaAccessor.filterable ?? isScalar,\n order: metaAccessor.order ?? index,\n readonly: metaAccessor.readonly ?? false,\n required: metaAccessor.required ?? false,\n enumValues: metaAccessor.enumValues,\n enumLabelKey: metaAccessor.enumLabelKey,\n linkLabelProperty,\n linkSearchProperty: metaAccessor.linkSearchProperty ?? linkLabelProperty,\n linkSerialization: metaAccessor.linkSerialization ?? 'id',\n childType: metaAccessor.childType?.(),\n resetOnClone: metaAccessor.resetOnClone ?? false,\n minItems: metaAccessor.minItems,\n maxItems: metaAccessor.maxItems,\n notBefore: metaAccessor.notBefore,\n };\n}\n\n/**\n * Resolves an entity's displayable members from its accessor metadata, sorted by\n * `order` (declaration order when undeclared).\n *\n * Getter-kind and non-`hidden` mirror the filter {@link serializeEntity} walks,\n * minus its `readonly` exclusion: a read-only member is still worth showing,\n * it just cannot be written back.\n *\n * `sample` is an optional instance used only to infer the `type` of accessors\n * that did not declare one.\n *\n * The target is a {@link ChildConstructor} rather than an `EntityConstructor`,\n * which is wider than it looks: nothing here reads `id`, and a `composition`\n * member's child is a **value** with accessors and no identity. So the same\n * walk describes an entity and describes one row of the collection it owns,\n * from one implementation.\n */\nexport function describeEntityColumns<TTarget extends object>(\n entityConstructor: ChildConstructor<TTarget>,\n sample?: TTarget,\n): EntityFieldDescriptor[] {\n const seen = new Set<string>();\n\n const descriptors = extractMetaAccessors(entityConstructor)\n .filter(metaAccessor => {\n if (metaAccessor.hidden || metaAccessor.kind !== 'getter') return false;\n const name = String(metaAccessor.name);\n if (seen.has(name)) return false;\n seen.add(name);\n return true;\n })\n .map((metaAccessor, index) => toDescriptor(metaAccessor, index, sample))\n .sort((left, right) => left.order - right.order);\n assertNotBefore(descriptors);\n return descriptors;\n}\n"],"names":["EntifixBuildError","extractMetaAccessors","EntityCollectionLink","EntityLink","assertBounds","SCALAR_TYPES","COLLECTION_TYPES","humanize","name","replace","trim","first","toUpperCase","inferType","value","Date","assertNotQueryable","metaAccessor","type","includes","sortable","filterable","String","assertItemCounts","minItems","maxItems","undefined","min","max","assertNotBefore","descriptors","descriptor","notBefore","earlier","find","other","toDescriptor","index","sample","isScalar","linkLabelProperty","key","alias","label","labelKey","order","readonly","required","enumValues","enumLabelKey","linkSearchProperty","linkSerialization","childType","resetOnClone","describeEntityColumns","entityConstructor","seen","Set","filter","hidden","kind","has","add","map","sort","left","right"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/entity-definition/describe/describe-entity-columns.ts"],"sourcesContent":["import { EntifixBuildError } from '../../base-entities/entifix-error/index.js';\nimport { extractMetaAccessors } from '../helpers/index.js';\nimport { EntityCollectionLink } from '../links/entity-collection-link/index.js';\nimport { EntityLink } from '../links/entity-link/index.js';\nimport { assertBounds } from '../meta-entities/bounds.js';\nimport {\n type ChildConstructor,\n type EntityLinkSerialization,\n MetaAccessor,\n MetaAccessorType,\n} from '../meta-entities/meta-accessor/index.js';\n\n/**\n * A displayable member of an entity, resolved from its accessor metadata. This\n * is the contract generic UI builds itself from — a table's columns today, a\n * form's fields later — so it stays framework-free and lives next to the\n * metadata it derives from.\n */\nexport interface EntityFieldDescriptor {\n /** Accessor name. The stable identity used by preferences and slots. */\n name: string;\n /** Wire/property key: `alias ?? name`. */\n key: string;\n label: string;\n /**\n * Catalog key for {@link label}, when the entity declared one. Resolution is\n * the presentation layer's job — core has no locale and no catalogs, and the\n * same descriptor feeds the server-side filter allowlist, where a translated\n * label would be meaningless.\n */\n labelKey?: string;\n type: MetaAccessorType;\n sortable: boolean;\n filterable: boolean;\n order: number;\n /** Cannot be written back. A form shows it but disables its input. */\n readonly: boolean;\n /** Must hold a value. A form rejects submit while it is empty. */\n required: boolean;\n enumValues?: readonly string[];\n /** Catalog prefix for {@link enumValues}; a value reads as `${enumLabelKey}.${value}`. */\n enumLabelKey?: string;\n /** Property of a `link` target used as its display label. */\n linkLabelProperty: string;\n /** Property of a `link` target a picker searches on. Defaults to the label. */\n linkSearchProperty: string;\n /** Whether a `link` writes back its foreign key or the inlined target. */\n linkSerialization: EntityLinkSerialization;\n /**\n * The class describing one row of a `composition` member, resolved from the\n * accessor's thunk. Absent on every other type.\n */\n childType?: ChildConstructor;\n /**\n * The member is dropped when the record is copied — a unique code, an audit\n * stamp. The identity member is always reset and is not flagged.\n */\n resetOnClone: boolean;\n /** The fewest values a collection member may hold. */\n minItems?: number;\n /** The most values a collection member may hold. */\n maxItems?: number;\n /** The `date` member this one may not precede. */\n notBefore?: string;\n /** This member holds the parent record's id: the entity forms a tree. */\n parent?: boolean;\n}\n\n/** Types whose values are scalars a user can meaningfully sort/filter on. */\nexport const SCALAR_TYPES: readonly MetaAccessorType[] = [\n 'string',\n 'number',\n 'boolean',\n 'date',\n 'enum',\n];\n\n/**\n * Types whose value is a collection. None of them may ever be sorted or\n * filtered: this descriptor is also the **server-side RSQL allowlist**, and an\n * array compared as a scalar matches nothing — so the query would not fail, it\n * would quietly return an empty page.\n *\n * Exported so `describe-entity-columns.spec.ts` can assert that this list,\n * {@link SCALAR_TYPES}, the two reference types and `localized` partition\n * `MetaAccessorTypes` exactly. Nothing in the repo guards `MetaAccessorType`\n * exhaustively — every switch over it has a `default` that treats the value as\n * a string — so that partition spec is what makes an eleventh type impossible\n * to add silently.\n */\nexport const COLLECTION_TYPES: readonly MetaAccessorType[] = [\n 'linkCollection',\n 'composition',\n 'scalarCollection',\n];\n\n/** `productCode` / `product-code` / `product_code` → `Product Code`. */\nfunction humanize(name: string): string {\n return name\n .replace(/[-_]+/g, ' ')\n .replace(/([a-z0-9])([A-Z])/g, '$1 $2')\n .replace(/\\s+/g, ' ')\n .trim()\n .replace(/^./, first => first.toUpperCase());\n}\n\n/**\n * Best-effort type for accessors that did not declare one. Entities are not\n * required to annotate every member, so a sample row keeps a bare `@accessor()`\n * usable; a declared `type` always wins over this.\n */\nfunction inferType(name: string, value: unknown): MetaAccessorType {\n if (value instanceof EntityLink) return 'link';\n if (value instanceof EntityCollectionLink) return 'linkCollection';\n if (value instanceof Date) return 'date';\n if (typeof value === 'number') return 'number';\n if (typeof value === 'boolean') return 'boolean';\n if (name === 'id') return 'id';\n return 'string';\n}\n\n/**\n * A collection member declared queryable is always a mistake, so it breaks the\n * developer rather than the query — the same posture as `applyEntityLinks`\n * throwing on an `embedded` link it cannot build.\n *\n * Silently clamping to `false` was the alternative and is worse: the author\n * keeps a declaration that reads as honoured, and the symptom is an empty\n * result page rather than an error anyone can act on.\n */\nfunction assertNotQueryable(\n metaAccessor: MetaAccessor,\n type: MetaAccessorType,\n) {\n if (!COLLECTION_TYPES.includes(type)) return;\n if (metaAccessor.sortable !== true && metaAccessor.filterable !== true)\n return;\n throw new EntifixBuildError(\n `\"${String(metaAccessor.name)}\" is a ${type} and cannot be sortable or filterable — member metadata is the server-side query allowlist, and a collection compared as a scalar matches nothing.`,\n );\n}\n\n/**\n * Item counts belong to a collection, and must be counts. A `minItems` on a\n * string would read as honoured and constrain nothing.\n */\nfunction assertItemCounts(metaAccessor: MetaAccessor, type: MetaAccessorType) {\n const { minItems, maxItems } = metaAccessor;\n if (minItems === undefined && maxItems === undefined) return;\n const name = String(metaAccessor.name);\n if (!COLLECTION_TYPES.includes(type)) {\n throw new EntifixBuildError(\n `\"${name}\" is a ${type}, and only a collection can declare minItems or maxItems.`,\n );\n }\n assertBounds(`\"${name}\"`, { min: minItems, max: maxItems });\n}\n\n/**\n * `notBefore` compares two instants, so both ends must be `date` members of\n * the same entity. Checked once every member is described, since the one it\n * names may be declared after it.\n */\nfunction assertNotBefore(descriptors: readonly EntityFieldDescriptor[]) {\n for (const descriptor of descriptors) {\n if (descriptor.notBefore === undefined) continue;\n const earlier = descriptors.find(\n other => other.name === descriptor.notBefore,\n );\n if (descriptor.type !== 'date' || earlier?.type !== 'date') {\n throw new EntifixBuildError(\n `\"${descriptor.name}\" declares notBefore \"${descriptor.notBefore}\", and both must be date members of the entity.`,\n );\n }\n }\n}\n\n/**\n * A parent is one record's id, so it is an `id` or a `string` member — a\n * number or a date cannot name a record, and a collection would be two\n * parents. Checked per member, since the type is only known once inferred.\n */\nfunction assertParentType(metaAccessor: MetaAccessor, type: MetaAccessorType) {\n if (metaAccessor.parent !== true) return;\n if (type === 'id' || type === 'string') return;\n throw new EntifixBuildError(\n `\"${String(metaAccessor.name)}\" is a ${type} and cannot be the parent member — a parent is one record's id, so it is an id or a string.`,\n );\n}\n\n/**\n * One parent member per entity. Two would be two trees over the same records,\n * and which one a table indents by would depend on declaration order.\n */\nexport function assertOneParent(\n members: readonly {\n readonly name: string | symbol;\n readonly parent?: boolean;\n }[],\n) {\n const parents = members.filter(member => member.parent === true);\n if (parents.length > 1) {\n throw new EntifixBuildError(\n `An entity declares one parent member at most; found ${parents.map(member => `\"${String(member.name)}\"`).join(', ')}.`,\n );\n }\n}\n\n/**\n * ⚠️ **A parent member must travel.** The serializer skips a `hidden` or\n * `readonly` member in both directions, so the parent key would not survive a\n * single trip over the wire and the tree would arrive flat, with nothing\n * failing. Hide it from a table or a form with a slot instead. Checked before\n * hidden members are dropped, since a hidden parent is exactly the one\n * describing would never see.\n */\nexport function assertParentVisible(\n members: readonly {\n readonly name: string | symbol;\n readonly parent?: boolean;\n readonly hidden?: boolean;\n readonly readonly?: boolean;\n }[],\n) {\n const stuck = members.find(\n member =>\n member.parent === true &&\n (member.hidden === true || member.readonly === true),\n );\n if (stuck === undefined) return;\n throw new EntifixBuildError(\n `\"${String(stuck.name)}\" is the parent member and is ${stuck.hidden === true ? 'hidden' : 'readonly'} — such a member is never serialized, so the tree would arrive flat. Hide it with a slot instead.`,\n );\n}\n\nfunction toDescriptor(\n metaAccessor: MetaAccessor,\n index: number,\n sample: unknown,\n): EntityFieldDescriptor {\n const name = String(metaAccessor.name);\n const type =\n metaAccessor.type ??\n inferType(name, (sample as Record<string, unknown> | undefined)?.[name]);\n assertNotQueryable(metaAccessor, type);\n assertItemCounts(metaAccessor, type);\n assertParentType(metaAccessor, type);\n // A localized member is queried one locale at a time (`name.en`), which\n // makes it as queryable as the text each locale holds.\n const isScalar = SCALAR_TYPES.includes(type) || type === 'localized';\n const linkLabelProperty = metaAccessor.linkLabelProperty ?? 'name';\n\n return {\n name,\n key: String(metaAccessor.alias ?? metaAccessor.name),\n label: metaAccessor.label ?? humanize(name),\n labelKey: metaAccessor.labelKey,\n type,\n sortable: metaAccessor.sortable ?? isScalar,\n filterable: metaAccessor.filterable ?? isScalar,\n order: metaAccessor.order ?? index,\n readonly: metaAccessor.readonly ?? false,\n required: metaAccessor.required ?? false,\n enumValues: metaAccessor.enumValues,\n enumLabelKey: metaAccessor.enumLabelKey,\n linkLabelProperty,\n linkSearchProperty: metaAccessor.linkSearchProperty ?? linkLabelProperty,\n linkSerialization: metaAccessor.linkSerialization ?? 'id',\n childType: metaAccessor.childType?.(),\n resetOnClone: metaAccessor.resetOnClone ?? false,\n minItems: metaAccessor.minItems,\n maxItems: metaAccessor.maxItems,\n notBefore: metaAccessor.notBefore,\n parent: metaAccessor.parent ?? false,\n };\n}\n\n/**\n * Resolves an entity's displayable members from its accessor metadata, sorted by\n * `order` (declaration order when undeclared).\n *\n * Getter-kind and non-`hidden` mirror the filter {@link serializeEntity} walks,\n * minus its `readonly` exclusion: a read-only member is still worth showing,\n * it just cannot be written back.\n *\n * `sample` is an optional instance used only to infer the `type` of accessors\n * that did not declare one.\n *\n * The target is a {@link ChildConstructor} rather than an `EntityConstructor`,\n * which is wider than it looks: nothing here reads `id`, and a `composition`\n * member's child is a **value** with accessors and no identity. So the same\n * walk describes an entity and describes one row of the collection it owns,\n * from one implementation.\n */\nexport function describeEntityColumns<TTarget extends object>(\n entityConstructor: ChildConstructor<TTarget>,\n sample?: TTarget,\n): EntityFieldDescriptor[] {\n const seen = new Set<string>();\n assertParentVisible(extractMetaAccessors(entityConstructor));\n\n const descriptors = extractMetaAccessors(entityConstructor)\n .filter(metaAccessor => {\n if (metaAccessor.hidden || metaAccessor.kind !== 'getter') return false;\n const name = String(metaAccessor.name);\n if (seen.has(name)) return false;\n seen.add(name);\n return true;\n })\n .map((metaAccessor, index) => toDescriptor(metaAccessor, index, sample))\n .sort((left, right) => left.order - right.order);\n assertNotBefore(descriptors);\n assertOneParent(descriptors);\n return descriptors;\n}\n"],"names":["EntifixBuildError","extractMetaAccessors","EntityCollectionLink","EntityLink","assertBounds","SCALAR_TYPES","COLLECTION_TYPES","humanize","name","replace","trim","first","toUpperCase","inferType","value","Date","assertNotQueryable","metaAccessor","type","includes","sortable","filterable","String","assertItemCounts","minItems","maxItems","undefined","min","max","assertNotBefore","descriptors","descriptor","notBefore","earlier","find","other","assertParentType","parent","assertOneParent","members","parents","filter","member","length","map","join","assertParentVisible","stuck","hidden","readonly","toDescriptor","index","sample","isScalar","linkLabelProperty","key","alias","label","labelKey","order","required","enumValues","enumLabelKey","linkSearchProperty","linkSerialization","childType","resetOnClone","describeEntityColumns","entityConstructor","seen","Set","kind","has","add","sort","left","right"],"mappings":"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"}
@@ -26,7 +26,8 @@ export interface UseCaseDescriptor {
26
26
  * Unlike {@link describeEntityColumns}, this is only trustworthy where the
27
27
  * use-case modules have been loaded: a `@useCase()` class registers itself when
28
28
  * its module evaluates. On a service that is the composition root's job, and
29
- * `@r10c/slices` fails the build on a class its package barrel does not export.
29
+ * a host's register should fail the build on a class its package barrel does
30
+ * not export.
30
31
  * A browser never calls this — it fetches the descriptors instead, so no
31
32
  * use-case implementation reaches a client bundle.
32
33
  */
@@ -1 +1 @@
1
- {"version":3,"file":"describe-entity-use-cases.d.ts","sourceRoot":"","sources":["../../../src/entity-definition/describe/describe-entity-use-cases.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAElE,OAAO,KAAK,EACV,cAAc,EACd,WAAW,EACX,cAAc,EACd,gBAAgB,EACjB,MAAM,yCAAyC,CAAC;AAEjD;;;;;;;GAOG;AACH,MAAM,WAAW,iBAAiB;IAChC,sEAAsE;IACtE,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,cAAc,CAAC;IACxB,SAAS,EAAE,gBAAgB,CAAC;IAC5B,0EAA0E;IAC1E,MAAM,CAAC,EAAE,SAAS,WAAW,EAAE,CAAC;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,cAAc,CAAC;IACzB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;;;;;;;GASG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,SAAS,MAAM,EAC3D,iBAAiB,EAAE,iBAAiB,CAAC,OAAO,CAAC,GAC5C,iBAAiB,EAAE,CAWrB"}
1
+ {"version":3,"file":"describe-entity-use-cases.d.ts","sourceRoot":"","sources":["../../../src/entity-definition/describe/describe-entity-use-cases.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAElE,OAAO,KAAK,EACV,cAAc,EACd,WAAW,EACX,cAAc,EACd,gBAAgB,EACjB,MAAM,yCAAyC,CAAC;AAEjD;;;;;;;GAOG;AACH,MAAM,WAAW,iBAAiB;IAChC,sEAAsE;IACtE,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,cAAc,CAAC;IACxB,SAAS,EAAE,gBAAgB,CAAC;IAC5B,0EAA0E;IAC1E,MAAM,CAAC,EAAE,SAAS,WAAW,EAAE,CAAC;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,cAAc,CAAC;IACzB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,sBAAsB,CAAC,OAAO,SAAS,MAAM,EAC3D,iBAAiB,EAAE,iBAAiB,CAAC,OAAO,CAAC,GAC5C,iBAAiB,EAAE,CAWrB"}
@@ -5,7 +5,8 @@ import { extractMetaUseCases } from '../helpers/index.js';
5
5
  * Unlike {@link describeEntityColumns}, this is only trustworthy where the
6
6
  * use-case modules have been loaded: a `@useCase()` class registers itself when
7
7
  * its module evaluates. On a service that is the composition root's job, and
8
- * `@r10c/slices` fails the build on a class its package barrel does not export.
8
+ * a host's register should fail the build on a class its package barrel does
9
+ * not export.
9
10
  * A browser never calls this — it fetches the descriptors instead, so no
10
11
  * use-case implementation reaches a client bundle.
11
12
  */ export function describeEntityUseCases(entityConstructor) {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/entity-definition/describe/describe-entity-use-cases.ts"],"sourcesContent":["import { Entity, EntityConstructor } from '../../types/Entity.js';\nimport { extractMetaUseCases } from '../helpers/index.js';\nimport type {\n UseCaseBinding,\n UseCaseCell,\n UseCaseConfirm,\n UseCasePlacement,\n} from '../meta-entities/meta-use-case/index.js';\n\n/**\n * An action a caller may take on an entity, resolved from its use-case\n * metadata. The counterpart to {@link EntityFieldDescriptor}, and like it,\n * framework-free and copy-free — every human-readable member is a catalog key.\n *\n * It carries no `entity`: this is the shape that goes over the wire from\n * `GET /api/<entity>/$metadata`, where the entity is already the address.\n */\nexport interface UseCaseDescriptor {\n /** The verb. Also the third segment of the permission guarding it. */\n key: string;\n binding: UseCaseBinding;\n placement: UseCasePlacement;\n /** Further cells this same verb appears in — see `MetaUseCaseOptions`. */\n alsoAt?: readonly UseCaseCell[];\n labelKey: string;\n keywordsKey?: string;\n confirm?: UseCaseConfirm;\n form?: string;\n}\n\n/**\n * Resolves the use cases declared against an entity, in declaration order.\n *\n * Unlike {@link describeEntityColumns}, this is only trustworthy where the\n * use-case modules have been loaded: a `@useCase()` class registers itself when\n * its module evaluates. On a service that is the composition root's job, and\n * `@r10c/slices` fails the build on a class its package barrel does not export.\n * A browser never calls this — it fetches the descriptors instead, so no\n * use-case implementation reaches a client bundle.\n */\nexport function describeEntityUseCases<TEntity extends Entity>(\n entityConstructor: EntityConstructor<TEntity>,\n): UseCaseDescriptor[] {\n return extractMetaUseCases(entityConstructor).map(metaUseCase => ({\n key: metaUseCase.key,\n binding: metaUseCase.binding,\n placement: metaUseCase.placement,\n alsoAt: metaUseCase.alsoAt,\n labelKey: metaUseCase.labelKey,\n keywordsKey: metaUseCase.keywordsKey,\n confirm: metaUseCase.confirm,\n form: metaUseCase.form,\n }));\n}\n"],"names":["extractMetaUseCases","describeEntityUseCases","entityConstructor","map","metaUseCase","key","binding","placement","alsoAt","labelKey","keywordsKey","confirm","form"],"mappings":"AACA,SAASA,mBAAmB,QAAQ,sBAAsB;AA6B1D;;;;;;;;;CASC,GACD,OAAO,SAASC,uBACdC,iBAA6C;IAE7C,OAAOF,oBAAoBE,mBAAmBC,GAAG,CAACC,CAAAA,cAAgB,CAAA;YAChEC,KAAKD,YAAYC,GAAG;YACpBC,SAASF,YAAYE,OAAO;YAC5BC,WAAWH,YAAYG,SAAS;YAChCC,QAAQJ,YAAYI,MAAM;YAC1BC,UAAUL,YAAYK,QAAQ;YAC9BC,aAAaN,YAAYM,WAAW;YACpCC,SAASP,YAAYO,OAAO;YAC5BC,MAAMR,YAAYQ,IAAI;QACxB,CAAA;AACF"}
1
+ {"version":3,"sources":["../../../src/entity-definition/describe/describe-entity-use-cases.ts"],"sourcesContent":["import { Entity, EntityConstructor } from '../../types/Entity.js';\nimport { extractMetaUseCases } from '../helpers/index.js';\nimport type {\n UseCaseBinding,\n UseCaseCell,\n UseCaseConfirm,\n UseCasePlacement,\n} from '../meta-entities/meta-use-case/index.js';\n\n/**\n * An action a caller may take on an entity, resolved from its use-case\n * metadata. The counterpart to {@link EntityFieldDescriptor}, and like it,\n * framework-free and copy-free — every human-readable member is a catalog key.\n *\n * It carries no `entity`: this is the shape that goes over the wire from\n * `GET /api/<entity>/$metadata`, where the entity is already the address.\n */\nexport interface UseCaseDescriptor {\n /** The verb. Also the third segment of the permission guarding it. */\n key: string;\n binding: UseCaseBinding;\n placement: UseCasePlacement;\n /** Further cells this same verb appears in — see `MetaUseCaseOptions`. */\n alsoAt?: readonly UseCaseCell[];\n labelKey: string;\n keywordsKey?: string;\n confirm?: UseCaseConfirm;\n form?: string;\n}\n\n/**\n * Resolves the use cases declared against an entity, in declaration order.\n *\n * Unlike {@link describeEntityColumns}, this is only trustworthy where the\n * use-case modules have been loaded: a `@useCase()` class registers itself when\n * its module evaluates. On a service that is the composition root's job, and\n * a host's register should fail the build on a class its package barrel does\n * not export.\n * A browser never calls this — it fetches the descriptors instead, so no\n * use-case implementation reaches a client bundle.\n */\nexport function describeEntityUseCases<TEntity extends Entity>(\n entityConstructor: EntityConstructor<TEntity>,\n): UseCaseDescriptor[] {\n return extractMetaUseCases(entityConstructor).map(metaUseCase => ({\n key: metaUseCase.key,\n binding: metaUseCase.binding,\n placement: metaUseCase.placement,\n alsoAt: metaUseCase.alsoAt,\n labelKey: metaUseCase.labelKey,\n keywordsKey: metaUseCase.keywordsKey,\n confirm: metaUseCase.confirm,\n form: metaUseCase.form,\n }));\n}\n"],"names":["extractMetaUseCases","describeEntityUseCases","entityConstructor","map","metaUseCase","key","binding","placement","alsoAt","labelKey","keywordsKey","confirm","form"],"mappings":"AACA,SAASA,mBAAmB,QAAQ,sBAAsB;AA6B1D;;;;;;;;;;CAUC,GACD,OAAO,SAASC,uBACdC,iBAA6C;IAE7C,OAAOF,oBAAoBE,mBAAmBC,GAAG,CAACC,CAAAA,cAAgB,CAAA;YAChEC,KAAKD,YAAYC,GAAG;YACpBC,SAASF,YAAYE,OAAO;YAC5BC,WAAWH,YAAYG,SAAS;YAChCC,QAAQJ,YAAYI,MAAM;YAC1BC,UAAUL,YAAYK,QAAQ;YAC9BC,aAAaN,YAAYM,WAAW;YACpCC,SAASP,YAAYO,OAAO;YAC5BC,MAAMR,YAAYQ,IAAI;QACxB,CAAA;AACF"}
@@ -24,8 +24,8 @@ export declare function appendMetaMethod(metadata: DecoratorMetadataObject | und
24
24
  * declared on its own class, but the entity is what a surface holds, so the
25
25
  * descriptor has to end up there. The consequence is that a use-case module
26
26
  * which is never imported leaves the entity looking as if it had no actions,
27
- * which is why `@r10c/slices` asserts every `@useCase()` class is re-exported
28
- * from its package barrel.
27
+ * which is why a host's register should assert every `@useCase()` class is
28
+ * re-exported from its package barrel.
29
29
  *
30
30
  * It reads the target's **own** metadata rather than going through
31
31
  * {@link readMetadata}. `Symbol.metadata` resolves along the prototype chain, so
@@ -40,8 +40,8 @@ export function appendMetaMethod(metadata, metaMethod) {
40
40
  * declared on its own class, but the entity is what a surface holds, so the
41
41
  * descriptor has to end up there. The consequence is that a use-case module
42
42
  * which is never imported leaves the entity looking as if it had no actions,
43
- * which is why `@r10c/slices` asserts every `@useCase()` class is re-exported
44
- * from its package barrel.
43
+ * which is why a host's register should assert every `@useCase()` class is
44
+ * re-exported from its package barrel.
45
45
  *
46
46
  * It reads the target's **own** metadata rather than going through
47
47
  * {@link readMetadata}. `Symbol.metadata` resolves along the prototype chain, so
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/entity-definition/helpers/entity-definition-helpers.ts"],"sourcesContent":["(Symbol as { metadata?: symbol }).metadata ??= Symbol.for('Symbol.metadata');\n\nimport { EntifixBuildError } from '../../base-entities/entifix-error/index.js';\nimport { Entity, EntityConstructor } from '../../types/Entity.js';\nimport { UseCaseConstructor } from '../../types/UseCase.js';\nimport {\n type ChildConstructor,\n MetaAccessor,\n} from '../meta-entities/meta-accessor/index.js';\nimport { MetaEntity } from '../meta-entities/meta-entity/index.js';\nimport { MetaMethod } from '../meta-entities/meta-method/index.js';\nimport {\n MetaUseCase,\n MetaUseCaseBinding,\n} from '../meta-entities/meta-use-case/index.js';\n\ndeclare global {\n interface SymbolConstructor {\n readonly metadata: unique symbol;\n }\n}\n\nexport const META_ENTITY_KEY = Symbol('MetaEntity');\nexport const META_ACCESSOR_KEY = Symbol('MetaAccessor');\nexport const META_METHOD_KEY = Symbol('MetaMethod');\nexport const META_USE_CASE_KEY = Symbol('MetaUseCase');\nexport const META_USE_CASE_BINDING_KEY = Symbol('MetaUseCaseBinding');\n\nfunction requireMetadata(\n metadata: DecoratorMetadataObject | undefined,\n): DecoratorMetadataObject {\n if (!metadata) {\n throw new EntifixBuildError(\n 'Decorator metadata unavailable — Symbol.metadata polyfill missing or stage-3 decorator emit not enabled.',\n );\n }\n return metadata;\n}\n\nexport function setMetaEntity(\n metadata: DecoratorMetadataObject | undefined,\n metaEntity: MetaEntity,\n) {\n requireMetadata(metadata)[META_ENTITY_KEY] = metaEntity;\n}\n\nexport function appendMetaAccessor(\n metadata: DecoratorMetadataObject | undefined,\n metaAccessor: MetaAccessor,\n) {\n const meta = requireMetadata(metadata);\n const existing =\n (meta[META_ACCESSOR_KEY] as MetaAccessor[] | undefined) ?? [];\n meta[META_ACCESSOR_KEY] = [...existing, metaAccessor];\n}\n\nexport function appendMetaMethod(\n metadata: DecoratorMetadataObject | undefined,\n metaMethod: MetaMethod,\n) {\n const meta = requireMetadata(metadata);\n const existing = (meta[META_METHOD_KEY] as MetaMethod[] | undefined) ?? [];\n meta[META_METHOD_KEY] = [...existing, metaMethod];\n}\n\n/**\n * Append a use case to the metadata of the entity it acts on.\n *\n * This is the one decorator that writes a bag other than its own: a use case is\n * declared on its own class, but the entity is what a surface holds, so the\n * descriptor has to end up there. The consequence is that a use-case module\n * which is never imported leaves the entity looking as if it had no actions,\n * which is why `@r10c/slices` asserts every `@useCase()` class is re-exported\n * from its package barrel.\n *\n * It reads the target's **own** metadata rather than going through\n * {@link readMetadata}. `Symbol.metadata` resolves along the prototype chain, so\n * an inherited read would append the verb to a base class's bag and leak it to\n * every subclass. A target with no own metadata is an entity missing\n * `@entity()`, or a circular import that evaluated the use case first.\n */\nexport function appendMetaUseCase<TEntity extends Entity>(\n target: EntityConstructor<TEntity>,\n metaUseCase: MetaUseCase,\n) {\n const own = Object.getOwnPropertyDescriptor(target, Symbol.metadata)\n ?.value as DecoratorMetadataObject | null | undefined;\n if (!own) {\n throw new EntifixBuildError(\n `Use case \"${metaUseCase.key}\" targets ${target.name}, which has no metadata of its own — it is missing @entity(), or a circular import evaluated the use case first.`,\n );\n }\n const existing = (own[META_USE_CASE_KEY] as MetaUseCase[] | undefined) ?? [];\n own[META_USE_CASE_KEY] = [...existing, metaUseCase];\n}\n\n/**\n * Record on the use-case class itself which entity and verb it implements, so\n * the permission can be derived from the class alone. That is what keeps the\n * verb string written once, at the declaration, instead of being retyped at\n * every guard.\n */\nexport function setMetaUseCaseBinding(\n metadata: DecoratorMetadataObject | undefined,\n binding: MetaUseCaseBinding,\n) {\n requireMetadata(metadata)[META_USE_CASE_BINDING_KEY] = binding;\n}\n\nfunction readMetadata(\n target: ChildConstructor,\n): DecoratorMetadataObject | undefined {\n return target[Symbol.metadata] ?? undefined;\n}\n\nexport function extractMetaEntity<TEntity extends Entity>(\n target: EntityConstructor<TEntity>,\n): MetaEntity {\n const metadata = readMetadata(target);\n const metaEntity = metadata?.[META_ENTITY_KEY] as MetaEntity | undefined;\n if (!metaEntity) {\n throw new EntifixBuildError(`MetaEntity not found for ${target.name}`);\n }\n return metaEntity;\n}\n\n/**\n * Every accessor declared on a class.\n *\n * The target is a {@link ChildConstructor}, not an `EntityConstructor`: an\n * `@accessor()` writes to its own class's metadata bag with no help from\n * `@entity()`, so a `composition` member's child — a value with no `id` — is\n * described by exactly this walk.\n */\nexport function extractMetaAccessors(target: ChildConstructor): MetaAccessor[] {\n const metadata = readMetadata(target);\n return (metadata?.[META_ACCESSOR_KEY] as MetaAccessor[] | undefined) ?? [];\n}\n\nexport function extractMetaMethods<TEntity extends Entity>(\n target: EntityConstructor<TEntity>,\n): MetaMethod[] {\n const metadata = readMetadata(target);\n return (metadata?.[META_METHOD_KEY] as MetaMethod[] | undefined) ?? [];\n}\n\n/** Every use case declared against an entity. Empty until one is imported. */\nexport function extractMetaUseCases<TEntity extends Entity>(\n target: EntityConstructor<TEntity>,\n): MetaUseCase[] {\n const metadata = readMetadata(target);\n return (metadata?.[META_USE_CASE_KEY] as MetaUseCase[] | undefined) ?? [];\n}\n\n/** The entity and verb a `@useCase()` class implements. */\nexport function extractMetaUseCaseBinding(\n target: UseCaseConstructor,\n): MetaUseCaseBinding {\n const metadata = (\n target as { [Symbol.metadata]?: DecoratorMetadataObject | null }\n )[Symbol.metadata];\n const binding = metadata?.[META_USE_CASE_BINDING_KEY] as\n MetaUseCaseBinding | undefined;\n if (!binding) {\n throw new EntifixBuildError(\n `MetaUseCase not found for ${target.name} — is it missing @useCase()?`,\n );\n }\n return binding;\n}\n"],"names":["Symbol","metadata","for","EntifixBuildError","META_ENTITY_KEY","META_ACCESSOR_KEY","META_METHOD_KEY","META_USE_CASE_KEY","META_USE_CASE_BINDING_KEY","requireMetadata","setMetaEntity","metaEntity","appendMetaAccessor","metaAccessor","meta","existing","appendMetaMethod","metaMethod","appendMetaUseCase","target","metaUseCase","own","Object","getOwnPropertyDescriptor","value","key","name","setMetaUseCaseBinding","binding","readMetadata","undefined","extractMetaEntity","extractMetaAccessors","extractMetaMethods","extractMetaUseCases","extractMetaUseCaseBinding"],"mappings":"IAACA,SAAD;CAAA,YAAA,CAACA,UAAAA,QAAiCC,QAAQ,YAA1C,YAAA,AAACD,QAAiCC,QAAQ,GAAKD,OAAOE,GAAG,CAAC;AAE1D,SAASC,iBAAiB,QAAQ,6CAA6C;AAoB/E,OAAO,MAAMC,kBAAkBJ,OAAO,cAAc;AACpD,OAAO,MAAMK,oBAAoBL,OAAO,gBAAgB;AACxD,OAAO,MAAMM,kBAAkBN,OAAO,cAAc;AACpD,OAAO,MAAMO,oBAAoBP,OAAO,eAAe;AACvD,OAAO,MAAMQ,4BAA4BR,OAAO,sBAAsB;AAEtE,SAASS,gBACPR,QAA6C;IAE7C,IAAI,CAACA,UAAU;QACb,MAAM,IAAIE,kBACR;IAEJ;IACA,OAAOF;AACT;AAEA,OAAO,SAASS,cACdT,QAA6C,EAC7CU,UAAsB;IAEtBF,gBAAgBR,SAAS,CAACG,gBAAgB,GAAGO;AAC/C;AAEA,OAAO,SAASC,mBACdX,QAA6C,EAC7CY,YAA0B;QAIvBC;IAFH,MAAMA,OAAOL,gBAAgBR;IAC7B,MAAMc,YACHD,0BAAAA,IAAI,CAACT,kBAAkB,YAAvBS,0BAA0D,EAAE;IAC/DA,IAAI,CAACT,kBAAkB,GAAG;WAAIU;QAAUF;KAAa;AACvD;AAEA,OAAO,SAASG,iBACdf,QAA6C,EAC7CgB,UAAsB;QAGJH;IADlB,MAAMA,OAAOL,gBAAgBR;IAC7B,MAAMc,YAAYD,wBAAAA,IAAI,CAACR,gBAAgB,YAArBQ,wBAAsD,EAAE;IAC1EA,IAAI,CAACR,gBAAgB,GAAG;WAAIS;QAAUE;KAAW;AACnD;AAEA;;;;;;;;;;;;;;;CAeC,GACD,OAAO,SAASC,kBACdC,MAAkC,EAClCC,WAAwB;QASNC;QAPNC;IAAZ,MAAMD,OAAMC,mCAAAA,OAAOC,wBAAwB,CAACJ,QAAQnB,OAAOC,QAAQ,sBAAvDqB,iCACRE,KAAK;IACT,IAAI,CAACH,KAAK;QACR,MAAM,IAAIlB,kBACR,CAAC,UAAU,EAAEiB,YAAYK,GAAG,CAAC,UAAU,EAAEN,OAAOO,IAAI,CAAC,gHAAgH,CAAC;IAE1K;IACA,MAAMX,YAAYM,yBAAAA,GAAG,CAACd,kBAAkB,YAAtBc,yBAAwD,EAAE;IAC5EA,GAAG,CAACd,kBAAkB,GAAG;WAAIQ;QAAUK;KAAY;AACrD;AAEA;;;;;CAKC,GACD,OAAO,SAASO,sBACd1B,QAA6C,EAC7C2B,OAA2B;IAE3BnB,gBAAgBR,SAAS,CAACO,0BAA0B,GAAGoB;AACzD;AAEA,SAASC,aACPV,MAAwB;QAEjBA;IAAP,QAAOA,0BAAAA,MAAM,CAACnB,OAAOC,QAAQ,CAAC,YAAvBkB,0BAA2BW;AACpC;AAEA,OAAO,SAASC,kBACdZ,MAAkC;IAElC,MAAMlB,WAAW4B,aAAaV;IAC9B,MAAMR,aAAaV,4BAAAA,QAAU,CAACG,gBAAgB;IAC9C,IAAI,CAACO,YAAY;QACf,MAAM,IAAIR,kBAAkB,CAAC,yBAAyB,EAAEgB,OAAOO,IAAI,EAAE;IACvE;IACA,OAAOf;AACT;AAEA;;;;;;;CAOC,GACD,OAAO,SAASqB,qBAAqBb,MAAwB;;IAC3D,MAAMlB,WAAW4B,aAAaV;IAC9B,eAAQlB,4BAAAA,QAAU,CAACI,kBAAkB,mBAAmC,EAAE;AAC5E;AAEA,OAAO,SAAS4B,mBACdd,MAAkC;;IAElC,MAAMlB,WAAW4B,aAAaV;IAC9B,eAAQlB,4BAAAA,QAAU,CAACK,gBAAgB,mBAAiC,EAAE;AACxE;AAEA,4EAA4E,GAC5E,OAAO,SAAS4B,oBACdf,MAAkC;;IAElC,MAAMlB,WAAW4B,aAAaV;IAC9B,eAAQlB,4BAAAA,QAAU,CAACM,kBAAkB,mBAAkC,EAAE;AAC3E;AAEA,yDAAyD,GACzD,OAAO,SAAS4B,0BACdhB,MAA0B;IAE1B,MAAMlB,WAAW,AACfkB,MACD,CAACnB,OAAOC,QAAQ,CAAC;IAClB,MAAM2B,UAAU3B,4BAAAA,QAAU,CAACO,0BAA0B;IAErD,IAAI,CAACoB,SAAS;QACZ,MAAM,IAAIzB,kBACR,CAAC,0BAA0B,EAAEgB,OAAOO,IAAI,CAAC,4BAA4B,CAAC;IAE1E;IACA,OAAOE;AACT"}
1
+ {"version":3,"sources":["../../../src/entity-definition/helpers/entity-definition-helpers.ts"],"sourcesContent":["(Symbol as { metadata?: symbol }).metadata ??= Symbol.for('Symbol.metadata');\n\nimport { EntifixBuildError } from '../../base-entities/entifix-error/index.js';\nimport { Entity, EntityConstructor } from '../../types/Entity.js';\nimport { UseCaseConstructor } from '../../types/UseCase.js';\nimport {\n type ChildConstructor,\n MetaAccessor,\n} from '../meta-entities/meta-accessor/index.js';\nimport { MetaEntity } from '../meta-entities/meta-entity/index.js';\nimport { MetaMethod } from '../meta-entities/meta-method/index.js';\nimport {\n MetaUseCase,\n MetaUseCaseBinding,\n} from '../meta-entities/meta-use-case/index.js';\n\ndeclare global {\n interface SymbolConstructor {\n readonly metadata: unique symbol;\n }\n}\n\nexport const META_ENTITY_KEY = Symbol('MetaEntity');\nexport const META_ACCESSOR_KEY = Symbol('MetaAccessor');\nexport const META_METHOD_KEY = Symbol('MetaMethod');\nexport const META_USE_CASE_KEY = Symbol('MetaUseCase');\nexport const META_USE_CASE_BINDING_KEY = Symbol('MetaUseCaseBinding');\n\nfunction requireMetadata(\n metadata: DecoratorMetadataObject | undefined,\n): DecoratorMetadataObject {\n if (!metadata) {\n throw new EntifixBuildError(\n 'Decorator metadata unavailable — Symbol.metadata polyfill missing or stage-3 decorator emit not enabled.',\n );\n }\n return metadata;\n}\n\nexport function setMetaEntity(\n metadata: DecoratorMetadataObject | undefined,\n metaEntity: MetaEntity,\n) {\n requireMetadata(metadata)[META_ENTITY_KEY] = metaEntity;\n}\n\nexport function appendMetaAccessor(\n metadata: DecoratorMetadataObject | undefined,\n metaAccessor: MetaAccessor,\n) {\n const meta = requireMetadata(metadata);\n const existing =\n (meta[META_ACCESSOR_KEY] as MetaAccessor[] | undefined) ?? [];\n meta[META_ACCESSOR_KEY] = [...existing, metaAccessor];\n}\n\nexport function appendMetaMethod(\n metadata: DecoratorMetadataObject | undefined,\n metaMethod: MetaMethod,\n) {\n const meta = requireMetadata(metadata);\n const existing = (meta[META_METHOD_KEY] as MetaMethod[] | undefined) ?? [];\n meta[META_METHOD_KEY] = [...existing, metaMethod];\n}\n\n/**\n * Append a use case to the metadata of the entity it acts on.\n *\n * This is the one decorator that writes a bag other than its own: a use case is\n * declared on its own class, but the entity is what a surface holds, so the\n * descriptor has to end up there. The consequence is that a use-case module\n * which is never imported leaves the entity looking as if it had no actions,\n * which is why a host's register should assert every `@useCase()` class is\n * re-exported from its package barrel.\n *\n * It reads the target's **own** metadata rather than going through\n * {@link readMetadata}. `Symbol.metadata` resolves along the prototype chain, so\n * an inherited read would append the verb to a base class's bag and leak it to\n * every subclass. A target with no own metadata is an entity missing\n * `@entity()`, or a circular import that evaluated the use case first.\n */\nexport function appendMetaUseCase<TEntity extends Entity>(\n target: EntityConstructor<TEntity>,\n metaUseCase: MetaUseCase,\n) {\n const own = Object.getOwnPropertyDescriptor(target, Symbol.metadata)\n ?.value as DecoratorMetadataObject | null | undefined;\n if (!own) {\n throw new EntifixBuildError(\n `Use case \"${metaUseCase.key}\" targets ${target.name}, which has no metadata of its own — it is missing @entity(), or a circular import evaluated the use case first.`,\n );\n }\n const existing = (own[META_USE_CASE_KEY] as MetaUseCase[] | undefined) ?? [];\n own[META_USE_CASE_KEY] = [...existing, metaUseCase];\n}\n\n/**\n * Record on the use-case class itself which entity and verb it implements, so\n * the permission can be derived from the class alone. That is what keeps the\n * verb string written once, at the declaration, instead of being retyped at\n * every guard.\n */\nexport function setMetaUseCaseBinding(\n metadata: DecoratorMetadataObject | undefined,\n binding: MetaUseCaseBinding,\n) {\n requireMetadata(metadata)[META_USE_CASE_BINDING_KEY] = binding;\n}\n\nfunction readMetadata(\n target: ChildConstructor,\n): DecoratorMetadataObject | undefined {\n return target[Symbol.metadata] ?? undefined;\n}\n\nexport function extractMetaEntity<TEntity extends Entity>(\n target: EntityConstructor<TEntity>,\n): MetaEntity {\n const metadata = readMetadata(target);\n const metaEntity = metadata?.[META_ENTITY_KEY] as MetaEntity | undefined;\n if (!metaEntity) {\n throw new EntifixBuildError(`MetaEntity not found for ${target.name}`);\n }\n return metaEntity;\n}\n\n/**\n * Every accessor declared on a class.\n *\n * The target is a {@link ChildConstructor}, not an `EntityConstructor`: an\n * `@accessor()` writes to its own class's metadata bag with no help from\n * `@entity()`, so a `composition` member's child — a value with no `id` — is\n * described by exactly this walk.\n */\nexport function extractMetaAccessors(target: ChildConstructor): MetaAccessor[] {\n const metadata = readMetadata(target);\n return (metadata?.[META_ACCESSOR_KEY] as MetaAccessor[] | undefined) ?? [];\n}\n\nexport function extractMetaMethods<TEntity extends Entity>(\n target: EntityConstructor<TEntity>,\n): MetaMethod[] {\n const metadata = readMetadata(target);\n return (metadata?.[META_METHOD_KEY] as MetaMethod[] | undefined) ?? [];\n}\n\n/** Every use case declared against an entity. Empty until one is imported. */\nexport function extractMetaUseCases<TEntity extends Entity>(\n target: EntityConstructor<TEntity>,\n): MetaUseCase[] {\n const metadata = readMetadata(target);\n return (metadata?.[META_USE_CASE_KEY] as MetaUseCase[] | undefined) ?? [];\n}\n\n/** The entity and verb a `@useCase()` class implements. */\nexport function extractMetaUseCaseBinding(\n target: UseCaseConstructor,\n): MetaUseCaseBinding {\n const metadata = (\n target as { [Symbol.metadata]?: DecoratorMetadataObject | null }\n )[Symbol.metadata];\n const binding = metadata?.[META_USE_CASE_BINDING_KEY] as\n MetaUseCaseBinding | undefined;\n if (!binding) {\n throw new EntifixBuildError(\n `MetaUseCase not found for ${target.name} — is it missing @useCase()?`,\n );\n }\n return binding;\n}\n"],"names":["Symbol","metadata","for","EntifixBuildError","META_ENTITY_KEY","META_ACCESSOR_KEY","META_METHOD_KEY","META_USE_CASE_KEY","META_USE_CASE_BINDING_KEY","requireMetadata","setMetaEntity","metaEntity","appendMetaAccessor","metaAccessor","meta","existing","appendMetaMethod","metaMethod","appendMetaUseCase","target","metaUseCase","own","Object","getOwnPropertyDescriptor","value","key","name","setMetaUseCaseBinding","binding","readMetadata","undefined","extractMetaEntity","extractMetaAccessors","extractMetaMethods","extractMetaUseCases","extractMetaUseCaseBinding"],"mappings":"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"}
@@ -23,8 +23,8 @@ export type MetaAccessorType = (typeof MetaAccessorTypes)[number];
23
23
  * ⚠️ It is a **shape declaration, not a runtime constructor contract.** Children
24
24
  * arrive off the wire as plain objects — the serializer walks `instanceof
25
25
  * EntityLink`, not the declared type, so an embedded array passes straight
26
- * through — and ADR 0032 forbids a class instance inside an autosaved draft
27
- * anyway. Nothing may branch on `instanceof` against this constructor.
26
+ * through — and an autosaved draft may hold nothing that does not survive a
27
+ * JSON round trip anyway. Nothing may branch on `instanceof` against this constructor.
28
28
  */
29
29
  export type ChildConstructor<TChild extends object = object> = new (...args: any[]) => TChild;
30
30
  /**
@@ -104,8 +104,8 @@ export interface MetaAccessorOptions {
104
104
  * Reset this member when the record is copied.
105
105
  *
106
106
  * It lives on the **accessor**, not on a use case's descriptor, and that is a
107
- * decision already on file: ADR 0026 closed `UseCaseDescriptor` against
108
- * per-verb payloads and named this exact case, because a descriptor that
107
+ * decision already made: `UseCaseDescriptor` is closed against per-verb
108
+ * payloads, and this is the exact case, because a descriptor that
109
109
  * grows a field per verb ends up a union of every verb's arguments. Which
110
110
  * members a copy must not carry is a property of the *member* — a unique
111
111
  * code, an audit stamp, a sequence number — and it is the same answer however
@@ -131,6 +131,18 @@ export interface MetaAccessorOptions {
131
131
  * be `date`s, or describing the entity fails.
132
132
  */
133
133
  notBefore?: string;
134
+ /**
135
+ * This member holds the id of the record's **parent**, another record of
136
+ * the same entity — so the entity forms a tree. At most one member per
137
+ * entity, and it must be an `id` or a `string`; describing the entity fails
138
+ * otherwise.
139
+ *
140
+ * An id rather than a `link` on purpose. A tree is always built from the
141
+ * whole set of its records ({@link buildTree}), never by resolving one parent
142
+ * at a time, so a link's lazy resolution would be a fetch per node that
143
+ * nothing needs. Absent means a root.
144
+ */
145
+ parent?: boolean;
134
146
  }
135
147
  export declare class MetaAccessor {
136
148
  readonly name: string | symbol;
@@ -155,6 +167,7 @@ export declare class MetaAccessor {
155
167
  readonly minItems?: number;
156
168
  readonly maxItems?: number;
157
169
  readonly notBefore?: string;
170
+ readonly parent?: boolean;
158
171
  constructor(name: string | symbol, kind: MetaAccessorKind, options?: MetaAccessorOptions);
159
172
  }
160
173
  //# sourceMappingURL=meta-accessor.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"meta-accessor.d.ts","sourceRoot":"","sources":["../../../../src/entity-definition/meta-entities/meta-accessor/meta-accessor.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"meta-accessor.d.ts","sourceRoot":"","sources":["../../../../src/entity-definition/meta-entities/meta-accessor/meta-accessor.ts"],"names":[],"mappings":"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"}
@@ -53,6 +53,7 @@ export class MetaAccessor {
53
53
  this.minItems = options == null ? void 0 : options.minItems;
54
54
  this.maxItems = options == null ? void 0 : options.maxItems;
55
55
  this.notBefore = options == null ? void 0 : options.notBefore;
56
+ this.parent = options == null ? void 0 : options.parent;
56
57
  }
57
58
  }
58
59