@entifix/react-integration 0.1.0-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 (135) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +7 -0
  3. package/dist/context/adapters-context/adapters-context.d.ts +4 -0
  4. package/dist/context/adapters-context/adapters-context.d.ts.map +1 -0
  5. package/dist/context/adapters-context/adapters-context.js +19 -0
  6. package/dist/context/adapters-context/adapters-context.js.map +1 -0
  7. package/dist/context/adapters-context/index.d.ts +2 -0
  8. package/dist/context/adapters-context/index.d.ts.map +1 -0
  9. package/dist/context/adapters-context/index.js +3 -0
  10. package/dist/context/adapters-context/index.js.map +1 -0
  11. package/dist/hooks/use-data-loading/index.d.ts +3 -0
  12. package/dist/hooks/use-data-loading/index.d.ts.map +1 -0
  13. package/dist/hooks/use-data-loading/index.js +4 -0
  14. package/dist/hooks/use-data-loading/index.js.map +1 -0
  15. package/dist/hooks/use-data-loading/use-data-loading.d.ts +26 -0
  16. package/dist/hooks/use-data-loading/use-data-loading.d.ts.map +1 -0
  17. package/dist/hooks/use-data-loading/use-data-loading.js +167 -0
  18. package/dist/hooks/use-data-loading/use-data-loading.js.map +1 -0
  19. package/dist/hooks/use-data-loading/use-data-loading.types.d.ts +53 -0
  20. package/dist/hooks/use-data-loading/use-data-loading.types.d.ts.map +1 -0
  21. package/dist/hooks/use-data-loading/use-data-loading.types.js +3 -0
  22. package/dist/hooks/use-data-loading/use-data-loading.types.js.map +1 -0
  23. package/dist/hooks/use-entity-form/index.d.ts +4 -0
  24. package/dist/hooks/use-entity-form/index.d.ts.map +1 -0
  25. package/dist/hooks/use-entity-form/index.js +5 -0
  26. package/dist/hooks/use-entity-form/index.js.map +1 -0
  27. package/dist/hooks/use-entity-form/use-entity-form.d.ts +31 -0
  28. package/dist/hooks/use-entity-form/use-entity-form.d.ts.map +1 -0
  29. package/dist/hooks/use-entity-form/use-entity-form.helpers.d.ts +136 -0
  30. package/dist/hooks/use-entity-form/use-entity-form.helpers.d.ts.map +1 -0
  31. package/dist/hooks/use-entity-form/use-entity-form.helpers.js +337 -0
  32. package/dist/hooks/use-entity-form/use-entity-form.helpers.js.map +1 -0
  33. package/dist/hooks/use-entity-form/use-entity-form.js +209 -0
  34. package/dist/hooks/use-entity-form/use-entity-form.js.map +1 -0
  35. package/dist/hooks/use-entity-form/use-entity-form.types.d.ts +173 -0
  36. package/dist/hooks/use-entity-form/use-entity-form.types.d.ts.map +1 -0
  37. package/dist/hooks/use-entity-form/use-entity-form.types.js +3 -0
  38. package/dist/hooks/use-entity-form/use-entity-form.types.js.map +1 -0
  39. package/dist/hooks/use-entity-link-resolver/index.d.ts +2 -0
  40. package/dist/hooks/use-entity-link-resolver/index.d.ts.map +1 -0
  41. package/dist/hooks/use-entity-link-resolver/index.js +3 -0
  42. package/dist/hooks/use-entity-link-resolver/index.js.map +1 -0
  43. package/dist/hooks/use-entity-link-resolver/use-entity-link-resolver.d.ts +17 -0
  44. package/dist/hooks/use-entity-link-resolver/use-entity-link-resolver.d.ts.map +1 -0
  45. package/dist/hooks/use-entity-link-resolver/use-entity-link-resolver.js +26 -0
  46. package/dist/hooks/use-entity-link-resolver/use-entity-link-resolver.js.map +1 -0
  47. package/dist/hooks/use-entity-link-source/index.d.ts +3 -0
  48. package/dist/hooks/use-entity-link-source/index.d.ts.map +1 -0
  49. package/dist/hooks/use-entity-link-source/index.js +4 -0
  50. package/dist/hooks/use-entity-link-source/index.js.map +1 -0
  51. package/dist/hooks/use-entity-link-source/use-entity-link-source.d.ts +25 -0
  52. package/dist/hooks/use-entity-link-source/use-entity-link-source.d.ts.map +1 -0
  53. package/dist/hooks/use-entity-link-source/use-entity-link-source.js +169 -0
  54. package/dist/hooks/use-entity-link-source/use-entity-link-source.js.map +1 -0
  55. package/dist/hooks/use-entity-link-source/use-entity-link-source.types.d.ts +49 -0
  56. package/dist/hooks/use-entity-link-source/use-entity-link-source.types.d.ts.map +1 -0
  57. package/dist/hooks/use-entity-link-source/use-entity-link-source.types.js +3 -0
  58. package/dist/hooks/use-entity-link-source/use-entity-link-source.types.js.map +1 -0
  59. package/dist/hooks/use-entity-mutation/index.d.ts +3 -0
  60. package/dist/hooks/use-entity-mutation/index.d.ts.map +1 -0
  61. package/dist/hooks/use-entity-mutation/index.js +4 -0
  62. package/dist/hooks/use-entity-mutation/index.js.map +1 -0
  63. package/dist/hooks/use-entity-mutation/use-entity-mutation.d.ts +22 -0
  64. package/dist/hooks/use-entity-mutation/use-entity-mutation.d.ts.map +1 -0
  65. package/dist/hooks/use-entity-mutation/use-entity-mutation.js +88 -0
  66. package/dist/hooks/use-entity-mutation/use-entity-mutation.js.map +1 -0
  67. package/dist/hooks/use-entity-mutation/use-entity-mutation.types.d.ts +19 -0
  68. package/dist/hooks/use-entity-mutation/use-entity-mutation.types.d.ts.map +1 -0
  69. package/dist/hooks/use-entity-mutation/use-entity-mutation.types.js +3 -0
  70. package/dist/hooks/use-entity-mutation/use-entity-mutation.types.js.map +1 -0
  71. package/dist/hooks/use-entity-record/index.d.ts +3 -0
  72. package/dist/hooks/use-entity-record/index.d.ts.map +1 -0
  73. package/dist/hooks/use-entity-record/index.js +4 -0
  74. package/dist/hooks/use-entity-record/index.js.map +1 -0
  75. package/dist/hooks/use-entity-record/use-entity-record.d.ts +21 -0
  76. package/dist/hooks/use-entity-record/use-entity-record.d.ts.map +1 -0
  77. package/dist/hooks/use-entity-record/use-entity-record.js +93 -0
  78. package/dist/hooks/use-entity-record/use-entity-record.js.map +1 -0
  79. package/dist/hooks/use-entity-record/use-entity-record.types.d.ts +22 -0
  80. package/dist/hooks/use-entity-record/use-entity-record.types.d.ts.map +1 -0
  81. package/dist/hooks/use-entity-record/use-entity-record.types.js +3 -0
  82. package/dist/hooks/use-entity-record/use-entity-record.types.js.map +1 -0
  83. package/dist/hooks/use-entity-use-cases/index.d.ts +2 -0
  84. package/dist/hooks/use-entity-use-cases/index.d.ts.map +1 -0
  85. package/dist/hooks/use-entity-use-cases/index.js +3 -0
  86. package/dist/hooks/use-entity-use-cases/index.js.map +1 -0
  87. package/dist/hooks/use-entity-use-cases/use-entity-use-cases.d.ts +25 -0
  88. package/dist/hooks/use-entity-use-cases/use-entity-use-cases.d.ts.map +1 -0
  89. package/dist/hooks/use-entity-use-cases/use-entity-use-cases.js +47 -0
  90. package/dist/hooks/use-entity-use-cases/use-entity-use-cases.js.map +1 -0
  91. package/dist/hooks/use-wizard/index.d.ts +3 -0
  92. package/dist/hooks/use-wizard/index.d.ts.map +1 -0
  93. package/dist/hooks/use-wizard/index.js +4 -0
  94. package/dist/hooks/use-wizard/index.js.map +1 -0
  95. package/dist/hooks/use-wizard/use-wizard.d.ts +19 -0
  96. package/dist/hooks/use-wizard/use-wizard.d.ts.map +1 -0
  97. package/dist/hooks/use-wizard/use-wizard.js +136 -0
  98. package/dist/hooks/use-wizard/use-wizard.js.map +1 -0
  99. package/dist/hooks/use-wizard/use-wizard.types.d.ts +87 -0
  100. package/dist/hooks/use-wizard/use-wizard.types.d.ts.map +1 -0
  101. package/dist/hooks/use-wizard/use-wizard.types.js +3 -0
  102. package/dist/hooks/use-wizard/use-wizard.types.js.map +1 -0
  103. package/dist/index.d.ts +17 -0
  104. package/dist/index.d.ts.map +1 -0
  105. package/dist/index.js +18 -0
  106. package/dist/index.js.map +1 -0
  107. package/dist/query/combine-filtering.d.ts +11 -0
  108. package/dist/query/combine-filtering.d.ts.map +1 -0
  109. package/dist/query/combine-filtering.js +18 -0
  110. package/dist/query/combine-filtering.js.map +1 -0
  111. package/dist/query/entity-query-key.d.ts +28 -0
  112. package/dist/query/entity-query-key.d.ts.map +1 -0
  113. package/dist/query/entity-query-key.js +28 -0
  114. package/dist/query/entity-query-key.js.map +1 -0
  115. package/dist/query/query-provider.d.ts +21 -0
  116. package/dist/query/query-provider.d.ts.map +1 -0
  117. package/dist/query/query-provider.js +35 -0
  118. package/dist/query/query-provider.js.map +1 -0
  119. package/dist/reactive/event-source-channel.d.ts +22 -0
  120. package/dist/reactive/event-source-channel.d.ts.map +1 -0
  121. package/dist/reactive/event-source-channel.js +93 -0
  122. package/dist/reactive/event-source-channel.js.map +1 -0
  123. package/dist/reactive/reactive-channel.d.ts +75 -0
  124. package/dist/reactive/reactive-channel.d.ts.map +1 -0
  125. package/dist/reactive/reactive-channel.js +55 -0
  126. package/dist/reactive/reactive-channel.js.map +1 -0
  127. package/dist/reactive/use-reactive-invalidation.d.ts +10 -0
  128. package/dist/reactive/use-reactive-invalidation.d.ts.map +1 -0
  129. package/dist/reactive/use-reactive-invalidation.js +25 -0
  130. package/dist/reactive/use-reactive-invalidation.js.map +1 -0
  131. package/dist/reactive/use-transaction-settlement.d.ts +41 -0
  132. package/dist/reactive/use-transaction-settlement.d.ts.map +1 -0
  133. package/dist/reactive/use-transaction-settlement.js +123 -0
  134. package/dist/reactive/use-transaction-settlement.js.map +1 -0
  135. package/package.json +73 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/hooks/use-wizard/use-wizard.ts"],"sourcesContent":["'use client';\n\nimport {\n advanceWizard,\n canFinish as canFinishWizard,\n emptyWizardState,\n type EntityDraft,\n entryStep,\n goBackWizard,\n goToWizardStep,\n stepStatuses,\n withStepValue,\n type WizardState,\n type WizardStepValue,\n} from '@entifix/core';\nimport { useCallback, useEffect, useMemo, useRef, useState } from 'react';\n\nimport type { EntityDraftStore } from '../use-entity-form/use-entity-form.types.js';\nimport type { UseWizardOptions, UseWizardResult } from './use-wizard.types.js';\n\n/**\n * Drives one wizard: where the operator is, what they have answered, and the\n * per-step draft each form step edits.\n *\n * **The draft lives here, above the forms.** A form step calls `useEntityForm`\n * itself — React's hook count must stay fixed, so N form steps cannot be N hook\n * calls in one component — and it unmounts when it is not the active step. What\n * makes Back safe is therefore not keeping the step mounted but keeping its\n * values out of it: {@link UseWizardResult.draftStoreFor} hands the step an\n * `EntityDraftStore` viewing this state, and the step is free to come and go\n * ([ADR 0045](../../../../../../../docs/adr/0045-the-wizard-a-step-graph-and-a-submit-that-hands-off.md)).\n *\n * It holds no router and performs no submit. `onStepChange` reports where the\n * operator went so a shell can write the URL, and finishing is the host's — this\n * hook does not know a transaction exists.\n */\nexport function useWizard({\n definition,\n draft,\n errored,\n onStepChange,\n}: UseWizardOptions): UseWizardResult {\n const entry = useMemo(() => entryStep(definition), [definition]);\n const [state, setState] = useState<WizardState>(() =>\n emptyWizardState(entry.id),\n );\n const [isResumed, setResumed] = useState(false);\n\n /**\n * The state as of the last commit, mirrored so a handler can read it without\n * a stale closure **and without doing its work inside a `setState` updater** —\n * an updater runs twice under StrictMode, which would fire `onStepChange`\n * and the persistence write twice per advance.\n */\n const stateRef = useRef(state);\n const touched = useRef(false);\n const adopted = useRef(false);\n\n const save = draft?.save;\n const persisted = draft?.state;\n\n const commit = useCallback(\n (updated: WizardState) => {\n const current = stateRef.current;\n if (updated === current) return;\n\n touched.current = true;\n stateRef.current = updated;\n setState(updated);\n save?.(updated);\n\n if (updated.activeStep !== current.activeStep) {\n // Resuming is a fact about arriving. Once the operator moves, the recap\n // has been read or ignored, and a banner that never leaves stops being\n // read at all.\n setResumed(false);\n onStepChange?.(updated.activeStep);\n }\n },\n [save, onStepChange],\n );\n\n const apply = useCallback(\n (next: (current: WizardState) => WizardState) =>\n commit(next(stateRef.current)),\n [commit],\n );\n\n /**\n * A persisted wizard is adopted **once, and only while untouched**.\n *\n * The store hydrates from an effect (`skipHydration`, ADR 0032), so it arrives\n * after the first render and cannot be a `useState` initializer. Adopting it\n * more than once would let a later hydration overwrite what the operator has\n * since typed, and adopting it after a first edit would do the same on the\n * very first keystroke.\n */\n useEffect(() => {\n if (adopted.current || touched.current || persisted === undefined) return;\n adopted.current = true;\n stateRef.current = persisted;\n setState(persisted);\n // Only a wizard that was genuinely mid-flow is a resume; one persisted on\n // its first step has nothing to recap.\n setResumed(persisted.history.length > 0);\n }, [persisted]);\n\n /**\n * One draft store per step id, minted once and kept forever.\n *\n * `useEntityForm` writes from an effect keyed on `save`, so a new identity per\n * render would make every render a write. A `Map` in a ref rather than a memo\n * because the set of steps a caller asks for grows as the flow is walked, and\n * a memo keyed on that set would rebuild every entry whenever one was added.\n */\n const stores = useRef(\n new Map<string, Pick<EntityDraftStore, 'save' | 'clear'>>(),\n );\n\n const draftStoreFor = useCallback(\n (stepId: string): EntityDraftStore => {\n let store = stores.current.get(stepId);\n if (store === undefined) {\n store = {\n save: (values: EntityDraft) =>\n apply(current =>\n withStepValue(current, stepId, { kind: 'form', values }),\n ),\n clear: () =>\n apply(current => withStepValue(current, stepId, { kind: 'none' })),\n };\n stores.current.set(stepId, store);\n }\n\n const value = state.steps[stepId];\n return {\n // The stored reference, and the key is **omitted** when the step holds\n // no draft — a fresh `{}` per render would re-seed the form every render,\n // which is the `Maximum update depth exceeded` hang ADR 0038 measured.\n ...(value?.kind === 'form' ? { draft: value.values } : {}),\n save: store.save,\n clear: store.clear,\n };\n },\n [apply, state],\n );\n\n return {\n state,\n activeStep: state.activeStep,\n steps: stepStatuses(definition, state, errored),\n canFinish: canFinishWizard(definition, state),\n isResumed,\n draftStoreFor,\n setStepValue: useCallback(\n (stepId: string, value: WizardStepValue) =>\n apply(current => withStepValue(current, stepId, value)),\n [apply],\n ),\n next: useCallback(\n () => apply(current => advanceWizard(definition, current)),\n [apply, definition],\n ),\n previous: useCallback(() => apply(goBackWizard), [apply]),\n goTo: useCallback(\n (stepId: string) => apply(current => goToWizardStep(current, stepId)),\n [apply],\n ),\n reset: useCallback(\n () => apply(() => emptyWizardState(entry.id)),\n [apply, entry],\n ),\n };\n}\n"],"names":["advanceWizard","canFinish","canFinishWizard","emptyWizardState","entryStep","goBackWizard","goToWizardStep","stepStatuses","withStepValue","useCallback","useEffect","useMemo","useRef","useState","useWizard","definition","draft","errored","onStepChange","entry","state","setState","id","isResumed","setResumed","stateRef","touched","adopted","save","persisted","commit","updated","current","activeStep","apply","next","undefined","history","length","stores","Map","draftStoreFor","stepId","store","get","values","kind","clear","set","value","steps","setStepValue","previous","goTo","reset"],"mappings":"AAAA;;AAEA,SACEA,aAAa,EACbC,aAAaC,eAAe,EAC5BC,gBAAgB,EAEhBC,SAAS,EACTC,YAAY,EACZC,cAAc,EACdC,YAAY,EACZC,aAAa,QAGR,gBAAgB;AACvB,SAASC,WAAW,EAAEC,SAAS,EAAEC,OAAO,EAAEC,MAAM,EAAEC,QAAQ,QAAQ,QAAQ;AAK1E;;;;;;;;;;;;;;;CAeC,GACD,OAAO,SAASC,UAAU,EACxBC,UAAU,EACVC,KAAK,EACLC,OAAO,EACPC,YAAY,EACK;IACjB,MAAMC,QAAQR,QAAQ,IAAMP,UAAUW,aAAa;QAACA;KAAW;IAC/D,MAAM,CAACK,OAAOC,SAAS,GAAGR,SAAsB,IAC9CV,iBAAiBgB,MAAMG,EAAE;IAE3B,MAAM,CAACC,WAAWC,WAAW,GAAGX,SAAS;IAEzC;;;;;GAKC,GACD,MAAMY,WAAWb,OAAOQ;IACxB,MAAMM,UAAUd,OAAO;IACvB,MAAMe,UAAUf,OAAO;IAEvB,MAAMgB,OAAOZ,yBAAAA,MAAOY,IAAI;IACxB,MAAMC,YAAYb,yBAAAA,MAAOI,KAAK;IAE9B,MAAMU,SAASrB,YACb,CAACsB;QACC,MAAMC,UAAUP,SAASO,OAAO;QAChC,IAAID,YAAYC,SAAS;QAEzBN,QAAQM,OAAO,GAAG;QAClBP,SAASO,OAAO,GAAGD;QACnBV,SAASU;QACTH,wBAAAA,KAAOG;QAEP,IAAIA,QAAQE,UAAU,KAAKD,QAAQC,UAAU,EAAE;YAC7C,wEAAwE;YACxE,uEAAuE;YACvE,eAAe;YACfT,WAAW;YACXN,gCAAAA,aAAea,QAAQE,UAAU;QACnC;IACF,GACA;QAACL;QAAMV;KAAa;IAGtB,MAAMgB,QAAQzB,YACZ,CAAC0B,OACCL,OAAOK,KAAKV,SAASO,OAAO,IAC9B;QAACF;KAAO;IAGV;;;;;;;;GAQC,GACDpB,UAAU;QACR,IAAIiB,QAAQK,OAAO,IAAIN,QAAQM,OAAO,IAAIH,cAAcO,WAAW;QACnET,QAAQK,OAAO,GAAG;QAClBP,SAASO,OAAO,GAAGH;QACnBR,SAASQ;QACT,0EAA0E;QAC1E,uCAAuC;QACvCL,WAAWK,UAAUQ,OAAO,CAACC,MAAM,GAAG;IACxC,GAAG;QAACT;KAAU;IAEd;;;;;;;GAOC,GACD,MAAMU,SAAS3B,OACb,IAAI4B;IAGN,MAAMC,gBAAgBhC,YACpB,CAACiC;QACC,IAAIC,QAAQJ,OAAOP,OAAO,CAACY,GAAG,CAACF;QAC/B,IAAIC,UAAUP,WAAW;YACvBO,QAAQ;gBACNf,MAAM,CAACiB,SACLX,MAAMF,CAAAA,UACJxB,cAAcwB,SAASU,QAAQ;4BAAEI,MAAM;4BAAQD;wBAAO;gBAE1DE,OAAO,IACLb,MAAMF,CAAAA,UAAWxB,cAAcwB,SAASU,QAAQ;4BAAEI,MAAM;wBAAO;YACnE;YACAP,OAAOP,OAAO,CAACgB,GAAG,CAACN,QAAQC;QAC7B;QAEA,MAAMM,QAAQ7B,MAAM8B,KAAK,CAACR,OAAO;QACjC,OAAO,aAIDO,CAAAA,yBAAAA,MAAOH,IAAI,MAAK,SAAS;YAAE9B,OAAOiC,MAAMJ,MAAM;QAAC,IAAI,CAAC;YACxDjB,MAAMe,MAAMf,IAAI;YAChBmB,OAAOJ,MAAMI,KAAK;;IAEtB,GACA;QAACb;QAAOd;KAAM;IAGhB,OAAO;QACLA;QACAa,YAAYb,MAAMa,UAAU;QAC5BiB,OAAO3C,aAAaQ,YAAYK,OAAOH;QACvChB,WAAWC,gBAAgBa,YAAYK;QACvCG;QACAkB;QACAU,cAAc1C,YACZ,CAACiC,QAAgBO,QACff,MAAMF,CAAAA,UAAWxB,cAAcwB,SAASU,QAAQO,SAClD;YAACf;SAAM;QAETC,MAAM1B,YACJ,IAAMyB,MAAMF,CAAAA,UAAWhC,cAAce,YAAYiB,WACjD;YAACE;YAAOnB;SAAW;QAErBqC,UAAU3C,YAAY,IAAMyB,MAAM7B,eAAe;YAAC6B;SAAM;QACxDmB,MAAM5C,YACJ,CAACiC,SAAmBR,MAAMF,CAAAA,UAAW1B,eAAe0B,SAASU,UAC7D;YAACR;SAAM;QAEToB,OAAO7C,YACL,IAAMyB,MAAM,IAAM/B,iBAAiBgB,MAAMG,EAAE,IAC3C;YAACY;YAAOf;SAAM;IAElB;AACF"}
@@ -0,0 +1,87 @@
1
+ import type { WizardDefinition, WizardState, WizardStepStatus, WizardStepValue } from '@entifix/core';
2
+ import type { EntityDraftStore } from '../use-entity-form/use-entity-form.types.js';
3
+ /**
4
+ * Where a wizard's state is persisted, as the hook sees it.
5
+ *
6
+ * A **port**, and for exactly the reason `EntityDraftStore` is one: `useDraft`
7
+ * and the zustand store behind it are `layer:shell`, this package is
8
+ * `layer:entifix`, and `@nx/enforce-module-boundaries` fails the build on the
9
+ * upward edge. `useWizardDraft` in `@entifix/next-shell` is the one adapter.
10
+ *
11
+ * It is a second port rather than a reuse of `EntityDraftStore` because the two
12
+ * carry different shapes — one holds an `EntityDraft`, this holds a whole
13
+ * {@link WizardState} — and a single port widened to both would let a form be
14
+ * handed a wizard's state by a caller that mixed them up.
15
+ */
16
+ export interface WizardDraftStore {
17
+ /** The persisted wizard, or `undefined` before anything was saved. */
18
+ readonly state?: WizardState;
19
+ /**
20
+ * Persist the wizard. Called on every answered step.
21
+ *
22
+ * Must be referentially stable across renders, for the reason
23
+ * `EntityDraftStore.save` must be: the write happens from an effect keyed on
24
+ * it, and a fresh identity per render turns every render into a write.
25
+ */
26
+ save(state: WizardState): void;
27
+ /**
28
+ * Discard it.
29
+ *
30
+ * **The hook never calls this.** A wizard is spent when its submit is
31
+ * *accepted*, and the hook does not know whether it was — it neither submits
32
+ * nor watches the transaction. The host that owns the mutation calls it, the
33
+ * same division `EntityDraftStore.clear` already draws.
34
+ */
35
+ clear(): void;
36
+ }
37
+ export interface UseWizardOptions {
38
+ definition: WizardDefinition;
39
+ /** Where to persist, so a half-finished flow survives a refresh. */
40
+ draft?: WizardDraftStore;
41
+ /**
42
+ * Steps the caller knows are invalid, overlaid onto the stepper.
43
+ *
44
+ * Supplied rather than derived: a form step's validity lives in its own
45
+ * `useEntityForm` instance, and this hook holds none of them.
46
+ */
47
+ errored?: readonly string[];
48
+ /** Called whenever the active step changes, so a shell can write the URL. */
49
+ onStepChange?: (stepId: string) => void;
50
+ }
51
+ export interface UseWizardResult {
52
+ state: WizardState;
53
+ activeStep: string;
54
+ /** The steps on the projected path, with how each reads. */
55
+ steps: ReadonlyArray<{
56
+ readonly id: string;
57
+ readonly status: WizardStepStatus;
58
+ }>;
59
+ /** The active step ends the flow. */
60
+ canFinish: boolean;
61
+ /**
62
+ * A persisted wizard was picked up mid-flow, so the host owes a recap.
63
+ *
64
+ * True only until the operator moves — resuming is a fact about arriving, and
65
+ * a banner that never goes away stops being read.
66
+ */
67
+ isResumed: boolean;
68
+ /**
69
+ * An `EntityDraftStore` viewing one form step's draft, to hand straight to
70
+ * `useEntityForm`.
71
+ *
72
+ * ⚠️ Its `save` is **stable per step id** and its `draft` is the stored
73
+ * reference, so a step that has not changed re-seeds nothing. Building either
74
+ * fresh per render is the write-amplification `useEntityDraft` documents, and
75
+ * a fresh `{}` for an unanswered step is worse — it re-seeds the form every
76
+ * render, which is the `Maximum update depth exceeded` hang ADR 0038 measured.
77
+ */
78
+ draftStoreFor(stepId: string): EntityDraftStore;
79
+ /** Record a non-form step's answer — a choice, a table selection. */
80
+ setStepValue(stepId: string, value: WizardStepValue): void;
81
+ next(): void;
82
+ previous(): void;
83
+ goTo(stepId: string): void;
84
+ /** Start over, discarding every answer. The host clears the draft store. */
85
+ reset(): void;
86
+ }
87
+ //# sourceMappingURL=use-wizard.types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-wizard.types.d.ts","sourceRoot":"","sources":["../../../src/hooks/use-wizard/use-wizard.types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,gBAAgB,EAChB,WAAW,EACX,gBAAgB,EAChB,eAAe,EAChB,MAAM,eAAe,CAAC;AAEvB,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,6CAA6C,CAAC;AAEpF;;;;;;;;;;;;GAYG;AACH,MAAM,WAAW,gBAAgB;IAC/B,sEAAsE;IACtE,QAAQ,CAAC,KAAK,CAAC,EAAE,WAAW,CAAC;IAC7B;;;;;;OAMG;IACH,IAAI,CAAC,KAAK,EAAE,WAAW,GAAG,IAAI,CAAC;IAC/B;;;;;;;OAOG;IACH,KAAK,IAAI,IAAI,CAAC;CACf;AAED,MAAM,WAAW,gBAAgB;IAC/B,UAAU,EAAE,gBAAgB,CAAC;IAC7B,oEAAoE;IACpE,KAAK,CAAC,EAAE,gBAAgB,CAAC;IACzB;;;;;OAKG;IACH,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC5B,6EAA6E;IAC7E,YAAY,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;CACzC;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,WAAW,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,4DAA4D;IAC5D,KAAK,EAAE,aAAa,CAAC;QACnB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;QACpB,QAAQ,CAAC,MAAM,EAAE,gBAAgB,CAAC;KACnC,CAAC,CAAC;IACH,qCAAqC;IACrC,SAAS,EAAE,OAAO,CAAC;IACnB;;;;;OAKG;IACH,SAAS,EAAE,OAAO,CAAC;IAEnB;;;;;;;;;OASG;IACH,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,gBAAgB,CAAC;IAChD,qEAAqE;IACrE,YAAY,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,eAAe,GAAG,IAAI,CAAC;IAE3D,IAAI,IAAI,IAAI,CAAC;IACb,QAAQ,IAAI,IAAI,CAAC;IACjB,IAAI,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,4EAA4E;IAC5E,KAAK,IAAI,IAAI,CAAC;CACf"}
@@ -0,0 +1,3 @@
1
+ export { };
2
+
3
+ //# sourceMappingURL=use-wizard.types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/hooks/use-wizard/use-wizard.types.ts"],"sourcesContent":["import type {\n WizardDefinition,\n WizardState,\n WizardStepStatus,\n WizardStepValue,\n} from '@entifix/core';\n\nimport type { EntityDraftStore } from '../use-entity-form/use-entity-form.types.js';\n\n/**\n * Where a wizard's state is persisted, as the hook sees it.\n *\n * A **port**, and for exactly the reason `EntityDraftStore` is one: `useDraft`\n * and the zustand store behind it are `layer:shell`, this package is\n * `layer:entifix`, and `@nx/enforce-module-boundaries` fails the build on the\n * upward edge. `useWizardDraft` in `@entifix/next-shell` is the one adapter.\n *\n * It is a second port rather than a reuse of `EntityDraftStore` because the two\n * carry different shapes — one holds an `EntityDraft`, this holds a whole\n * {@link WizardState} — and a single port widened to both would let a form be\n * handed a wizard's state by a caller that mixed them up.\n */\nexport interface WizardDraftStore {\n /** The persisted wizard, or `undefined` before anything was saved. */\n readonly state?: WizardState;\n /**\n * Persist the wizard. Called on every answered step.\n *\n * Must be referentially stable across renders, for the reason\n * `EntityDraftStore.save` must be: the write happens from an effect keyed on\n * it, and a fresh identity per render turns every render into a write.\n */\n save(state: WizardState): void;\n /**\n * Discard it.\n *\n * **The hook never calls this.** A wizard is spent when its submit is\n * *accepted*, and the hook does not know whether it was — it neither submits\n * nor watches the transaction. The host that owns the mutation calls it, the\n * same division `EntityDraftStore.clear` already draws.\n */\n clear(): void;\n}\n\nexport interface UseWizardOptions {\n definition: WizardDefinition;\n /** Where to persist, so a half-finished flow survives a refresh. */\n draft?: WizardDraftStore;\n /**\n * Steps the caller knows are invalid, overlaid onto the stepper.\n *\n * Supplied rather than derived: a form step's validity lives in its own\n * `useEntityForm` instance, and this hook holds none of them.\n */\n errored?: readonly string[];\n /** Called whenever the active step changes, so a shell can write the URL. */\n onStepChange?: (stepId: string) => void;\n}\n\nexport interface UseWizardResult {\n state: WizardState;\n activeStep: string;\n /** The steps on the projected path, with how each reads. */\n steps: ReadonlyArray<{\n readonly id: string;\n readonly status: WizardStepStatus;\n }>;\n /** The active step ends the flow. */\n canFinish: boolean;\n /**\n * A persisted wizard was picked up mid-flow, so the host owes a recap.\n *\n * True only until the operator moves — resuming is a fact about arriving, and\n * a banner that never goes away stops being read.\n */\n isResumed: boolean;\n\n /**\n * An `EntityDraftStore` viewing one form step's draft, to hand straight to\n * `useEntityForm`.\n *\n * ⚠️ Its `save` is **stable per step id** and its `draft` is the stored\n * reference, so a step that has not changed re-seeds nothing. Building either\n * fresh per render is the write-amplification `useEntityDraft` documents, and\n * a fresh `{}` for an unanswered step is worse — it re-seeds the form every\n * render, which is the `Maximum update depth exceeded` hang ADR 0038 measured.\n */\n draftStoreFor(stepId: string): EntityDraftStore;\n /** Record a non-form step's answer — a choice, a table selection. */\n setStepValue(stepId: string, value: WizardStepValue): void;\n\n next(): void;\n previous(): void;\n goTo(stepId: string): void;\n /** Start over, discarding every answer. The host clears the draft store. */\n reset(): void;\n}\n"],"names":[],"mappings":"AA2DA,WAqCC"}
@@ -0,0 +1,17 @@
1
+ export * from './context/adapters-context/index.js';
2
+ export * from './hooks/use-data-loading/index.js';
3
+ export * from './hooks/use-entity-form/index.js';
4
+ export * from './hooks/use-entity-link-resolver/index.js';
5
+ export * from './hooks/use-entity-link-source/index.js';
6
+ export * from './hooks/use-entity-mutation/index.js';
7
+ export * from './hooks/use-entity-record/index.js';
8
+ export * from './hooks/use-entity-use-cases/index.js';
9
+ export * from './hooks/use-wizard/index.js';
10
+ export * from './query/combine-filtering.js';
11
+ export * from './query/entity-query-key.js';
12
+ export * from './query/query-provider.js';
13
+ export * from './reactive/event-source-channel.js';
14
+ export * from './reactive/reactive-channel.js';
15
+ export * from './reactive/use-reactive-invalidation.js';
16
+ export * from './reactive/use-transaction-settlement.js';
17
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,qCAAqC,CAAC;AACpD,cAAc,mCAAmC,CAAC;AAClD,cAAc,kCAAkC,CAAC;AACjD,cAAc,2CAA2C,CAAC;AAC1D,cAAc,yCAAyC,CAAC;AACxD,cAAc,sCAAsC,CAAC;AACrD,cAAc,oCAAoC,CAAC;AACnD,cAAc,uCAAuC,CAAC;AACtD,cAAc,6BAA6B,CAAC;AAC5C,cAAc,8BAA8B,CAAC;AAC7C,cAAc,6BAA6B,CAAC;AAC5C,cAAc,2BAA2B,CAAC;AAC1C,cAAc,oCAAoC,CAAC;AACnD,cAAc,gCAAgC,CAAC;AAC/C,cAAc,yCAAyC,CAAC;AACxD,cAAc,0CAA0C,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,18 @@
1
+ export * from './context/adapters-context/index.js';
2
+ export * from './hooks/use-data-loading/index.js';
3
+ export * from './hooks/use-entity-form/index.js';
4
+ export * from './hooks/use-entity-link-resolver/index.js';
5
+ export * from './hooks/use-entity-link-source/index.js';
6
+ export * from './hooks/use-entity-mutation/index.js';
7
+ export * from './hooks/use-entity-record/index.js';
8
+ export * from './hooks/use-entity-use-cases/index.js';
9
+ export * from './hooks/use-wizard/index.js';
10
+ export * from './query/combine-filtering.js';
11
+ export * from './query/entity-query-key.js';
12
+ export * from './query/query-provider.js';
13
+ export * from './reactive/event-source-channel.js';
14
+ export * from './reactive/reactive-channel.js';
15
+ export * from './reactive/use-reactive-invalidation.js';
16
+ export * from './reactive/use-transaction-settlement.js';
17
+
18
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts"],"sourcesContent":["export * from './context/adapters-context/index.js';\nexport * from './hooks/use-data-loading/index.js';\nexport * from './hooks/use-entity-form/index.js';\nexport * from './hooks/use-entity-link-resolver/index.js';\nexport * from './hooks/use-entity-link-source/index.js';\nexport * from './hooks/use-entity-mutation/index.js';\nexport * from './hooks/use-entity-record/index.js';\nexport * from './hooks/use-entity-use-cases/index.js';\nexport * from './hooks/use-wizard/index.js';\nexport * from './query/combine-filtering.js';\nexport * from './query/entity-query-key.js';\nexport * from './query/query-provider.js';\nexport * from './reactive/event-source-channel.js';\nexport * from './reactive/reactive-channel.js';\nexport * from './reactive/use-reactive-invalidation.js';\nexport * from './reactive/use-transaction-settlement.js';\n"],"names":[],"mappings":"AAAA,cAAc,sCAAsC;AACpD,cAAc,oCAAoC;AAClD,cAAc,mCAAmC;AACjD,cAAc,4CAA4C;AAC1D,cAAc,0CAA0C;AACxD,cAAc,uCAAuC;AACrD,cAAc,qCAAqC;AACnD,cAAc,wCAAwC;AACtD,cAAc,8BAA8B;AAC5C,cAAc,+BAA+B;AAC7C,cAAc,8BAA8B;AAC5C,cAAc,4BAA4B;AAC1C,cAAc,qCAAqC;AACnD,cAAc,iCAAiC;AAC/C,cAAc,0CAA0C;AACxD,cAAc,2CAA2C"}
@@ -0,0 +1,11 @@
1
+ import type { Entity, FilterGroup } from '@entifix/core';
2
+ /**
3
+ * ANDs filter groups, dropping the ones that say nothing.
4
+ *
5
+ * A group with no values matches everything, so keeping it would send a filter
6
+ * that narrows nothing; and a single surviving group is returned as itself rather
7
+ * than wrapped, so the serialized RSQL — which is also a query cache key — does
8
+ * not change shape just because a restriction was configured.
9
+ */
10
+ export declare function combineFilterGroups<TEntity extends Entity>(...groups: ReadonlyArray<FilterGroup<TEntity> | undefined>): FilterGroup<TEntity> | undefined;
11
+ //# sourceMappingURL=combine-filtering.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"combine-filtering.d.ts","sourceRoot":"","sources":["../../src/query/combine-filtering.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAEzD;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,SAAS,MAAM,EACxD,GAAG,MAAM,EAAE,aAAa,CAAC,WAAW,CAAC,OAAO,CAAC,GAAG,SAAS,CAAC,GACzD,WAAW,CAAC,OAAO,CAAC,GAAG,SAAS,CASlC"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * ANDs filter groups, dropping the ones that say nothing.
3
+ *
4
+ * A group with no values matches everything, so keeping it would send a filter
5
+ * that narrows nothing; and a single surviving group is returned as itself rather
6
+ * than wrapped, so the serialized RSQL — which is also a query cache key — does
7
+ * not change shape just because a restriction was configured.
8
+ */ export function combineFilterGroups(...groups) {
9
+ const meaningful = groups.filter((group)=>group !== undefined && group.values.length > 0);
10
+ if (meaningful.length === 0) return undefined;
11
+ if (meaningful.length === 1) return meaningful[0];
12
+ return {
13
+ operator: 'and',
14
+ values: meaningful
15
+ };
16
+ }
17
+
18
+ //# sourceMappingURL=combine-filtering.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/query/combine-filtering.ts"],"sourcesContent":["import type { Entity, FilterGroup } from '@entifix/core';\n\n/**\n * ANDs filter groups, dropping the ones that say nothing.\n *\n * A group with no values matches everything, so keeping it would send a filter\n * that narrows nothing; and a single surviving group is returned as itself rather\n * than wrapped, so the serialized RSQL — which is also a query cache key — does\n * not change shape just because a restriction was configured.\n */\nexport function combineFilterGroups<TEntity extends Entity>(\n ...groups: ReadonlyArray<FilterGroup<TEntity> | undefined>\n): FilterGroup<TEntity> | undefined {\n const meaningful = groups.filter(\n (group): group is FilterGroup<TEntity> =>\n group !== undefined && group.values.length > 0,\n );\n\n if (meaningful.length === 0) return undefined;\n if (meaningful.length === 1) return meaningful[0];\n return { operator: 'and', values: meaningful };\n}\n"],"names":["combineFilterGroups","groups","meaningful","filter","group","undefined","values","length","operator"],"mappings":"AAEA;;;;;;;CAOC,GACD,OAAO,SAASA,oBACd,GAAGC,MAAuD;IAE1D,MAAMC,aAAaD,OAAOE,MAAM,CAC9B,CAACC,QACCA,UAAUC,aAAaD,MAAME,MAAM,CAACC,MAAM,GAAG;IAGjD,IAAIL,WAAWK,MAAM,KAAK,GAAG,OAAOF;IACpC,IAAIH,WAAWK,MAAM,KAAK,GAAG,OAAOL,UAAU,CAAC,EAAE;IACjD,OAAO;QAAEM,UAAU;QAAOF,QAAQJ;IAAW;AAC/C"}
@@ -0,0 +1,28 @@
1
+ import { type Entity, type EntityConstructor, type EntityLoadRequest } from '@entifix/core';
2
+ /**
3
+ * The TanStack Query cache key for a single entity list request.
4
+ *
5
+ * `['entity', <entity name>, <serialized load request>]` — the entity name
6
+ * (`key ?? name`, the same one every adapter routes on) scopes the key so two
7
+ * different entities never collide, and the RSQL-serialized load request keys
8
+ * each page/filter/sort variant. Reusing {@link serializeLoadRequestParams}
9
+ * means the key changes exactly when the wire request does — the same string
10
+ * the REST adapter already sends.
11
+ */
12
+ export type EntityQueryKey = readonly ['entity', string, string];
13
+ /** The prefix shared by every query key of an entity — the invalidation scope. */
14
+ export type EntityQueryScope = readonly ['entity', string];
15
+ /**
16
+ * The invalidation scope from the wire name alone.
17
+ *
18
+ * A constructor is what a table or a form holds; a **string** is what arrives
19
+ * out of band — a record search result, an `EntityChangeEvent` — and neither can
20
+ * produce the other without importing every entity class in the fleet. Splitting
21
+ * the derivation here is what makes "invalidate on the same scope
22
+ * `entityQueryScope` uses" a shared function rather than a convention two call
23
+ * sites happen to follow.
24
+ */
25
+ export declare function entityQueryScopeFor(entity: string): EntityQueryScope;
26
+ export declare function entityQueryScope<TEntity extends Entity>(entityConstructor: EntityConstructor<TEntity>): EntityQueryScope;
27
+ export declare function entityQueryKey<TEntity extends Entity>(entityConstructor: EntityConstructor<TEntity>, request: EntityLoadRequest<TEntity>): EntityQueryKey;
28
+ //# sourceMappingURL=entity-query-key.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"entity-query-key.d.ts","sourceRoot":"","sources":["../../src/query/entity-query-key.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,MAAM,EACX,KAAK,iBAAiB,EACtB,KAAK,iBAAiB,EAGvB,MAAM,eAAe,CAAC;AAEvB;;;;;;;;;GASG;AACH,MAAM,MAAM,cAAc,GAAG,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;AAEjE,kFAAkF;AAClF,MAAM,MAAM,gBAAgB,GAAG,SAAS,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;AAE3D;;;;;;;;;GASG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,gBAAgB,CAEpE;AAED,wBAAgB,gBAAgB,CAAC,OAAO,SAAS,MAAM,EACrD,iBAAiB,EAAE,iBAAiB,CAAC,OAAO,CAAC,GAC5C,gBAAgB,CAElB;AAED,wBAAgB,cAAc,CAAC,OAAO,SAAS,MAAM,EACnD,iBAAiB,EAAE,iBAAiB,CAAC,OAAO,CAAC,EAC7C,OAAO,EAAE,iBAAiB,CAAC,OAAO,CAAC,GAClC,cAAc,CAMhB"}
@@ -0,0 +1,28 @@
1
+ import { envelopeEntityName, serializeLoadRequestParams } from '@entifix/core';
2
+ /**
3
+ * The invalidation scope from the wire name alone.
4
+ *
5
+ * A constructor is what a table or a form holds; a **string** is what arrives
6
+ * out of band — a record search result, an `EntityChangeEvent` — and neither can
7
+ * produce the other without importing every entity class in the fleet. Splitting
8
+ * the derivation here is what makes "invalidate on the same scope
9
+ * `entityQueryScope` uses" a shared function rather than a convention two call
10
+ * sites happen to follow.
11
+ */ export function entityQueryScopeFor(entity) {
12
+ return [
13
+ 'entity',
14
+ entity
15
+ ];
16
+ }
17
+ export function entityQueryScope(entityConstructor) {
18
+ return entityQueryScopeFor(envelopeEntityName(entityConstructor));
19
+ }
20
+ export function entityQueryKey(entityConstructor, request) {
21
+ return [
22
+ 'entity',
23
+ envelopeEntityName(entityConstructor),
24
+ serializeLoadRequestParams(request).toString()
25
+ ];
26
+ }
27
+
28
+ //# sourceMappingURL=entity-query-key.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/query/entity-query-key.ts"],"sourcesContent":["import {\n type Entity,\n type EntityConstructor,\n type EntityLoadRequest,\n envelopeEntityName,\n serializeLoadRequestParams,\n} from '@entifix/core';\n\n/**\n * The TanStack Query cache key for a single entity list request.\n *\n * `['entity', <entity name>, <serialized load request>]` — the entity name\n * (`key ?? name`, the same one every adapter routes on) scopes the key so two\n * different entities never collide, and the RSQL-serialized load request keys\n * each page/filter/sort variant. Reusing {@link serializeLoadRequestParams}\n * means the key changes exactly when the wire request does — the same string\n * the REST adapter already sends.\n */\nexport type EntityQueryKey = readonly ['entity', string, string];\n\n/** The prefix shared by every query key of an entity — the invalidation scope. */\nexport type EntityQueryScope = readonly ['entity', string];\n\n/**\n * The invalidation scope from the wire name alone.\n *\n * A constructor is what a table or a form holds; a **string** is what arrives\n * out of band — a record search result, an `EntityChangeEvent` — and neither can\n * produce the other without importing every entity class in the fleet. Splitting\n * the derivation here is what makes \"invalidate on the same scope\n * `entityQueryScope` uses\" a shared function rather than a convention two call\n * sites happen to follow.\n */\nexport function entityQueryScopeFor(entity: string): EntityQueryScope {\n return ['entity', entity];\n}\n\nexport function entityQueryScope<TEntity extends Entity>(\n entityConstructor: EntityConstructor<TEntity>,\n): EntityQueryScope {\n return entityQueryScopeFor(envelopeEntityName(entityConstructor));\n}\n\nexport function entityQueryKey<TEntity extends Entity>(\n entityConstructor: EntityConstructor<TEntity>,\n request: EntityLoadRequest<TEntity>,\n): EntityQueryKey {\n return [\n 'entity',\n envelopeEntityName(entityConstructor),\n serializeLoadRequestParams(request).toString(),\n ];\n}\n"],"names":["envelopeEntityName","serializeLoadRequestParams","entityQueryScopeFor","entity","entityQueryScope","entityConstructor","entityQueryKey","request","toString"],"mappings":"AAAA,SAIEA,kBAAkB,EAClBC,0BAA0B,QACrB,gBAAgB;AAiBvB;;;;;;;;;CASC,GACD,OAAO,SAASC,oBAAoBC,MAAc;IAChD,OAAO;QAAC;QAAUA;KAAO;AAC3B;AAEA,OAAO,SAASC,iBACdC,iBAA6C;IAE7C,OAAOH,oBAAoBF,mBAAmBK;AAChD;AAEA,OAAO,SAASC,eACdD,iBAA6C,EAC7CE,OAAmC;IAEnC,OAAO;QACL;QACAP,mBAAmBK;QACnBJ,2BAA2BM,SAASC,QAAQ;KAC7C;AACH"}
@@ -0,0 +1,21 @@
1
+ import { QueryClient, type QueryClientConfig } from '@tanstack/react-query';
2
+ import { type ReactNode } from 'react';
3
+ /**
4
+ * A `QueryClient` tuned for the entity data layer: server data is cached and
5
+ * served stale-while-revalidate (so a revisited list paints from cache with no
6
+ * spinner), and failed queries surface their {@link EntifixError} immediately
7
+ * rather than being retried behind a spinner.
8
+ */
9
+ export declare function makeQueryClient(config?: QueryClientConfig): QueryClient;
10
+ export interface EntifixQueryProviderProps {
11
+ children: ReactNode;
12
+ /** Inject a client (tests, SSR); otherwise one stable client is created. */
13
+ client?: QueryClient;
14
+ }
15
+ /**
16
+ * Mounts the entity data layer's `QueryClient`. Mount once at the app root,
17
+ * above the adapters provider. Holding the client in `useState` keeps a single
18
+ * instance across renders without recreating it (which would drop the cache).
19
+ */
20
+ export declare function EntifixQueryProvider({ children, client, }: EntifixQueryProviderProps): import("react").JSX.Element;
21
+ //# sourceMappingURL=query-provider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"query-provider.d.ts","sourceRoot":"","sources":["../../src/query/query-provider.tsx"],"names":[],"mappings":"AAEA,OAAO,EACL,WAAW,EACX,KAAK,iBAAiB,EAEvB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,KAAK,SAAS,EAAY,MAAM,OAAO,CAAC;AAEjD;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,MAAM,CAAC,EAAE,iBAAiB,GAAG,WAAW,CAavE;AAED,MAAM,WAAW,yBAAyB;IACxC,QAAQ,EAAE,SAAS,CAAC;IACpB,4EAA4E;IAC5E,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,EACnC,QAAQ,EACR,MAAM,GACP,EAAE,yBAAyB,+BAK3B"}
@@ -0,0 +1,35 @@
1
+ 'use client';
2
+ import { _ as _extends } from "@swc/helpers/_/_extends";
3
+ import { jsx as _jsx } from "react/jsx-runtime";
4
+ import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
5
+ import { useState } from 'react';
6
+ /**
7
+ * A `QueryClient` tuned for the entity data layer: server data is cached and
8
+ * served stale-while-revalidate (so a revisited list paints from cache with no
9
+ * spinner), and failed queries surface their {@link EntifixError} immediately
10
+ * rather than being retried behind a spinner.
11
+ */ export function makeQueryClient(config) {
12
+ var _config_defaultOptions;
13
+ return new QueryClient(_extends({}, config, {
14
+ defaultOptions: _extends({}, config == null ? void 0 : config.defaultOptions, {
15
+ queries: _extends({
16
+ staleTime: 30000,
17
+ retry: false,
18
+ refetchOnWindowFocus: false
19
+ }, config == null ? void 0 : (_config_defaultOptions = config.defaultOptions) == null ? void 0 : _config_defaultOptions.queries)
20
+ })
21
+ }));
22
+ }
23
+ /**
24
+ * Mounts the entity data layer's `QueryClient`. Mount once at the app root,
25
+ * above the adapters provider. Holding the client in `useState` keeps a single
26
+ * instance across renders without recreating it (which would drop the cache).
27
+ */ export function EntifixQueryProvider({ children, client }) {
28
+ const [queryClient] = useState(()=>client != null ? client : makeQueryClient());
29
+ return /*#__PURE__*/ _jsx(QueryClientProvider, {
30
+ client: queryClient,
31
+ children: children
32
+ });
33
+ }
34
+
35
+ //# sourceMappingURL=query-provider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/query/query-provider.tsx"],"sourcesContent":["'use client';\n\nimport {\n QueryClient,\n type QueryClientConfig,\n QueryClientProvider,\n} from '@tanstack/react-query';\nimport { type ReactNode, useState } from 'react';\n\n/**\n * A `QueryClient` tuned for the entity data layer: server data is cached and\n * served stale-while-revalidate (so a revisited list paints from cache with no\n * spinner), and failed queries surface their {@link EntifixError} immediately\n * rather than being retried behind a spinner.\n */\nexport function makeQueryClient(config?: QueryClientConfig): QueryClient {\n return new QueryClient({\n ...config,\n defaultOptions: {\n ...config?.defaultOptions,\n queries: {\n staleTime: 30_000,\n retry: false,\n refetchOnWindowFocus: false,\n ...config?.defaultOptions?.queries,\n },\n },\n });\n}\n\nexport interface EntifixQueryProviderProps {\n children: ReactNode;\n /** Inject a client (tests, SSR); otherwise one stable client is created. */\n client?: QueryClient;\n}\n\n/**\n * Mounts the entity data layer's `QueryClient`. Mount once at the app root,\n * above the adapters provider. Holding the client in `useState` keeps a single\n * instance across renders without recreating it (which would drop the cache).\n */\nexport function EntifixQueryProvider({\n children,\n client,\n}: EntifixQueryProviderProps) {\n const [queryClient] = useState(() => client ?? makeQueryClient());\n return (\n <QueryClientProvider client={queryClient}>{children}</QueryClientProvider>\n );\n}\n"],"names":["QueryClient","QueryClientProvider","useState","makeQueryClient","config","defaultOptions","queries","staleTime","retry","refetchOnWindowFocus","EntifixQueryProvider","children","client","queryClient"],"mappings":"AAAA;;;AAEA,SACEA,WAAW,EAEXC,mBAAmB,QACd,wBAAwB;AAC/B,SAAyBC,QAAQ,QAAQ,QAAQ;AAEjD;;;;;CAKC,GACD,OAAO,SAASC,gBAAgBC,MAA0B;QAS/CA;IART,OAAO,IAAIJ,YAAY,aAClBI;QACHC,gBAAgB,aACXD,0BAAAA,OAAQC,cAAc;YACzBC,SAAS;gBACPC,WAAW;gBACXC,OAAO;gBACPC,sBAAsB;eACnBL,2BAAAA,yBAAAA,OAAQC,cAAc,qBAAtBD,uBAAwBE,OAAO;;;AAI1C;AAQA;;;;CAIC,GACD,OAAO,SAASI,qBAAqB,EACnCC,QAAQ,EACRC,MAAM,EACoB;IAC1B,MAAM,CAACC,YAAY,GAAGX,SAAS,IAAMU,iBAAAA,SAAUT;IAC/C,qBACE,KAACF;QAAoBW,QAAQC;kBAAcF;;AAE/C"}
@@ -0,0 +1,22 @@
1
+ import type { ReactiveChannel } from './reactive-channel.js';
2
+ /**
3
+ * The reactive stream's browser end: one `EventSource` over a same-origin URL.
4
+ *
5
+ * **Same-origin is the whole reason this is SSE and not a WebSocket.** `entifix_at`
6
+ * is `httpOnly` and `sameSite: 'lax'`, and the `WebSocket` constructor accepts
7
+ * no headers — so a socket would need a second class of bearer token handed to
8
+ * client JavaScript, which is the one thing `httpOnly` protects against. An
9
+ * `EventSource` against the app's own proxy path sends the cookie the way every
10
+ * other call in the repo does (ADR 0036).
11
+ *
12
+ * What it also gets for free is the half #137 would otherwise hand-write:
13
+ * `EventSource` reconnects on its own with backoff and re-sends `Last-Event-ID`.
14
+ * The server accepts that header and ignores it — the stream is a hint that
15
+ * something changed, and the record behind it is the truth.
16
+ *
17
+ * The connection is opened lazily on the first subscriber and closed with the
18
+ * last, so a page that mounts no workspace holds none. One connection per URL,
19
+ * not one per listener: SSE shares the origin's HTTP/1.1 connection budget.
20
+ */
21
+ export declare function makeEventSourceReactiveChannel(url: string): ReactiveChannel;
22
+ //# sourceMappingURL=event-source-channel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"event-source-channel.d.ts","sourceRoot":"","sources":["../../src/reactive/event-source-channel.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAEV,eAAe,EAChB,MAAM,uBAAuB,CAAC;AAE/B;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,8BAA8B,CAAC,GAAG,EAAE,MAAM,GAAG,eAAe,CAkF3E"}
@@ -0,0 +1,93 @@
1
+ import { readEventEnvelope } from '@entifix/core';
2
+ import { Effect, Either } from 'effect';
3
+ /**
4
+ * The reactive stream's browser end: one `EventSource` over a same-origin URL.
5
+ *
6
+ * **Same-origin is the whole reason this is SSE and not a WebSocket.** `entifix_at`
7
+ * is `httpOnly` and `sameSite: 'lax'`, and the `WebSocket` constructor accepts
8
+ * no headers — so a socket would need a second class of bearer token handed to
9
+ * client JavaScript, which is the one thing `httpOnly` protects against. An
10
+ * `EventSource` against the app's own proxy path sends the cookie the way every
11
+ * other call in the repo does (ADR 0036).
12
+ *
13
+ * What it also gets for free is the half #137 would otherwise hand-write:
14
+ * `EventSource` reconnects on its own with backoff and re-sends `Last-Event-ID`.
15
+ * The server accepts that header and ignores it — the stream is a hint that
16
+ * something changed, and the record behind it is the truth.
17
+ *
18
+ * The connection is opened lazily on the first subscriber and closed with the
19
+ * last, so a page that mounts no workspace holds none. One connection per URL,
20
+ * not one per listener: SSE shares the origin's HTTP/1.1 connection budget.
21
+ */ export function makeEventSourceReactiveChannel(url) {
22
+ const listeners = new Set();
23
+ const connectListeners = new Set();
24
+ let source;
25
+ // Tracked here rather than read off `source.readyState`, because it must
26
+ // survive the close/reopen cycle the refcount drives: what a reconciling
27
+ // consumer needs to know is "has this channel been connected", not "is this
28
+ // particular `EventSource` object open right now".
29
+ let connected = false;
30
+ const deliver = (raw)=>{
31
+ // A frame this build cannot read is dropped rather than thrown: `onmessage`
32
+ // runs inside the browser's event loop, where a throw reaches no caller and
33
+ // would take the rest of this delivery's listeners with it.
34
+ const parsed = Effect.runSync(Effect.either(Effect.flatMap(Effect.try(()=>JSON.parse(raw)), readEventEnvelope)));
35
+ if (Either.isLeft(parsed)) {
36
+ return;
37
+ }
38
+ for (const listener of listeners){
39
+ listener(parsed.right);
40
+ }
41
+ };
42
+ const open = ()=>{
43
+ // Guarded because a server-rendered pass has no `EventSource` at all, and a
44
+ // channel built at module scope must not throw there.
45
+ if (source !== undefined || typeof EventSource === 'undefined') {
46
+ return;
47
+ }
48
+ source = new EventSource(url);
49
+ source.onmessage = (message)=>{
50
+ deliver(message.data);
51
+ };
52
+ // Fires on the first open *and* on every browser-driven reconnect, because
53
+ // `EventSource` reconnects the same object rather than handing back a new
54
+ // one — so nothing has to re-arm this.
55
+ source.onopen = ()=>{
56
+ connected = true;
57
+ for (const listener of connectListeners){
58
+ listener();
59
+ }
60
+ };
61
+ };
62
+ const close = ()=>{
63
+ source == null ? void 0 : source.close();
64
+ source = undefined;
65
+ connected = false;
66
+ };
67
+ return {
68
+ subscribe (listener) {
69
+ listeners.add(listener);
70
+ open();
71
+ return ()=>{
72
+ listeners.delete(listener);
73
+ if (listeners.size === 0) {
74
+ close();
75
+ }
76
+ };
77
+ },
78
+ onConnect (listener) {
79
+ connectListeners.add(listener);
80
+ // ⚠️ The replay, and the reason this signal works at all. Registering does
81
+ // *not* open the connection — subscribing is what does that — so a
82
+ // consumer arriving after another one opened the stream would otherwise
83
+ // wait for a network drop that may never come, and a restored pending set
84
+ // would never be reconciled.
85
+ if (connected) listener();
86
+ return ()=>{
87
+ connectListeners.delete(listener);
88
+ };
89
+ }
90
+ };
91
+ }
92
+
93
+ //# sourceMappingURL=event-source-channel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/reactive/event-source-channel.ts"],"sourcesContent":["import { type EntityChangeEvent, readEventEnvelope } from '@entifix/core';\nimport { Effect, Either } from 'effect';\n\nimport type {\n EntityChangeListener,\n ReactiveChannel,\n} from './reactive-channel.js';\n\n/**\n * The reactive stream's browser end: one `EventSource` over a same-origin URL.\n *\n * **Same-origin is the whole reason this is SSE and not a WebSocket.** `entifix_at`\n * is `httpOnly` and `sameSite: 'lax'`, and the `WebSocket` constructor accepts\n * no headers — so a socket would need a second class of bearer token handed to\n * client JavaScript, which is the one thing `httpOnly` protects against. An\n * `EventSource` against the app's own proxy path sends the cookie the way every\n * other call in the repo does (ADR 0036).\n *\n * What it also gets for free is the half #137 would otherwise hand-write:\n * `EventSource` reconnects on its own with backoff and re-sends `Last-Event-ID`.\n * The server accepts that header and ignores it — the stream is a hint that\n * something changed, and the record behind it is the truth.\n *\n * The connection is opened lazily on the first subscriber and closed with the\n * last, so a page that mounts no workspace holds none. One connection per URL,\n * not one per listener: SSE shares the origin's HTTP/1.1 connection budget.\n */\nexport function makeEventSourceReactiveChannel(url: string): ReactiveChannel {\n const listeners = new Set<EntityChangeListener>();\n const connectListeners = new Set<() => void>();\n let source: EventSource | undefined;\n // Tracked here rather than read off `source.readyState`, because it must\n // survive the close/reopen cycle the refcount drives: what a reconciling\n // consumer needs to know is \"has this channel been connected\", not \"is this\n // particular `EventSource` object open right now\".\n let connected = false;\n\n const deliver = (raw: string): void => {\n // A frame this build cannot read is dropped rather than thrown: `onmessage`\n // runs inside the browser's event loop, where a throw reaches no caller and\n // would take the rest of this delivery's listeners with it.\n const parsed = Effect.runSync(\n Effect.either(\n Effect.flatMap(\n Effect.try(() => JSON.parse(raw) as unknown),\n readEventEnvelope<EntityChangeEvent>,\n ),\n ),\n );\n if (Either.isLeft(parsed)) {\n return;\n }\n\n for (const listener of listeners) {\n listener(parsed.right);\n }\n };\n\n const open = (): void => {\n // Guarded because a server-rendered pass has no `EventSource` at all, and a\n // channel built at module scope must not throw there.\n if (source !== undefined || typeof EventSource === 'undefined') {\n return;\n }\n source = new EventSource(url);\n source.onmessage = message => {\n deliver(message.data as string);\n };\n // Fires on the first open *and* on every browser-driven reconnect, because\n // `EventSource` reconnects the same object rather than handing back a new\n // one — so nothing has to re-arm this.\n source.onopen = () => {\n connected = true;\n for (const listener of connectListeners) {\n listener();\n }\n };\n };\n\n const close = (): void => {\n source?.close();\n source = undefined;\n connected = false;\n };\n\n return {\n subscribe(listener) {\n listeners.add(listener);\n open();\n return () => {\n listeners.delete(listener);\n if (listeners.size === 0) {\n close();\n }\n };\n },\n onConnect(listener) {\n connectListeners.add(listener);\n // ⚠️ The replay, and the reason this signal works at all. Registering does\n // *not* open the connection — subscribing is what does that — so a\n // consumer arriving after another one opened the stream would otherwise\n // wait for a network drop that may never come, and a restored pending set\n // would never be reconciled.\n if (connected) listener();\n return () => {\n connectListeners.delete(listener);\n };\n },\n };\n}\n"],"names":["readEventEnvelope","Effect","Either","makeEventSourceReactiveChannel","url","listeners","Set","connectListeners","source","connected","deliver","raw","parsed","runSync","either","flatMap","try","JSON","parse","isLeft","listener","right","open","undefined","EventSource","onmessage","message","data","onopen","close","subscribe","add","delete","size","onConnect"],"mappings":"AAAA,SAAiCA,iBAAiB,QAAQ,gBAAgB;AAC1E,SAASC,MAAM,EAAEC,MAAM,QAAQ,SAAS;AAOxC;;;;;;;;;;;;;;;;;;CAkBC,GACD,OAAO,SAASC,+BAA+BC,GAAW;IACxD,MAAMC,YAAY,IAAIC;IACtB,MAAMC,mBAAmB,IAAID;IAC7B,IAAIE;IACJ,yEAAyE;IACzE,yEAAyE;IACzE,4EAA4E;IAC5E,mDAAmD;IACnD,IAAIC,YAAY;IAEhB,MAAMC,UAAU,CAACC;QACf,4EAA4E;QAC5E,4EAA4E;QAC5E,4DAA4D;QAC5D,MAAMC,SAASX,OAAOY,OAAO,CAC3BZ,OAAOa,MAAM,CACXb,OAAOc,OAAO,CACZd,OAAOe,GAAG,CAAC,IAAMC,KAAKC,KAAK,CAACP,OAC5BX;QAIN,IAAIE,OAAOiB,MAAM,CAACP,SAAS;YACzB;QACF;QAEA,KAAK,MAAMQ,YAAYf,UAAW;YAChCe,SAASR,OAAOS,KAAK;QACvB;IACF;IAEA,MAAMC,OAAO;QACX,4EAA4E;QAC5E,sDAAsD;QACtD,IAAId,WAAWe,aAAa,OAAOC,gBAAgB,aAAa;YAC9D;QACF;QACAhB,SAAS,IAAIgB,YAAYpB;QACzBI,OAAOiB,SAAS,GAAGC,CAAAA;YACjBhB,QAAQgB,QAAQC,IAAI;QACtB;QACA,2EAA2E;QAC3E,0EAA0E;QAC1E,uCAAuC;QACvCnB,OAAOoB,MAAM,GAAG;YACdnB,YAAY;YACZ,KAAK,MAAMW,YAAYb,iBAAkB;gBACvCa;YACF;QACF;IACF;IAEA,MAAMS,QAAQ;QACZrB,0BAAAA,OAAQqB,KAAK;QACbrB,SAASe;QACTd,YAAY;IACd;IAEA,OAAO;QACLqB,WAAUV,QAAQ;YAChBf,UAAU0B,GAAG,CAACX;YACdE;YACA,OAAO;gBACLjB,UAAU2B,MAAM,CAACZ;gBACjB,IAAIf,UAAU4B,IAAI,KAAK,GAAG;oBACxBJ;gBACF;YACF;QACF;QACAK,WAAUd,QAAQ;YAChBb,iBAAiBwB,GAAG,CAACX;YACrB,2EAA2E;YAC3E,mEAAmE;YACnE,wEAAwE;YACxE,0EAA0E;YAC1E,6BAA6B;YAC7B,IAAIX,WAAWW;YACf,OAAO;gBACLb,iBAAiByB,MAAM,CAACZ;YAC1B;QACF;IACF;AACF"}
@@ -0,0 +1,75 @@
1
+ import type { DomainEvent, EntityChangeEvent } from '@entifix/core';
2
+ import { Context } from 'effect';
3
+ export type { EntityChangeEvent };
4
+ /**
5
+ * A listener on the reactive stream.
6
+ *
7
+ * It receives the whole message, not just the payload: the transaction id, the
8
+ * emission time and the sequence a consumer needs are `meta.event.correlationId`,
9
+ * `.at` and `.id`, and duplicating them onto {@link EntityChangeEvent} is how
10
+ * two sources of one fact drift (ADR 0036). `#137` settles an optimistic write
11
+ * on the correlation id; `useReactiveInvalidation` reads only `data.entity`.
12
+ */
13
+ export type EntityChangeListener = (event: DomainEvent<EntityChangeEvent>) => void;
14
+ /**
15
+ * The reactive-updates port. Framework-free on purpose (like the OTel tooling):
16
+ * the transport is swappable, and the React side wires `subscribe` to the query
17
+ * client. `subscribe` returns its own unsubscribe.
18
+ */
19
+ export interface ReactiveChannel {
20
+ subscribe(listener: EntityChangeListener): () => void;
21
+ /**
22
+ * Runs whenever the stream is (re)connected — and **immediately if it is
23
+ * already connected when the listener registers**.
24
+ *
25
+ * The stream does not replay, by design (ADR 0036), so a consumer that missed
26
+ * an outcome while disconnected has to go and ask. This is the signal that
27
+ * tells it when to: `#137` re-queries every pending transaction id here.
28
+ *
29
+ * ⚠️ **The replay is the mechanism, not a convenience.** The channel is
30
+ * refcounted and opened by its *first* subscriber, and
31
+ * `useReactiveInvalidation` typically is that subscriber. A consumer mounting
32
+ * after it — which depends on nothing more than component-tree ordering —
33
+ * would otherwise register for an open that had already happened, and
34
+ * reconcile nothing until the next real network drop. On a page load with no
35
+ * disconnect, a pending set restored from the previous session would never be
36
+ * reconciled at all, which is the case this exists for.
37
+ *
38
+ * Required rather than optional: an optional member on a port is how one
39
+ * implementation quietly stops honouring it while every consumer compiles.
40
+ */
41
+ onConnect(listener: () => void): () => void;
42
+ }
43
+ declare const ReactiveChannelTag_base: Context.TagClass<ReactiveChannelTag, "ReactiveChannel", ReactiveChannel>;
44
+ export declare class ReactiveChannelTag extends ReactiveChannelTag_base {
45
+ }
46
+ /**
47
+ * A channel that never emits — the default until a transport is provided.
48
+ *
49
+ * It never connects either, so `onConnect` registers nothing: a consumer of this
50
+ * channel reconciles never, which is the honest behaviour for a transport that
51
+ * is not there.
52
+ */
53
+ export declare const NoopReactiveChannel: ReactiveChannel;
54
+ export interface InMemoryReactiveChannel extends ReactiveChannel {
55
+ /** Push an event to every current subscriber (drives tests and the mock). */
56
+ emit(event: DomainEvent<EntityChangeEvent>): void;
57
+ /**
58
+ * Announce a (re)connection, so a spec can drive the reconciliation path.
59
+ *
60
+ * Also flips this channel to "connected", which is what makes a listener
61
+ * registered afterwards fire immediately — the behaviour the real transport
62
+ * has and the one most likely to be got wrong.
63
+ */
64
+ connect(): void;
65
+ }
66
+ /**
67
+ * A synchronous in-memory channel: `emit` fans out to every live subscriber.
68
+ *
69
+ * It backs specs. It is no longer what the workspace runs on — that is
70
+ * `makeEventSourceReactiveChannel`, and until ADR 0036 landed a transport this
71
+ * one was mounted in its place, emitting nothing, so a reader of the workspace
72
+ * could not tell "the transport is missing" from "nothing changed".
73
+ */
74
+ export declare function makeInMemoryReactiveChannel(): InMemoryReactiveChannel;
75
+ //# sourceMappingURL=reactive-channel.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reactive-channel.d.ts","sourceRoot":"","sources":["../../src/reactive/reactive-channel.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AACpE,OAAO,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AAEjC,YAAY,EAAE,iBAAiB,EAAE,CAAC;AAElC;;;;;;;;GAQG;AACH,MAAM,MAAM,oBAAoB,GAAG,CACjC,KAAK,EAAE,WAAW,CAAC,iBAAiB,CAAC,KAClC,IAAI,CAAC;AAEV;;;;GAIG;AACH,MAAM,WAAW,eAAe;IAC9B,SAAS,CAAC,QAAQ,EAAE,oBAAoB,GAAG,MAAM,IAAI,CAAC;IACtD;;;;;;;;;;;;;;;;;;;OAmBG;IACH,SAAS,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC;CAC7C;;AAED,qBAAa,kBAAmB,SAAQ,uBAGrC;CAAG;AAEN;;;;;;GAMG;AACH,eAAO,MAAM,mBAAmB,EAAE,eAGjC,CAAC;AAEF,MAAM,WAAW,uBAAwB,SAAQ,eAAe;IAC9D,6EAA6E;IAC7E,IAAI,CAAC,KAAK,EAAE,WAAW,CAAC,iBAAiB,CAAC,GAAG,IAAI,CAAC;IAClD;;;;;;OAMG;IACH,OAAO,IAAI,IAAI,CAAC;CACjB;AAED;;;;;;;GAOG;AACH,wBAAgB,2BAA2B,IAAI,uBAAuB,CAgCrE"}