@kozou/svelte-ui 1.3.0 → 1.4.1

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 (161) hide show
  1. package/build/client/_app/immutable/assets/0.BpzpdRYx.css +2 -0
  2. package/build/client/_app/immutable/assets/0.BpzpdRYx.css.br +0 -0
  3. package/build/client/_app/immutable/assets/0.BpzpdRYx.css.gz +0 -0
  4. package/build/client/_app/immutable/chunks/B18MYhkI.js +4 -0
  5. package/build/client/_app/immutable/chunks/B18MYhkI.js.br +0 -0
  6. package/build/client/_app/immutable/chunks/B18MYhkI.js.gz +0 -0
  7. package/build/client/_app/immutable/chunks/{DxikiVqj.js → BAfs2EwQ.js} +1 -1
  8. package/build/client/_app/immutable/chunks/BAfs2EwQ.js.br +0 -0
  9. package/build/client/_app/immutable/chunks/BAfs2EwQ.js.gz +0 -0
  10. package/build/client/_app/immutable/chunks/CdqGzY-s.js +1 -0
  11. package/build/client/_app/immutable/chunks/CdqGzY-s.js.br +0 -0
  12. package/build/client/_app/immutable/chunks/CdqGzY-s.js.gz +0 -0
  13. package/build/client/_app/immutable/chunks/{BgT5WiOF.js → ClT4djUO.js} +1 -1
  14. package/build/client/_app/immutable/chunks/ClT4djUO.js.br +0 -0
  15. package/build/client/_app/immutable/chunks/{BgT5WiOF.js.gz → ClT4djUO.js.gz} +0 -0
  16. package/build/client/_app/immutable/chunks/D-3Io3Xp.js +214 -0
  17. package/build/client/_app/immutable/chunks/D-3Io3Xp.js.br +0 -0
  18. package/build/client/_app/immutable/chunks/D-3Io3Xp.js.gz +0 -0
  19. package/build/client/_app/immutable/chunks/{BLwQsTD2.js → PpzXWueh.js} +1 -1
  20. package/build/client/_app/immutable/chunks/PpzXWueh.js.br +0 -0
  21. package/build/client/_app/immutable/chunks/PpzXWueh.js.gz +0 -0
  22. package/build/client/_app/immutable/entry/app.hTXrPHEe.js +2 -0
  23. package/build/client/_app/immutable/entry/app.hTXrPHEe.js.br +0 -0
  24. package/build/client/_app/immutable/entry/app.hTXrPHEe.js.gz +0 -0
  25. package/build/client/_app/immutable/entry/start.DRJ6mn95.js +1 -0
  26. package/build/client/_app/immutable/entry/start.DRJ6mn95.js.br +0 -0
  27. package/build/client/_app/immutable/entry/start.DRJ6mn95.js.gz +0 -0
  28. package/build/client/_app/immutable/nodes/{0.BTi1ueXu.js → 0.ahUg4RPH.js} +1 -1
  29. package/build/client/_app/immutable/nodes/0.ahUg4RPH.js.br +0 -0
  30. package/build/client/_app/immutable/nodes/0.ahUg4RPH.js.gz +0 -0
  31. package/build/client/_app/immutable/nodes/{1.D09foNRb.js → 1.D-eXQsNF.js} +1 -1
  32. package/build/client/_app/immutable/nodes/1.D-eXQsNF.js.br +1 -0
  33. package/build/client/_app/immutable/nodes/1.D-eXQsNF.js.gz +0 -0
  34. package/build/client/_app/immutable/nodes/2.DXYbQoLe.js +1 -0
  35. package/build/client/_app/immutable/nodes/2.DXYbQoLe.js.br +0 -0
  36. package/build/client/_app/immutable/nodes/2.DXYbQoLe.js.gz +0 -0
  37. package/build/client/_app/immutable/nodes/3.nVf6pSGl.js +1 -0
  38. package/build/client/_app/immutable/nodes/3.nVf6pSGl.js.br +0 -0
  39. package/build/client/_app/immutable/nodes/3.nVf6pSGl.js.gz +0 -0
  40. package/build/client/_app/immutable/nodes/{3.BfuWEZdX.js → 4.C486-zEy.js} +1 -1
  41. package/build/client/_app/immutable/nodes/4.C486-zEy.js.br +2 -0
  42. package/build/client/_app/immutable/nodes/4.C486-zEy.js.gz +0 -0
  43. package/build/client/_app/immutable/nodes/{4.5H5WIc-o.js → 5.A-_8tbYC.js} +1 -1
  44. package/build/client/_app/immutable/nodes/5.A-_8tbYC.js.br +0 -0
  45. package/build/client/_app/immutable/nodes/5.A-_8tbYC.js.gz +0 -0
  46. package/build/client/_app/immutable/nodes/6.CzngNnZ1.js +1 -0
  47. package/build/client/_app/immutable/nodes/6.CzngNnZ1.js.br +0 -0
  48. package/build/client/_app/immutable/nodes/6.CzngNnZ1.js.gz +0 -0
  49. package/build/client/_app/immutable/nodes/7.M4F1VOf8.js +1 -0
  50. package/build/client/_app/immutable/nodes/7.M4F1VOf8.js.br +0 -0
  51. package/build/client/_app/immutable/nodes/7.M4F1VOf8.js.gz +0 -0
  52. package/build/client/_app/immutable/nodes/{7.CjEmXoAF.js → 8.Cb70WGbk.js} +1 -1
  53. package/build/client/_app/immutable/nodes/8.Cb70WGbk.js.br +1 -0
  54. package/build/client/_app/immutable/nodes/8.Cb70WGbk.js.gz +0 -0
  55. package/build/client/_app/version.json +1 -1
  56. package/build/client/_app/version.json.br +0 -0
  57. package/build/client/_app/version.json.gz +0 -0
  58. package/build/server/chunks/{0-C4CgvBlc.js → 0-CTaiiMQp.js} +3 -3
  59. package/build/server/chunks/{0-C4CgvBlc.js.map → 0-CTaiiMQp.js.map} +1 -1
  60. package/build/server/chunks/{1-BvxsIwwy.js → 1-HvpuDSr9.js} +3 -3
  61. package/build/server/chunks/{1-BvxsIwwy.js.map → 1-HvpuDSr9.js.map} +1 -1
  62. package/build/server/chunks/{2-D9il_3ty.js → 2-DWDp9L0J.js} +19 -5
  63. package/build/server/chunks/2-DWDp9L0J.js.map +1 -0
  64. package/build/server/chunks/3-D3TOYjW7.js +144 -0
  65. package/build/server/chunks/3-D3TOYjW7.js.map +1 -0
  66. package/build/server/chunks/{3-DfrqlU53.js → 4-DfL_8ypg.js} +5 -5
  67. package/build/server/chunks/{3-DfrqlU53.js.map → 4-DfL_8ypg.js.map} +1 -1
  68. package/build/server/chunks/{4-1chJCfPL.js → 5-PdIKGOry.js} +6 -6
  69. package/build/server/chunks/{4-1chJCfPL.js.map → 5-PdIKGOry.js.map} +1 -1
  70. package/build/server/chunks/{5-DUCxshKE.js → 6-DTP2dgQ4.js} +11 -10
  71. package/build/server/chunks/6-DTP2dgQ4.js.map +1 -0
  72. package/build/server/chunks/{6-Dh7Tg-XB.js → 7-BO2wqZFZ.js} +11 -10
  73. package/build/server/chunks/7-BO2wqZFZ.js.map +1 -0
  74. package/build/server/chunks/{7-CKkRKfs8.js → 8-Q9JasCCv.js} +5 -5
  75. package/build/server/chunks/{7-CKkRKfs8.js.map → 8-Q9JasCCv.js.map} +1 -1
  76. package/build/server/chunks/{_page.svelte-DaJwoT9F.js → _page.svelte-BRJFxYD9.js} +3 -3
  77. package/build/server/chunks/{_page.svelte-DaJwoT9F.js.map → _page.svelte-BRJFxYD9.js.map} +1 -1
  78. package/build/server/chunks/{_page.svelte-DI_FY3zW.js → _page.svelte-ByJgj70z.js} +7 -6
  79. package/build/server/chunks/_page.svelte-ByJgj70z.js.map +1 -0
  80. package/build/server/chunks/{_page.svelte-BN1au56r.js → _page.svelte-Cup_HkLy.js} +19 -3
  81. package/build/server/chunks/_page.svelte-Cup_HkLy.js.map +1 -0
  82. package/build/server/chunks/_page.svelte-D5OPNrD6.js +143 -0
  83. package/build/server/chunks/_page.svelte-D5OPNrD6.js.map +1 -0
  84. package/build/server/chunks/{_page.svelte-CBUJNhG1.js → _page.svelte-DLZjvC2B.js} +7 -6
  85. package/build/server/chunks/_page.svelte-DLZjvC2B.js.map +1 -0
  86. package/build/server/chunks/{_server.ts-C-Wy675T.js → _server.ts-Bl2Id2sq.js} +4 -4
  87. package/build/server/chunks/{_server.ts-C-Wy675T.js.map → _server.ts-Bl2Id2sq.js.map} +1 -1
  88. package/build/server/chunks/{adapter-CXNsjV1V.js → adapter-D185EFin.js} +14 -2
  89. package/build/server/chunks/{adapter-CXNsjV1V.js.map → adapter-D185EFin.js.map} +1 -1
  90. package/build/server/chunks/{client-C004jkBy.js → client-DEXFQakh.js} +2 -2
  91. package/build/server/chunks/{client-C004jkBy.js.map → client-DEXFQakh.js.map} +1 -1
  92. package/build/server/chunks/{composite-form-_tg7iknp.js → composite-form-BXkkWWG1.js} +60 -21
  93. package/build/server/chunks/composite-form-BXkkWWG1.js.map +1 -0
  94. package/build/server/chunks/{error.svelte-u8Vsq8vF.js → error.svelte-5yVrLvBD.js} +3 -3
  95. package/build/server/chunks/{error.svelte-u8Vsq8vF.js.map → error.svelte-5yVrLvBD.js.map} +1 -1
  96. package/build/server/chunks/{hooks.server-DtCXcOVD.js → hooks.server-DSFHJrhe.js} +636 -12
  97. package/build/server/chunks/hooks.server-DSFHJrhe.js.map +1 -0
  98. package/build/server/chunks/{index-5kYmxIr9.js → index-CVekYGHP.js} +2 -2
  99. package/build/server/chunks/{index-5kYmxIr9.js.map → index-CVekYGHP.js.map} +1 -1
  100. package/build/server/chunks/{internal-fxcuSPWe.js → internal-B5RB0ss0.js} +3 -3
  101. package/build/server/chunks/{internal-fxcuSPWe.js.map → internal-B5RB0ss0.js.map} +1 -1
  102. package/build/server/chunks/privilege-readonly-CK4k2aYO.js +15 -0
  103. package/build/server/chunks/privilege-readonly-CK4k2aYO.js.map +1 -0
  104. package/build/server/chunks/{relation-options-Dzy9DaQG.js → relation-options-BQ3XnftO.js} +2 -2
  105. package/build/server/chunks/{relation-options-Dzy9DaQG.js.map → relation-options-BQ3XnftO.js.map} +1 -1
  106. package/build/server/chunks/relation-select-composite-field-DZuvCpmt.js +101 -0
  107. package/build/server/chunks/relation-select-composite-field-DZuvCpmt.js.map +1 -0
  108. package/build/server/chunks/{widget-registry-BcgMXjkA.js → widget-registry-6ku8orXa.js} +3 -98
  109. package/build/server/chunks/widget-registry-6ku8orXa.js.map +1 -0
  110. package/build/server/index.js +2 -2
  111. package/build/server/manifest.js +23 -15
  112. package/build/server/manifest.js.map +1 -1
  113. package/package.json +4 -4
  114. package/build/client/_app/immutable/assets/0._vgqT-Ja.css +0 -2
  115. package/build/client/_app/immutable/assets/0._vgqT-Ja.css.br +0 -0
  116. package/build/client/_app/immutable/assets/0._vgqT-Ja.css.gz +0 -0
  117. package/build/client/_app/immutable/chunks/BLwQsTD2.js.br +0 -0
  118. package/build/client/_app/immutable/chunks/BLwQsTD2.js.gz +0 -0
  119. package/build/client/_app/immutable/chunks/BgT5WiOF.js.br +0 -0
  120. package/build/client/_app/immutable/chunks/DY0oakY5.js +0 -214
  121. package/build/client/_app/immutable/chunks/DY0oakY5.js.br +0 -0
  122. package/build/client/_app/immutable/chunks/DY0oakY5.js.gz +0 -0
  123. package/build/client/_app/immutable/chunks/DxikiVqj.js.br +0 -0
  124. package/build/client/_app/immutable/chunks/DxikiVqj.js.gz +0 -0
  125. package/build/client/_app/immutable/chunks/rPPoD7Sp.js +0 -4
  126. package/build/client/_app/immutable/chunks/rPPoD7Sp.js.br +0 -0
  127. package/build/client/_app/immutable/chunks/rPPoD7Sp.js.gz +0 -0
  128. package/build/client/_app/immutable/entry/app.G2Y8IfhN.js +0 -2
  129. package/build/client/_app/immutable/entry/app.G2Y8IfhN.js.br +0 -0
  130. package/build/client/_app/immutable/entry/app.G2Y8IfhN.js.gz +0 -0
  131. package/build/client/_app/immutable/entry/start.sPBaDl_M.js +0 -1
  132. package/build/client/_app/immutable/entry/start.sPBaDl_M.js.br +0 -1
  133. package/build/client/_app/immutable/entry/start.sPBaDl_M.js.gz +0 -0
  134. package/build/client/_app/immutable/nodes/0.BTi1ueXu.js.br +0 -0
  135. package/build/client/_app/immutable/nodes/0.BTi1ueXu.js.gz +0 -0
  136. package/build/client/_app/immutable/nodes/1.D09foNRb.js.br +0 -2
  137. package/build/client/_app/immutable/nodes/1.D09foNRb.js.gz +0 -0
  138. package/build/client/_app/immutable/nodes/2.CWOXCYvQ.js +0 -1
  139. package/build/client/_app/immutable/nodes/2.CWOXCYvQ.js.br +0 -0
  140. package/build/client/_app/immutable/nodes/2.CWOXCYvQ.js.gz +0 -0
  141. package/build/client/_app/immutable/nodes/3.BfuWEZdX.js.br +0 -0
  142. package/build/client/_app/immutable/nodes/3.BfuWEZdX.js.gz +0 -0
  143. package/build/client/_app/immutable/nodes/4.5H5WIc-o.js.br +0 -0
  144. package/build/client/_app/immutable/nodes/4.5H5WIc-o.js.gz +0 -0
  145. package/build/client/_app/immutable/nodes/5.BR2iBXwV.js +0 -1
  146. package/build/client/_app/immutable/nodes/5.BR2iBXwV.js.br +0 -0
  147. package/build/client/_app/immutable/nodes/5.BR2iBXwV.js.gz +0 -0
  148. package/build/client/_app/immutable/nodes/6.ChubZePh.js +0 -1
  149. package/build/client/_app/immutable/nodes/6.ChubZePh.js.br +0 -0
  150. package/build/client/_app/immutable/nodes/6.ChubZePh.js.gz +0 -0
  151. package/build/client/_app/immutable/nodes/7.CjEmXoAF.js.br +0 -1
  152. package/build/client/_app/immutable/nodes/7.CjEmXoAF.js.gz +0 -0
  153. package/build/server/chunks/2-D9il_3ty.js.map +0 -1
  154. package/build/server/chunks/5-DUCxshKE.js.map +0 -1
  155. package/build/server/chunks/6-Dh7Tg-XB.js.map +0 -1
  156. package/build/server/chunks/_page.svelte-BN1au56r.js.map +0 -1
  157. package/build/server/chunks/_page.svelte-CBUJNhG1.js.map +0 -1
  158. package/build/server/chunks/_page.svelte-DI_FY3zW.js.map +0 -1
  159. package/build/server/chunks/composite-form-_tg7iknp.js.map +0 -1
  160. package/build/server/chunks/hooks.server-DtCXcOVD.js.map +0 -1
  161. package/build/server/chunks/widget-registry-BcgMXjkA.js.map +0 -1
@@ -1,4 +1,4 @@
1
- import { _ as __commonJSMin, b as __require } from './internal-fxcuSPWe.js';
1
+ import { _ as __commonJSMin, b as __require } from './internal-B5RB0ss0.js';
2
2
  import { e as encodeResourceId } from './resource-id-PDcQeAnc.js';
3
3
  import { z } from 'zod';
4
4
  import require$$0$3 from 'events';
@@ -6478,7 +6478,9 @@ var KNOWN_TAGS = new Set([
6478
6478
  "ai",
6479
6479
  "widget",
6480
6480
  "policy",
6481
- "example"
6481
+ "example",
6482
+ "expose",
6483
+ "arg"
6482
6484
  ]);
6483
6485
  var TAG_RE = /^\s*@([a-zA-Z_][a-zA-Z0-9_]*)\s*:(.*)$/;
6484
6486
  var INDENT_RE = /^[ \t]/;
@@ -6516,13 +6518,66 @@ function warnMidlineTag(token) {
6516
6518
  function isWidgetType(value) {
6517
6519
  return KNOWN_WIDGETS.has(value);
6518
6520
  }
6521
+ /** Index of the first whitespace character, or -1. Manual scan (no regex) to
6522
+ * stay off CodeQL's polynomial-ReDoS radar, like the rest of this module. */
6523
+ function firstWhitespaceIndex(s) {
6524
+ for (let i = 0; i < s.length; i += 1) if (LINE_WS_RE.test(s[i])) return i;
6525
+ return -1;
6526
+ }
6527
+ /** Extract `<inner>` from a `<name>(<inner>)` directive token (trimmed), or
6528
+ * null when the token is not exactly that shape. Manual scan, no regex. */
6529
+ function parenDirective(directive, name) {
6530
+ const prefix = `${name}(`;
6531
+ if (!directive.startsWith(prefix) || !directive.endsWith(")")) return null;
6532
+ return directive.slice(prefix.length, -1).trim();
6533
+ }
6534
+ /** Parse an `@arg:` value — "<argname> relation(<ref>)" or
6535
+ * "<argname> widget(<type>)". Returns null on any malformed shape so the
6536
+ * caller can warn (no silent drop). `<ref>` is `table.col` (schema defaulted
6537
+ * later) or `schema.table.col`. */
6538
+ function parseArgHint(value) {
6539
+ const ws = firstWhitespaceIndex(value);
6540
+ if (ws === -1) return null;
6541
+ const name = value.slice(0, ws);
6542
+ const directive = value.slice(ws + 1).trim();
6543
+ if (name === "" || directive === "") return null;
6544
+ const rel = parenDirective(directive, "relation");
6545
+ if (rel !== null) {
6546
+ const parts = rel.split(".").map((p) => p.trim());
6547
+ if (parts.length === 2 && parts.every((p) => p !== "")) return {
6548
+ name,
6549
+ relation: {
6550
+ schema: null,
6551
+ table: parts[0],
6552
+ column: parts[1]
6553
+ }
6554
+ };
6555
+ if (parts.length === 3 && parts.every((p) => p !== "")) return {
6556
+ name,
6557
+ relation: {
6558
+ schema: parts[0],
6559
+ table: parts[1],
6560
+ column: parts[2]
6561
+ }
6562
+ };
6563
+ return null;
6564
+ }
6565
+ const wid = parenDirective(directive, "widget");
6566
+ if (wid !== null) return isWidgetType(wid) ? {
6567
+ name,
6568
+ widget: wid
6569
+ } : null;
6570
+ return null;
6571
+ }
6519
6572
  function parseCommentTags(comment) {
6520
6573
  const result = {
6521
6574
  body: "",
6522
6575
  ai: [],
6523
6576
  widget: null,
6524
6577
  policy: [],
6525
- examples: []
6578
+ examples: [],
6579
+ expose: "none",
6580
+ args: []
6526
6581
  };
6527
6582
  if (comment === null || comment === "") return result;
6528
6583
  const lines = comment.split("\n");
@@ -6598,6 +6653,22 @@ function parseCommentTags(comment) {
6598
6653
  };
6599
6654
  continue;
6600
6655
  }
6656
+ if (tag === "expose") {
6657
+ const tokens = value.toLowerCase().split(/\s+/).filter((t) => t !== "");
6658
+ if (tokens.length === 1 && tokens[0] === "rpc") result.expose = "rpc";
6659
+ else if (tokens.length === 2 && tokens[0] === "rpc" && tokens[1] === "public") result.expose = "rpc-public";
6660
+ else {
6661
+ result.expose = "none";
6662
+ console.warn(`[@kozou/core] parseCommentTags: invalid @expose value "${value}" (expected "rpc" or "rpc public"; not exposed)`);
6663
+ }
6664
+ continue;
6665
+ }
6666
+ if (tag === "arg") {
6667
+ const hint = parseArgHint(value);
6668
+ if (hint !== null) result.args.push(hint);
6669
+ else console.warn(`[@kozou/core] parseCommentTags: invalid @arg value "${value}" (expected "<name> relation(<table.col>)" or "<name> widget(<type>)"; skip)`);
6670
+ continue;
6671
+ }
6601
6672
  if (!KNOWN_TAGS.has(tag)) {
6602
6673
  console.warn(`[@kozou/core] parseCommentTags: unknown tag "@${tag}" (forward compat: kept in body)`);
6603
6674
  bodyLines.push(line);
@@ -6663,6 +6734,252 @@ function inferWidget(input) {
6663
6734
  return "text";
6664
6735
  }
6665
6736
  //#endregion
6737
+ //#region ../core/dist/buildFunctionContext.js
6738
+ var POLYMORPHIC_TYPES = new Set([
6739
+ "anyelement",
6740
+ "anyarray",
6741
+ "anynonarray",
6742
+ "anyenum",
6743
+ "anyrange",
6744
+ "anymultirange",
6745
+ "anycompatible",
6746
+ "anycompatiblearray",
6747
+ "anycompatiblenonarray",
6748
+ "anycompatiblerange",
6749
+ "anycompatiblemultirange"
6750
+ ]);
6751
+ /** True for an argument that is part of the call's input (so it appears in the
6752
+ * named-args body). OUT (`out`) and RETURNS TABLE columns (`table`) are
6753
+ * return-side and excluded. */
6754
+ function isInputArg(arg) {
6755
+ return arg.mode === "in" || arg.mode === "inout" || arg.mode === "variadic";
6756
+ }
6757
+ /** Validate an allowlist: every entry must be a schema-qualified name of
6758
+ * exactly the form `schema.function`, with both parts non-empty and no extra
6759
+ * dot. A bare name is ambiguous (which schema?); an entry with two+
6760
+ * dots is ambiguous against a quoted identifier that itself contains a dot, so
6761
+ * it could authorize the wrong function — both are dropped with a build issue
6762
+ * rather than matched loosely (fail-closed for the definer / PUBLIC gates).
6763
+ * Returns the set of valid entries. */
6764
+ function normalizeAllowlist(entries, configKey, issues) {
6765
+ const set = /* @__PURE__ */ new Set();
6766
+ if (entries === void 0) return set;
6767
+ for (const entry of entries) {
6768
+ const parts = entry.split(".");
6769
+ if (parts.length !== 2 || parts[0] === "" || parts[1] === "") {
6770
+ issues.push({
6771
+ path: `rpc.${configKey}`,
6772
+ message: `"${entry}" is not a schema-qualified function name (expected exactly "schema.function"); ignored.`
6773
+ });
6774
+ continue;
6775
+ }
6776
+ set.add(entry);
6777
+ }
6778
+ return set;
6779
+ }
6780
+ /** The owner-relative safe-search_path predicate for `security definer`
6781
+ * functions. A definer function runs as its owner, so an
6782
+ * unqualified name resolved through a schema that someone else can write to
6783
+ * can be hijacked. Safe requires: a declared search_path; `pg_temp` present
6784
+ * exactly once and last (else the temp schema is searched implicitly first);
6785
+ * and every other element a schema only the owner may CREATE in. Anything
6786
+ * unresolvable is unsafe (fail-closed). */
6787
+ function checkSafeSearchPath(fn) {
6788
+ const sp = fn.searchPath;
6789
+ if (sp === null || sp.length === 0) return {
6790
+ safe: false,
6791
+ reason: "no SET search_path is declared"
6792
+ };
6793
+ const tempCount = sp.filter((e) => e.isTemp).length;
6794
+ if (tempCount === 0) return {
6795
+ safe: false,
6796
+ reason: "pg_temp is not listed, so the session temp schema is searched implicitly first"
6797
+ };
6798
+ if (tempCount > 1 || !sp[sp.length - 1].isTemp) return {
6799
+ safe: false,
6800
+ reason: "pg_temp must appear exactly once and as the last element"
6801
+ };
6802
+ for (const el of sp) {
6803
+ if (el.isTemp) continue;
6804
+ if (el.schema === null) return {
6805
+ safe: false,
6806
+ reason: `element "${el.raw}" does not resolve to a fixed schema`
6807
+ };
6808
+ if (el.writableByOthers === null) return {
6809
+ safe: false,
6810
+ reason: `cannot determine who may CREATE in schema "${el.schema}"`
6811
+ };
6812
+ if (el.writableByOthers === true) return {
6813
+ safe: false,
6814
+ reason: `schema "${el.schema}" is writable by PUBLIC or a role other than the owner`
6815
+ };
6816
+ }
6817
+ return {
6818
+ safe: true,
6819
+ reason: ""
6820
+ };
6821
+ }
6822
+ /** Build a synthetic RawColumn so argument widget inference reuses the exact
6823
+ * column heuristics (`inferWidget`). Arguments are not foreign keys, so a
6824
+ * relation-select only comes from an `@arg` relation hint, handled in
6825
+ * `buildArg`; here we only need name + udtName for the type-based path. */
6826
+ function inferArgWidget(arg, enumValues) {
6827
+ return inferWidget({
6828
+ column: {
6829
+ name: arg.name,
6830
+ dataType: arg.typeName,
6831
+ udtName: arg.udtName,
6832
+ nullable: true,
6833
+ defaultExpr: null,
6834
+ comment: null,
6835
+ position: 0
6836
+ },
6837
+ isForeignKey: false,
6838
+ relationSelectable: false,
6839
+ enumValues,
6840
+ commentBody: ""
6841
+ });
6842
+ }
6843
+ /** ENUM members for an argument's type, matched by type name (preferring the
6844
+ * function's own schema when an enum name is reused across schemas). */
6845
+ function findEnumValues(enums, udtName, fnSchema) {
6846
+ return (enums.find((e) => e.name === udtName && e.schema === fnSchema) ?? enums.find((e) => e.name === udtName))?.values;
6847
+ }
6848
+ function buildArg(arg, parsed, fnSchema, enums) {
6849
+ const hint = parsed.args.find((h) => h.name === arg.name);
6850
+ const relation = hint?.relation ? {
6851
+ schema: hint.relation.schema ?? fnSchema,
6852
+ table: hint.relation.table,
6853
+ column: hint.relation.column
6854
+ } : void 0;
6855
+ const enumValues = findEnumValues(enums, arg.udtName, fnSchema);
6856
+ let widget;
6857
+ if (hint?.widget) widget = hint.widget;
6858
+ else if (relation) widget = "relation-select";
6859
+ else widget = inferArgWidget(arg, enumValues ?? null);
6860
+ return {
6861
+ name: arg.name,
6862
+ typeName: arg.typeName,
6863
+ hasDefault: arg.hasDefault,
6864
+ ...enumValues ? { enumValues } : {},
6865
+ ...relation ? { relation } : {},
6866
+ widget
6867
+ };
6868
+ }
6869
+ function mapReturn(r) {
6870
+ return {
6871
+ kind: r.kind === "unsupported" ? "void" : r.kind,
6872
+ typeName: r.typeName,
6873
+ ...r.columns ? { columns: r.columns.map((c) => ({
6874
+ name: c.name,
6875
+ typeName: c.typeName
6876
+ })) } : {}
6877
+ };
6878
+ }
6879
+ function decideAndBuild(input) {
6880
+ const { fn, parsed, qualifiedName, enums, allowDefiner, allowPublicExecute, issues } = input;
6881
+ const skip = (message) => {
6882
+ issues.push({
6883
+ path: `functions.${qualifiedName}`,
6884
+ message: `"${qualifiedName}" ${message}`
6885
+ });
6886
+ return null;
6887
+ };
6888
+ const inputArgs = fn.arguments.filter(isInputArg);
6889
+ for (const arg of inputArgs) {
6890
+ if (arg.mode === "variadic") return skip(`has a VARIADIC argument ("${arg.name}"), unsupported in v1; not exposed.`);
6891
+ if (arg.name === "") return skip("has an unnamed argument; the named-args RPC body requires names, so it is not exposed.");
6892
+ if (POLYMORPHIC_TYPES.has(arg.udtName)) return skip(`has a polymorphic argument ("${arg.name}" ${arg.typeName}), unsupported in v1; not exposed.`);
6893
+ }
6894
+ if (fn.returns.kind === "unsupported") return skip(`returns "${fn.returns.typeName}", an unsupported return shape in v1 (OUT/INOUT composite, record, or polymorphic); not exposed.`);
6895
+ if (fn.security === "definer") {
6896
+ if (!allowDefiner.has(qualifiedName)) return skip("is SECURITY DEFINER and tagged @expose: rpc, but is not listed in api.rpc.allowDefiner; not exposed (the operator must opt in to a privilege-bypassing endpoint).");
6897
+ const sp = checkSafeSearchPath(fn);
6898
+ if (!sp.safe) return skip(`is SECURITY DEFINER but its search_path is not owner-safe (${sp.reason}); not exposed. Declare SET search_path with owner-only schemas and a trailing pg_temp.`);
6899
+ }
6900
+ let publicCallable = false;
6901
+ if (fn.publicExecute) {
6902
+ if (!(parsed.expose === "rpc-public" || allowPublicExecute.has(qualifiedName))) return skip("still grants EXECUTE to PUBLIC (the CREATE FUNCTION default); not exposed. REVOKE EXECUTE FROM PUBLIC and GRANT it to the intended role, or declare the public endpoint intentional with @expose: rpc public / api.rpc.allowPublicExecute.");
6903
+ publicCallable = true;
6904
+ }
6905
+ const args = inputArgs.map((arg) => buildArg(arg, parsed, fn.schema, enums));
6906
+ const inputArgNames = new Set(inputArgs.map((a) => a.name));
6907
+ for (const hint of parsed.args) if (!inputArgNames.has(hint.name)) issues.push({
6908
+ path: `functions.${qualifiedName}.args.${hint.name}`,
6909
+ message: `@arg hint references argument "${hint.name}", which "${qualifiedName}" does not have; ignored.`
6910
+ });
6911
+ const body = parsed.body !== "" ? parsed.body : null;
6912
+ const label = body !== null ? body.split("\n")[0].trim() : fn.name;
6913
+ return {
6914
+ schema: fn.schema,
6915
+ name: fn.name,
6916
+ qualifiedName,
6917
+ label,
6918
+ description: body,
6919
+ aiDescription: parsed.ai.length > 0 ? parsed.ai.join("\n") : null,
6920
+ policy: parsed.policy,
6921
+ args,
6922
+ returns: mapReturn(fn.returns),
6923
+ volatility: fn.volatility,
6924
+ security: fn.security,
6925
+ publicCallable,
6926
+ rawFunction: fn
6927
+ };
6928
+ }
6929
+ /** Decide which functions are exposed as RPC actions and shape them into
6930
+ * FunctionContexts. Skipped-but-tagged functions are pushed
6931
+ * onto `issues`. Output is sorted by qualified name for stable surfaces. */
6932
+ function buildFunctionContexts(input) {
6933
+ const { functions, enums, issues } = input;
6934
+ const allowDefiner = normalizeAllowlist(input.rpc?.allowDefiner, "allowDefiner", issues);
6935
+ const allowPublicExecute = normalizeAllowlist(input.rpc?.allowPublicExecute, "allowPublicExecute", issues);
6936
+ const byQualified = /* @__PURE__ */ new Map();
6937
+ for (const fn of functions) {
6938
+ const parsed = parseCommentTags(fn.comment);
6939
+ const q = `${fn.schema}.${fn.name}`;
6940
+ const group = byQualified.get(q);
6941
+ if (group) group.push({
6942
+ fn,
6943
+ parsed
6944
+ });
6945
+ else byQualified.set(q, [{
6946
+ fn,
6947
+ parsed
6948
+ }]);
6949
+ }
6950
+ const result = [];
6951
+ for (const [qualifiedName, group] of byQualified) {
6952
+ if (!group.some((e) => e.parsed.expose !== "none")) continue;
6953
+ if (group.length > 1) {
6954
+ issues.push({
6955
+ path: `functions.${qualifiedName}`,
6956
+ message: `"${qualifiedName}" has ${group.length} overloaded definitions and at least one is tagged for RPC exposure; v1 cannot disambiguate overloads by signature, so none are exposed. Rename the overloads or keep a single definition for this name.`
6957
+ });
6958
+ continue;
6959
+ }
6960
+ const entry = group[0];
6961
+ if (entry.fn.schema.includes(".") || entry.fn.name.includes(".")) {
6962
+ issues.push({
6963
+ path: `functions.${entry.fn.schema}.${entry.fn.name}`,
6964
+ message: `"${entry.fn.schema}"."${entry.fn.name}" has a "." in its schema or name; the schema-qualified RPC identity (schema.function) would be ambiguous, so it is not exposed.`
6965
+ });
6966
+ continue;
6967
+ }
6968
+ const ctx = decideAndBuild({
6969
+ fn: entry.fn,
6970
+ parsed: entry.parsed,
6971
+ qualifiedName,
6972
+ enums,
6973
+ allowDefiner,
6974
+ allowPublicExecute,
6975
+ issues
6976
+ });
6977
+ if (ctx !== null) result.push(ctx);
6978
+ }
6979
+ result.sort((a, b) => a.qualifiedName.localeCompare(b.qualifiedName));
6980
+ return result;
6981
+ }
6982
+ //#endregion
6666
6983
  //#region ../core/dist/displayField.js
6667
6984
  var CANDIDATES = [
6668
6985
  "name",
@@ -6928,7 +7245,7 @@ function buildConcept(view) {
6928
7245
  };
6929
7246
  }
6930
7247
  async function buildSchemaContext(opts) {
6931
- const { raw, uiHints, strict = false } = opts;
7248
+ const { raw, uiHints, strict = false, rpc } = opts;
6932
7249
  const issues = [];
6933
7250
  const knownTables = new Set(raw.tables.map((t) => `${t.schema}.${t.name}`));
6934
7251
  raw.views.forEach((v) => knownTables.add(`${v.schema}.${v.name}`));
@@ -6981,6 +7298,12 @@ async function buildSchemaContext(opts) {
6981
7298
  description: null
6982
7299
  }));
6983
7300
  const concepts = visibleRawViews.map(buildConcept);
7301
+ const functions = buildFunctionContexts({
7302
+ functions: raw.functions,
7303
+ enums: raw.enums,
7304
+ rpc,
7305
+ issues
7306
+ });
6984
7307
  if (issues.length > 0) {
6985
7308
  if (strict) throw new KozouBuildError(`buildSchemaContext: ${issues.length} validation issue(s) (strict=true)`, issues);
6986
7309
  for (const issue of issues) console.warn(`[@kozou/core] ${issue.path}: ${issue.message}`);
@@ -6994,7 +7317,8 @@ async function buildSchemaContext(opts) {
6994
7317
  tables,
6995
7318
  views,
6996
7319
  enums,
6997
- concepts
7320
+ concepts,
7321
+ functions
6998
7322
  };
6999
7323
  }
7000
7324
  //#endregion
@@ -13945,7 +14269,7 @@ async function fetchEnums(client, schemas) {
13945
14269
  return (await runQuery(client, `SELECT
13946
14270
  n.nspname AS schema,
13947
14271
  t.typname AS name,
13948
- array_agg(e.enumlabel ORDER BY e.enumsortorder) AS values
14272
+ array_agg(e.enumlabel::text ORDER BY e.enumsortorder) AS values
13949
14273
  FROM pg_type t
13950
14274
  JOIN pg_namespace n ON n.oid = t.typnamespace
13951
14275
  JOIN pg_enum e ON e.enumtypid = t.oid
@@ -13959,6 +14283,294 @@ async function fetchEnums(client, schemas) {
13959
14283
  }));
13960
14284
  }
13961
14285
  //#endregion
14286
+ //#region ../introspect/dist/functions.js
14287
+ var FUNCTIONS_SQL = `
14288
+ SELECT
14289
+ n.nspname AS schema,
14290
+ p.proname AS name,
14291
+ pg_get_function_arguments(p.oid) AS argument_signature,
14292
+ p.pronargdefaults AS ndef,
14293
+ p.provolatile AS volatility,
14294
+ p.prosecdef AS security_definer,
14295
+ p.proowner AS owner_oid,
14296
+ ro.rolname AS owner_name,
14297
+ CASE
14298
+ WHEN p.proacl IS NULL THEN true
14299
+ WHEN EXISTS (
14300
+ SELECT 1 FROM aclexplode(p.proacl) ae
14301
+ WHERE ae.grantee = 0 AND ae.privilege_type = 'EXECUTE'
14302
+ ) THEN true
14303
+ ELSE false
14304
+ END AS public_execute,
14305
+ obj_description(p.oid, 'pg_proc') AS comment,
14306
+ (p.prorettype = 'pg_catalog.void'::regtype) AS returns_void,
14307
+ p.proretset AS returns_set,
14308
+ format_type(p.prorettype, NULL) AS return_type,
14309
+ -- Resolve one level of DOMAIN to its base type so a domain over a composite
14310
+ -- is not mistaken for a scalar (and a domain over a scalar stays scalar). A
14311
+ -- still-domain result (domain over domain) is left as 'd' and treated as
14312
+ -- unsupported downstream (fail-closed for that exotic nesting).
14313
+ eff.eff_typtype AS return_typtype,
14314
+ (
14315
+ SELECT json_agg(json_build_object(
14316
+ 'name', COALESCE(an.argname, ''),
14317
+ 'typeName', format_type(at.typeoid, NULL),
14318
+ 'udtName', t.typname,
14319
+ 'typeOid', at.typeoid::int,
14320
+ 'mode', COALESCE(am.mode, 'i'),
14321
+ 'ord', at.ord
14322
+ ) ORDER BY at.ord)
14323
+ FROM unnest(COALESCE(p.proallargtypes, p.proargtypes::oid[]))
14324
+ WITH ORDINALITY AS at(typeoid, ord)
14325
+ LEFT JOIN unnest(p.proargmodes) WITH ORDINALITY AS am(mode, ord) ON am.ord = at.ord
14326
+ LEFT JOIN unnest(p.proargnames) WITH ORDINALITY AS an(argname, ord) ON an.ord = at.ord
14327
+ JOIN pg_type t ON t.oid = at.typeoid
14328
+ ) AS args,
14329
+ CASE
14330
+ WHEN eff.eff_typrelid <> 0 THEN (
14331
+ SELECT json_agg(json_build_object(
14332
+ 'name', a.attname,
14333
+ 'typeName', format_type(a.atttypid, a.atttypmod),
14334
+ 'typeOid', a.atttypid::int
14335
+ ) ORDER BY a.attnum)
14336
+ FROM pg_attribute a
14337
+ WHERE a.attrelid = eff.eff_typrelid AND a.attnum > 0 AND NOT a.attisdropped
14338
+ )
14339
+ ELSE (
14340
+ SELECT json_agg(json_build_object(
14341
+ 'name', COALESCE(an.argname, ''),
14342
+ 'typeName', format_type(at.typeoid, NULL),
14343
+ 'typeOid', at.typeoid::int
14344
+ ) ORDER BY at.ord)
14345
+ FROM unnest(COALESCE(p.proallargtypes, p.proargtypes::oid[]))
14346
+ WITH ORDINALITY AS at(typeoid, ord)
14347
+ JOIN unnest(p.proargmodes) WITH ORDINALITY AS am(mode, ord) ON am.ord = at.ord
14348
+ LEFT JOIN unnest(p.proargnames) WITH ORDINALITY AS an(argname, ord) ON an.ord = at.ord
14349
+ WHERE am.mode IN ('o', 'b', 't')
14350
+ )
14351
+ END AS return_columns,
14352
+ p.proconfig AS proconfig
14353
+ FROM pg_proc p
14354
+ JOIN pg_namespace n ON n.oid = p.pronamespace
14355
+ JOIN pg_roles ro ON ro.oid = p.proowner
14356
+ JOIN pg_type rt ON rt.oid = p.prorettype
14357
+ LEFT JOIN pg_type bt ON bt.oid = rt.typbasetype
14358
+ CROSS JOIN LATERAL (
14359
+ SELECT
14360
+ CASE WHEN rt.typtype = 'd' AND bt.oid IS NOT NULL THEN bt.typtype ELSE rt.typtype END
14361
+ AS eff_typtype,
14362
+ CASE WHEN rt.typtype = 'd' AND bt.oid IS NOT NULL THEN bt.typrelid ELSE rt.typrelid END
14363
+ AS eff_typrelid
14364
+ ) eff
14365
+ WHERE p.prokind = 'f'
14366
+ AND n.nspname = ANY($1)
14367
+ ORDER BY n.nspname, p.proname`;
14368
+ var SCHEMA_WRITABILITY_SQL = `
14369
+ SELECT
14370
+ n.nspname AS schema,
14371
+ EXISTS (
14372
+ SELECT 1 FROM aclexplode(COALESCE(n.nspacl, acldefault('n', n.nspowner))) ae
14373
+ WHERE ae.privilege_type = 'CREATE' AND ae.grantee = 0
14374
+ ) AS public_create,
14375
+ COALESCE((
14376
+ SELECT array_agg(r.oid)
14377
+ FROM pg_roles r
14378
+ WHERE NOT r.rolsuper
14379
+ AND has_schema_privilege(r.oid, n.oid, 'CREATE')
14380
+ ), ARRAY[]::oid[]) AS creator_roles
14381
+ FROM pg_namespace n
14382
+ WHERE n.nspname = ANY($1)`;
14383
+ function mapVolatility(c) {
14384
+ if (c === "i") return "immutable";
14385
+ if (c === "s") return "stable";
14386
+ return "volatile";
14387
+ }
14388
+ function mapArgMode(c) {
14389
+ switch (c) {
14390
+ case "o": return "out";
14391
+ case "b": return "inout";
14392
+ case "v": return "variadic";
14393
+ case "t": return "table";
14394
+ default: return "in";
14395
+ }
14396
+ }
14397
+ function buildArgs(row) {
14398
+ const args = (row.args ?? []).slice().sort((a, b) => a.ord - b.ord).map((a) => ({
14399
+ name: a.name,
14400
+ typeName: a.typeName,
14401
+ udtName: a.udtName,
14402
+ typeOid: a.typeOid,
14403
+ mode: mapArgMode(a.mode),
14404
+ hasDefault: false
14405
+ }));
14406
+ if (row.ndef > 0) {
14407
+ const inputIdx = args.map((a, i) => ({
14408
+ a,
14409
+ i
14410
+ })).filter((e) => e.a.mode === "in" || e.a.mode === "inout" || e.a.mode === "variadic").map((e) => e.i);
14411
+ for (const i of inputIdx.slice(Math.max(0, inputIdx.length - row.ndef))) args[i].hasDefault = true;
14412
+ }
14413
+ return args;
14414
+ }
14415
+ function classifyReturn(row) {
14416
+ const typeName = row.return_type;
14417
+ const columns = row.return_columns && row.return_columns.length > 0 ? row.return_columns.map((c) => ({
14418
+ name: c.name,
14419
+ typeName: c.typeName,
14420
+ typeOid: c.typeOid
14421
+ })) : void 0;
14422
+ const isScalarType = [
14423
+ "b",
14424
+ "e",
14425
+ "r",
14426
+ "m"
14427
+ ].includes(row.return_typtype);
14428
+ if (row.returns_void) return {
14429
+ kind: "void",
14430
+ typeName,
14431
+ returnsSet: false
14432
+ };
14433
+ if (row.returns_set) {
14434
+ if (columns) return {
14435
+ kind: "setof",
14436
+ typeName,
14437
+ returnsSet: true,
14438
+ columns
14439
+ };
14440
+ if (isScalarType) return {
14441
+ kind: "setof",
14442
+ typeName,
14443
+ returnsSet: true
14444
+ };
14445
+ return {
14446
+ kind: "unsupported",
14447
+ typeName,
14448
+ returnsSet: true
14449
+ };
14450
+ }
14451
+ if (row.return_typtype === "c") return columns ? {
14452
+ kind: "composite",
14453
+ typeName,
14454
+ returnsSet: false,
14455
+ columns
14456
+ } : {
14457
+ kind: "unsupported",
14458
+ typeName,
14459
+ returnsSet: false
14460
+ };
14461
+ if (isScalarType) return {
14462
+ kind: "scalar",
14463
+ typeName,
14464
+ returnsSet: false
14465
+ };
14466
+ return {
14467
+ kind: "unsupported",
14468
+ typeName,
14469
+ returnsSet: false
14470
+ };
14471
+ }
14472
+ /** Split a SET search_path GUC value on commas that are not inside double
14473
+ * quotes, then unquote each element. Handles `"$user", public` and a quoted
14474
+ * identifier that itself contains a comma. */
14475
+ function splitSearchPath(value) {
14476
+ const elements = [];
14477
+ let current = "";
14478
+ let inQuotes = false;
14479
+ for (let i = 0; i < value.length; i += 1) {
14480
+ const ch = value[i];
14481
+ if (ch === "\"") if (inQuotes && value[i + 1] === "\"") {
14482
+ current += "\"";
14483
+ i += 1;
14484
+ } else inQuotes = !inQuotes;
14485
+ else if (ch === "," && !inQuotes) {
14486
+ elements.push(current.trim());
14487
+ current = "";
14488
+ } else current += ch;
14489
+ }
14490
+ elements.push(current.trim());
14491
+ return elements.filter((e) => e !== "");
14492
+ }
14493
+ /** Parse the proconfig `search_path` setting into elements, or null when the
14494
+ * function declares no SET search_path. Each element records whether it is
14495
+ * pg_temp and (for a fixed schema) is resolved to a schema name; a dynamic
14496
+ * element (`$user`) resolves to null. Writability is filled in afterwards. */
14497
+ function parseSearchPath(proconfig) {
14498
+ if (proconfig === null) return null;
14499
+ const entry = proconfig.find((c) => c.toLowerCase().startsWith("search_path="));
14500
+ if (entry === void 0) return null;
14501
+ return splitSearchPath(entry.slice(entry.indexOf("=") + 1)).map((raw) => {
14502
+ if (raw === "pg_temp") return {
14503
+ raw,
14504
+ schema: null,
14505
+ writableByOthers: null,
14506
+ isTemp: true
14507
+ };
14508
+ return {
14509
+ raw,
14510
+ schema: raw.startsWith("$") ? null : raw,
14511
+ writableByOthers: null,
14512
+ isTemp: false
14513
+ };
14514
+ });
14515
+ }
14516
+ /** Fetch CREATE-writability info for the given schema names. */
14517
+ async function fetchSchemaWritability(client, schemaNames) {
14518
+ const map = /* @__PURE__ */ new Map();
14519
+ if (schemaNames.length === 0) return map;
14520
+ const rows = await runQuery(client, SCHEMA_WRITABILITY_SQL, [schemaNames], "fetchFunctions (schema writability)");
14521
+ for (const row of rows) map.set(row.schema, {
14522
+ publicCreate: row.public_create,
14523
+ creatorRoles: row.creator_roles ?? []
14524
+ });
14525
+ return map;
14526
+ }
14527
+ async function fetchFunctions(client, schemas) {
14528
+ if (schemas.length === 0) return [];
14529
+ const rows = await runQuery(client, FUNCTIONS_SQL, [schemas], "fetchFunctions (functions)");
14530
+ if (rows.length === 0) return [];
14531
+ const parsed = rows.map((row) => ({
14532
+ row,
14533
+ searchPath: parseSearchPath(row.proconfig)
14534
+ }));
14535
+ const schemaNames = /* @__PURE__ */ new Set();
14536
+ for (const { row, searchPath } of parsed) {
14537
+ if (!row.security_definer || searchPath === null) continue;
14538
+ for (const el of searchPath) if (!el.isTemp && el.schema !== null) schemaNames.add(el.schema);
14539
+ }
14540
+ const writability = await fetchSchemaWritability(client, [...schemaNames]);
14541
+ return parsed.map(({ row, searchPath }) => {
14542
+ const resolvedSearchPath = searchPath === null ? null : searchPath.map((el) => {
14543
+ if (el.isTemp || el.schema === null) return el;
14544
+ const w = writability.get(el.schema);
14545
+ if (w === void 0) return {
14546
+ ...el,
14547
+ writableByOthers: null
14548
+ };
14549
+ const writableByOthers = w.publicCreate || w.creatorRoles.some((r) => r !== row.owner_oid);
14550
+ return {
14551
+ ...el,
14552
+ writableByOthers
14553
+ };
14554
+ });
14555
+ return {
14556
+ schema: row.schema,
14557
+ name: row.name,
14558
+ argumentSignature: row.argument_signature,
14559
+ arguments: buildArgs(row),
14560
+ returns: classifyReturn(row),
14561
+ volatility: mapVolatility(row.volatility),
14562
+ security: row.security_definer ? "definer" : "invoker",
14563
+ owner: {
14564
+ oid: row.owner_oid,
14565
+ name: row.owner_name
14566
+ },
14567
+ publicExecute: row.public_execute,
14568
+ searchPath: resolvedSearchPath,
14569
+ comment: row.comment
14570
+ };
14571
+ });
14572
+ }
14573
+ //#endregion
13962
14574
  //#region ../introspect/dist/privileges.js
13963
14575
  var tableKey = (schema, name) => `${schema}.${name}`;
13964
14576
  /** Throw a clear error if the configured privilege role does not exist, rather
@@ -14119,6 +14731,7 @@ async function introspect(opts) {
14119
14731
  mergeTableMetadata(allTables, await fetchForeignKeys(client, validSchemas), await fetchChecks(client, validSchemas));
14120
14732
  const allViews = await fetchViews(client, validSchemas);
14121
14733
  const enums = await fetchEnums(client, validSchemas);
14734
+ const functions = await fetchFunctions(client, validSchemas);
14122
14735
  if (opts.privilegeRole !== void 0) await fetchAndAttachPrivileges(client, validSchemas, opts.privilegeRole, allTables, allViews);
14123
14736
  const filterOpts = {
14124
14737
  include: opts.include,
@@ -14134,7 +14747,7 @@ async function introspect(opts) {
14134
14747
  tables,
14135
14748
  views,
14136
14749
  enums,
14137
- functions: []
14750
+ functions
14138
14751
  };
14139
14752
  } finally {
14140
14753
  try {
@@ -14230,11 +14843,22 @@ var cache = new SchemaCache({ loader: async () => {
14230
14843
  const connection = process.env.DATABASE_URL;
14231
14844
  if (typeof connection !== "string" || connection.length === 0) throw new Error("hooks.server: DATABASE_URL is required to introspect the schema.");
14232
14845
  const privilegeRole = process.env.KOZOU_INTROSPECTION_ROLE;
14233
- return buildSchemaContext({ raw: await introspect({
14234
- connection,
14235
- ...typeof privilegeRole === "string" && privilegeRole.length > 0 ? { privilegeRole } : {}
14236
- }) });
14846
+ return buildSchemaContext({
14847
+ raw: await introspect({
14848
+ connection,
14849
+ ...typeof privilegeRole === "string" && privilegeRole.length > 0 ? { privilegeRole } : {}
14850
+ }),
14851
+ rpc: {
14852
+ allowDefiner: parseList(process.env.KOZOU_RPC_ALLOW_DEFINER),
14853
+ allowPublicExecute: parseList(process.env.KOZOU_RPC_ALLOW_PUBLIC_EXECUTE)
14854
+ }
14855
+ });
14237
14856
  } });
14857
+ /** Parse a comma-separated env list into trimmed, non-empty entries. */
14858
+ function parseList(raw) {
14859
+ if (raw === void 0 || raw.length === 0) return [];
14860
+ return raw.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
14861
+ }
14238
14862
  var fkRowCache = new FkRowCache();
14239
14863
  var handle = async ({ event, resolve }) => {
14240
14864
  event.locals.schema = await cache.get();
@@ -14243,4 +14867,4 @@ var handle = async ({ event, resolve }) => {
14243
14867
  };
14244
14868
 
14245
14869
  export { handle };
14246
- //# sourceMappingURL=hooks.server-DtCXcOVD.js.map
14870
+ //# sourceMappingURL=hooks.server-DSFHJrhe.js.map