@agent-native/core 0.7.82 → 0.8.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 (265) hide show
  1. package/dist/action.js +1 -1
  2. package/dist/action.js.map +1 -1
  3. package/dist/agent/production-agent.d.ts.map +1 -1
  4. package/dist/agent/production-agent.js +8 -8
  5. package/dist/agent/production-agent.js.map +1 -1
  6. package/dist/agent/run-manager.d.ts +2 -0
  7. package/dist/agent/run-manager.d.ts.map +1 -1
  8. package/dist/agent/run-manager.js +44 -18
  9. package/dist/agent/run-manager.js.map +1 -1
  10. package/dist/agent/types.d.ts +1 -1
  11. package/dist/agent/types.d.ts.map +1 -1
  12. package/dist/agent/types.js.map +1 -1
  13. package/dist/cli/create.d.ts +1 -1
  14. package/dist/cli/create.d.ts.map +1 -1
  15. package/dist/cli/create.js +87 -19
  16. package/dist/cli/create.js.map +1 -1
  17. package/dist/cli/workspacify.d.ts.map +1 -1
  18. package/dist/cli/workspacify.js +12 -9
  19. package/dist/cli/workspacify.js.map +1 -1
  20. package/dist/client/AgentPanel.d.ts +1 -1
  21. package/dist/client/AgentPanel.d.ts.map +1 -1
  22. package/dist/client/AgentPanel.js +22 -1
  23. package/dist/client/AgentPanel.js.map +1 -1
  24. package/dist/client/FeedbackButton.d.ts +3 -2
  25. package/dist/client/FeedbackButton.d.ts.map +1 -1
  26. package/dist/client/FeedbackButton.js +18 -14
  27. package/dist/client/FeedbackButton.js.map +1 -1
  28. package/dist/client/agent-chat-adapter.d.ts.map +1 -1
  29. package/dist/client/agent-chat-adapter.js +254 -29
  30. package/dist/client/agent-chat-adapter.js.map +1 -1
  31. package/dist/client/agent-chat.d.ts +2 -0
  32. package/dist/client/agent-chat.d.ts.map +1 -1
  33. package/dist/client/agent-chat.js +11 -2
  34. package/dist/client/agent-chat.js.map +1 -1
  35. package/dist/client/builder-frame.d.ts +11 -0
  36. package/dist/client/builder-frame.d.ts.map +1 -1
  37. package/dist/client/builder-frame.js +40 -9
  38. package/dist/client/builder-frame.js.map +1 -1
  39. package/dist/client/composer/ComposerPlusMenu.js +1 -1
  40. package/dist/client/composer/ComposerPlusMenu.js.map +1 -1
  41. package/dist/client/composer/PromptComposer.d.ts +2 -0
  42. package/dist/client/composer/PromptComposer.d.ts.map +1 -1
  43. package/dist/client/composer/PromptComposer.js +3 -3
  44. package/dist/client/composer/PromptComposer.js.map +1 -1
  45. package/dist/client/composer/TiptapComposer.d.ts +3 -1
  46. package/dist/client/composer/TiptapComposer.d.ts.map +1 -1
  47. package/dist/client/composer/TiptapComposer.js +25 -13
  48. package/dist/client/composer/TiptapComposer.js.map +1 -1
  49. package/dist/client/composer/types.d.ts +1 -1
  50. package/dist/client/composer/types.d.ts.map +1 -1
  51. package/dist/client/composer/types.js.map +1 -1
  52. package/dist/client/extensions/EmbeddedExtension.d.ts +20 -0
  53. package/dist/client/extensions/EmbeddedExtension.d.ts.map +1 -0
  54. package/dist/client/{tools/EmbeddedTool.js → extensions/EmbeddedExtension.js} +41 -41
  55. package/dist/client/extensions/EmbeddedExtension.js.map +1 -0
  56. package/dist/client/extensions/ExtensionEditor.d.ts +5 -0
  57. package/dist/client/extensions/ExtensionEditor.d.ts.map +1 -0
  58. package/dist/client/extensions/ExtensionEditor.js +129 -0
  59. package/dist/client/extensions/ExtensionEditor.js.map +1 -0
  60. package/dist/client/{tools → extensions}/ExtensionSlot.d.ts +3 -3
  61. package/dist/client/extensions/ExtensionSlot.d.ts.map +1 -0
  62. package/dist/client/{tools → extensions}/ExtensionSlot.js +14 -14
  63. package/dist/client/extensions/ExtensionSlot.js.map +1 -0
  64. package/dist/client/extensions/ExtensionViewer.d.ts +5 -0
  65. package/dist/client/extensions/ExtensionViewer.d.ts.map +1 -0
  66. package/dist/client/{tools/ToolViewer.js → extensions/ExtensionViewer.js} +67 -65
  67. package/dist/client/extensions/ExtensionViewer.js.map +1 -0
  68. package/dist/client/extensions/ExtensionViewerPage.d.ts +2 -0
  69. package/dist/client/extensions/ExtensionViewerPage.d.ts.map +1 -0
  70. package/dist/client/{tools/ToolViewerPage.js → extensions/ExtensionViewerPage.js} +8 -8
  71. package/dist/client/extensions/ExtensionViewerPage.js.map +1 -0
  72. package/dist/client/extensions/ExtensionsListPage.d.ts +2 -0
  73. package/dist/client/extensions/ExtensionsListPage.d.ts.map +1 -0
  74. package/dist/client/extensions/ExtensionsListPage.js +67 -0
  75. package/dist/client/extensions/ExtensionsListPage.js.map +1 -0
  76. package/dist/client/extensions/ExtensionsSidebarSection.d.ts +2 -0
  77. package/dist/client/extensions/ExtensionsSidebarSection.d.ts.map +1 -0
  78. package/dist/client/{tools/ToolsSidebarSection.js → extensions/ExtensionsSidebarSection.js} +58 -58
  79. package/dist/client/extensions/ExtensionsSidebarSection.js.map +1 -0
  80. package/dist/client/{tools/tool-order.d.ts → extensions/extension-order.d.ts} +2 -2
  81. package/dist/client/extensions/extension-order.d.ts.map +1 -0
  82. package/dist/client/{tools/tool-order.js → extensions/extension-order.js} +3 -3
  83. package/dist/client/extensions/extension-order.js.map +1 -0
  84. package/dist/client/{tools → extensions}/iframe-bridge.d.ts +11 -11
  85. package/dist/client/extensions/iframe-bridge.d.ts.map +1 -0
  86. package/dist/client/{tools → extensions}/iframe-bridge.js +24 -24
  87. package/dist/client/extensions/iframe-bridge.js.map +1 -0
  88. package/dist/client/extensions/index.d.ts +14 -0
  89. package/dist/client/extensions/index.d.ts.map +1 -0
  90. package/dist/client/extensions/index.js +19 -0
  91. package/dist/client/extensions/index.js.map +1 -0
  92. package/dist/client/integrations/IntegrationsPanel.d.ts.map +1 -1
  93. package/dist/client/integrations/IntegrationsPanel.js +4 -1
  94. package/dist/client/integrations/IntegrationsPanel.js.map +1 -1
  95. package/dist/client/sse-event-processor.d.ts +2 -1
  96. package/dist/client/sse-event-processor.d.ts.map +1 -1
  97. package/dist/client/sse-event-processor.js +87 -6
  98. package/dist/client/sse-event-processor.js.map +1 -1
  99. package/dist/extensions/actions.d.ts +3 -0
  100. package/dist/extensions/actions.d.ts.map +1 -0
  101. package/dist/{tools → extensions}/actions.js +54 -51
  102. package/dist/extensions/actions.js.map +1 -0
  103. package/dist/{tools → extensions}/fetch-tool.d.ts +4 -0
  104. package/dist/extensions/fetch-tool.d.ts.map +1 -0
  105. package/dist/{tools → extensions}/fetch-tool.js +12 -7
  106. package/dist/extensions/fetch-tool.js.map +1 -0
  107. package/dist/extensions/html-shell.d.ts +56 -0
  108. package/dist/extensions/html-shell.d.ts.map +1 -0
  109. package/dist/{tools → extensions}/html-shell.js +101 -83
  110. package/dist/extensions/html-shell.js.map +1 -0
  111. package/dist/{tools → extensions}/proxy-security.d.ts +2 -2
  112. package/dist/extensions/proxy-security.d.ts.map +1 -0
  113. package/dist/{tools → extensions}/proxy-security.js +3 -3
  114. package/dist/extensions/proxy-security.js.map +1 -0
  115. package/dist/extensions/routes.d.ts +2 -0
  116. package/dist/extensions/routes.d.ts.map +1 -0
  117. package/dist/{tools → extensions}/routes.js +73 -69
  118. package/dist/extensions/routes.js.map +1 -0
  119. package/dist/{tools → extensions}/schema.d.ts +44 -38
  120. package/dist/extensions/schema.d.ts.map +1 -0
  121. package/dist/{tools → extensions}/schema.js +41 -34
  122. package/dist/extensions/schema.js.map +1 -0
  123. package/dist/extensions/slots/routes.d.ts +15 -0
  124. package/dist/extensions/slots/routes.d.ts.map +1 -0
  125. package/dist/{tools → extensions}/slots/routes.js +26 -26
  126. package/dist/extensions/slots/routes.js.map +1 -0
  127. package/dist/{tools → extensions}/slots/schema.d.ts +24 -21
  128. package/dist/extensions/slots/schema.d.ts.map +1 -0
  129. package/dist/extensions/slots/schema.js +79 -0
  130. package/dist/extensions/slots/schema.js.map +1 -0
  131. package/dist/extensions/slots/store.d.ts +66 -0
  132. package/dist/extensions/slots/store.d.ts.map +1 -0
  133. package/dist/extensions/slots/store.js +238 -0
  134. package/dist/extensions/slots/store.js.map +1 -0
  135. package/dist/extensions/store.d.ts +40 -0
  136. package/dist/extensions/store.d.ts.map +1 -0
  137. package/dist/{tools → extensions}/store.js +59 -54
  138. package/dist/extensions/store.js.map +1 -0
  139. package/dist/extensions/theme.d.ts.map +1 -0
  140. package/dist/extensions/theme.js.map +1 -0
  141. package/dist/{tools → extensions}/url-safety.d.ts +5 -3
  142. package/dist/extensions/url-safety.d.ts.map +1 -0
  143. package/dist/{tools → extensions}/url-safety.js +11 -4
  144. package/dist/extensions/url-safety.js.map +1 -0
  145. package/dist/server/action-discovery.d.ts +15 -0
  146. package/dist/server/action-discovery.d.ts.map +1 -1
  147. package/dist/server/action-discovery.js +45 -0
  148. package/dist/server/action-discovery.js.map +1 -1
  149. package/dist/server/agent-chat-plugin.d.ts.map +1 -1
  150. package/dist/server/agent-chat-plugin.js +12 -10
  151. package/dist/server/agent-chat-plugin.js.map +1 -1
  152. package/dist/server/auth.d.ts +5 -4
  153. package/dist/server/auth.d.ts.map +1 -1
  154. package/dist/server/auth.js +80 -28
  155. package/dist/server/auth.js.map +1 -1
  156. package/dist/server/core-routes-plugin.d.ts +15 -0
  157. package/dist/server/core-routes-plugin.d.ts.map +1 -1
  158. package/dist/server/core-routes-plugin.js +65 -13
  159. package/dist/server/core-routes-plugin.js.map +1 -1
  160. package/dist/server/csrf.d.ts +3 -2
  161. package/dist/server/csrf.d.ts.map +1 -1
  162. package/dist/server/csrf.js +3 -2
  163. package/dist/server/csrf.js.map +1 -1
  164. package/dist/server/google-oauth.d.ts.map +1 -1
  165. package/dist/server/google-oauth.js +15 -3
  166. package/dist/server/google-oauth.js.map +1 -1
  167. package/dist/server/index.d.ts +2 -2
  168. package/dist/server/index.d.ts.map +1 -1
  169. package/dist/server/index.js +1 -1
  170. package/dist/server/index.js.map +1 -1
  171. package/dist/shared/workspace-app-id.d.ts +1 -1
  172. package/dist/shared/workspace-app-id.d.ts.map +1 -1
  173. package/dist/shared/workspace-app-id.js +5 -1
  174. package/dist/shared/workspace-app-id.js.map +1 -1
  175. package/dist/templates/workspace-root/README.md +5 -4
  176. package/dist/usage/store.d.ts +1 -1
  177. package/dist/usage/store.d.ts.map +1 -1
  178. package/dist/usage/store.js +1 -1
  179. package/dist/usage/store.js.map +1 -1
  180. package/dist/vite/client.d.ts.map +1 -1
  181. package/dist/vite/client.js +17 -1
  182. package/dist/vite/client.js.map +1 -1
  183. package/docs/content/actions.md +10 -10
  184. package/docs/content/extensions.md +230 -0
  185. package/docs/content/key-concepts.md +2 -2
  186. package/docs/content/server.md +13 -13
  187. package/docs/content/sharing.md +2 -2
  188. package/docs/content/template-analytics.md +10 -0
  189. package/docs/content/template-calendar.md +10 -0
  190. package/docs/content/template-clips.md +10 -0
  191. package/docs/content/template-content.md +10 -0
  192. package/docs/content/template-dispatch.md +15 -0
  193. package/docs/content/template-forms.md +10 -0
  194. package/docs/content/template-mail.md +10 -0
  195. package/docs/content/template-slides.md +11 -1
  196. package/docs/content/template-starter.md +10 -0
  197. package/docs/content/template-video.md +10 -0
  198. package/docs/content/what-is-agent-native.md +1 -1
  199. package/package.json +22 -17
  200. package/src/templates/workspace-root/README.md +5 -4
  201. package/dist/client/tools/EmbeddedTool.d.ts +0 -20
  202. package/dist/client/tools/EmbeddedTool.d.ts.map +0 -1
  203. package/dist/client/tools/EmbeddedTool.js.map +0 -1
  204. package/dist/client/tools/ExtensionSlot.d.ts.map +0 -1
  205. package/dist/client/tools/ExtensionSlot.js.map +0 -1
  206. package/dist/client/tools/ToolEditor.d.ts +0 -5
  207. package/dist/client/tools/ToolEditor.d.ts.map +0 -1
  208. package/dist/client/tools/ToolEditor.js +0 -129
  209. package/dist/client/tools/ToolEditor.js.map +0 -1
  210. package/dist/client/tools/ToolViewer.d.ts +0 -5
  211. package/dist/client/tools/ToolViewer.d.ts.map +0 -1
  212. package/dist/client/tools/ToolViewer.js.map +0 -1
  213. package/dist/client/tools/ToolViewerPage.d.ts +0 -2
  214. package/dist/client/tools/ToolViewerPage.d.ts.map +0 -1
  215. package/dist/client/tools/ToolViewerPage.js.map +0 -1
  216. package/dist/client/tools/ToolsListPage.d.ts +0 -2
  217. package/dist/client/tools/ToolsListPage.d.ts.map +0 -1
  218. package/dist/client/tools/ToolsListPage.js +0 -67
  219. package/dist/client/tools/ToolsListPage.js.map +0 -1
  220. package/dist/client/tools/ToolsSidebarSection.d.ts +0 -2
  221. package/dist/client/tools/ToolsSidebarSection.d.ts.map +0 -1
  222. package/dist/client/tools/ToolsSidebarSection.js.map +0 -1
  223. package/dist/client/tools/iframe-bridge.d.ts.map +0 -1
  224. package/dist/client/tools/iframe-bridge.js.map +0 -1
  225. package/dist/client/tools/index.d.ts +0 -8
  226. package/dist/client/tools/index.d.ts.map +0 -1
  227. package/dist/client/tools/index.js +0 -8
  228. package/dist/client/tools/index.js.map +0 -1
  229. package/dist/client/tools/tool-order.d.ts.map +0 -1
  230. package/dist/client/tools/tool-order.js.map +0 -1
  231. package/dist/tools/actions.d.ts +0 -3
  232. package/dist/tools/actions.d.ts.map +0 -1
  233. package/dist/tools/actions.js.map +0 -1
  234. package/dist/tools/fetch-tool.d.ts.map +0 -1
  235. package/dist/tools/fetch-tool.js.map +0 -1
  236. package/dist/tools/html-shell.d.ts +0 -45
  237. package/dist/tools/html-shell.d.ts.map +0 -1
  238. package/dist/tools/html-shell.js.map +0 -1
  239. package/dist/tools/proxy-security.d.ts.map +0 -1
  240. package/dist/tools/proxy-security.js.map +0 -1
  241. package/dist/tools/routes.d.ts +0 -2
  242. package/dist/tools/routes.d.ts.map +0 -1
  243. package/dist/tools/routes.js.map +0 -1
  244. package/dist/tools/schema.d.ts.map +0 -1
  245. package/dist/tools/schema.js.map +0 -1
  246. package/dist/tools/slots/routes.d.ts +0 -15
  247. package/dist/tools/slots/routes.d.ts.map +0 -1
  248. package/dist/tools/slots/routes.js.map +0 -1
  249. package/dist/tools/slots/schema.d.ts.map +0 -1
  250. package/dist/tools/slots/schema.js +0 -76
  251. package/dist/tools/slots/schema.js.map +0 -1
  252. package/dist/tools/slots/store.d.ts +0 -66
  253. package/dist/tools/slots/store.d.ts.map +0 -1
  254. package/dist/tools/slots/store.js +0 -227
  255. package/dist/tools/slots/store.js.map +0 -1
  256. package/dist/tools/store.d.ts +0 -40
  257. package/dist/tools/store.d.ts.map +0 -1
  258. package/dist/tools/store.js.map +0 -1
  259. package/dist/tools/theme.d.ts.map +0 -1
  260. package/dist/tools/theme.js.map +0 -1
  261. package/dist/tools/url-safety.d.ts.map +0 -1
  262. package/dist/tools/url-safety.js.map +0 -1
  263. package/docs/content/tools.md +0 -205
  264. /package/dist/{tools → extensions}/theme.d.ts +0 -0
  265. /package/dist/{tools → extensions}/theme.js +0 -0
@@ -1,19 +1,22 @@
1
1
  /**
2
- * Drizzle schema for the tool extension-points system.
2
+ * Drizzle schema for the extension-points slot system.
3
3
  *
4
4
  * Two tables:
5
5
  *
6
- * - `tool_slots` — declarations: "tool X can render in slot Y". Authored
7
- * once per tool, regardless of installer.
8
- * - `tool_slot_installs` — per-user installs: "user U wants tool X in slot Y at
9
- * position N". Always scoped by owner_email.
6
+ * - `extension_slots` — declarations: "extension X can render in slot Y".
7
+ * Authored once per extension, regardless of installer.
8
+ * Physical SQL name stays `tool_slots` (additive-only).
9
+ * - `extension_slot_installs` — per-user installs: "user U wants extension X in
10
+ * slot Y at position N". Always scoped by
11
+ * owner_email. Physical SQL name stays
12
+ * `tool_slot_installs`.
10
13
  *
11
14
  * Neither table spreads `ownableColumns()` — they're not first-class shareable
12
- * resources. Access to the underlying tool flows through the existing `tools`
13
- * table sharing model; install rows are personal preferences scoped to the
14
- * installing user.
15
+ * resources. Access to the underlying extension flows through the existing
16
+ * `extensions` table sharing model; install rows are personal preferences
17
+ * scoped to the installing user.
15
18
  */
16
- export declare const toolSlots: import("drizzle-orm/sqlite-core").SQLiteTableWithColumns<{
19
+ export declare const extensionSlots: import("drizzle-orm/sqlite-core").SQLiteTableWithColumns<{
17
20
  name: "tool_slots";
18
21
  schema: undefined;
19
22
  columns: {
@@ -36,7 +39,7 @@ export declare const toolSlots: import("drizzle-orm/sqlite-core").SQLiteTableWit
36
39
  }, {}, {
37
40
  length: number;
38
41
  }>;
39
- toolId: import("drizzle-orm/sqlite-core").SQLiteColumn<{
42
+ extensionId: import("drizzle-orm/sqlite-core").SQLiteColumn<{
40
43
  name: "tool_id";
41
44
  tableName: "tool_slots";
42
45
  dataType: "string";
@@ -115,7 +118,7 @@ export declare const toolSlots: import("drizzle-orm/sqlite-core").SQLiteTableWit
115
118
  };
116
119
  dialect: "sqlite";
117
120
  }>;
118
- export declare const toolSlotInstalls: import("drizzle-orm/sqlite-core").SQLiteTableWithColumns<{
121
+ export declare const extensionSlotInstalls: import("drizzle-orm/sqlite-core").SQLiteTableWithColumns<{
119
122
  name: "tool_slot_installs";
120
123
  schema: undefined;
121
124
  columns: {
@@ -138,7 +141,7 @@ export declare const toolSlotInstalls: import("drizzle-orm/sqlite-core").SQLiteT
138
141
  }, {}, {
139
142
  length: number;
140
143
  }>;
141
- toolId: import("drizzle-orm/sqlite-core").SQLiteColumn<{
144
+ extensionId: import("drizzle-orm/sqlite-core").SQLiteColumn<{
142
145
  name: "tool_id";
143
146
  tableName: "tool_slot_installs";
144
147
  dataType: "string";
@@ -291,13 +294,13 @@ export declare const toolSlotInstalls: import("drizzle-orm/sqlite-core").SQLiteT
291
294
  };
292
295
  dialect: "sqlite";
293
296
  }>;
294
- export declare const TOOL_SLOTS_CREATE_SQL = "CREATE TABLE IF NOT EXISTS tool_slots (\n id TEXT PRIMARY KEY,\n tool_id TEXT NOT NULL,\n slot_id TEXT NOT NULL,\n config TEXT,\n created_at TEXT NOT NULL DEFAULT (datetime('now'))\n)";
295
- export declare const TOOL_SLOTS_CREATE_SQL_PG = "CREATE TABLE IF NOT EXISTS tool_slots (\n id TEXT PRIMARY KEY,\n tool_id TEXT NOT NULL,\n slot_id TEXT NOT NULL,\n config TEXT,\n created_at TEXT NOT NULL DEFAULT now()\n)";
296
- export declare const TOOL_SLOTS_BY_SLOT_INDEX_SQL = "CREATE INDEX IF NOT EXISTS tool_slots_by_slot_idx ON tool_slots (slot_id)";
297
- export declare const TOOL_SLOTS_BY_TOOL_INDEX_SQL = "CREATE INDEX IF NOT EXISTS tool_slots_by_tool_idx ON tool_slots (tool_id)";
298
- export declare const TOOL_SLOTS_UNIQUE_INDEX_SQL = "CREATE UNIQUE INDEX IF NOT EXISTS tool_slots_unique_idx ON tool_slots (tool_id, slot_id)";
299
- export declare const TOOL_SLOT_INSTALLS_CREATE_SQL = "CREATE TABLE IF NOT EXISTS tool_slot_installs (\n id TEXT PRIMARY KEY,\n tool_id TEXT NOT NULL,\n slot_id TEXT NOT NULL,\n owner_email TEXT NOT NULL,\n org_id TEXT,\n position INTEGER NOT NULL DEFAULT 0,\n config TEXT,\n created_at TEXT NOT NULL DEFAULT (datetime('now')),\n updated_at TEXT NOT NULL DEFAULT (datetime('now'))\n)";
300
- export declare const TOOL_SLOT_INSTALLS_CREATE_SQL_PG = "CREATE TABLE IF NOT EXISTS tool_slot_installs (\n id TEXT PRIMARY KEY,\n tool_id TEXT NOT NULL,\n slot_id TEXT NOT NULL,\n owner_email TEXT NOT NULL,\n org_id TEXT,\n position INTEGER NOT NULL DEFAULT 0,\n config TEXT,\n created_at TEXT NOT NULL DEFAULT now(),\n updated_at TEXT NOT NULL DEFAULT now()\n)";
301
- export declare const TOOL_SLOT_INSTALLS_BY_USER_SLOT_INDEX_SQL = "CREATE INDEX IF NOT EXISTS tool_slot_installs_by_user_slot_idx ON tool_slot_installs (owner_email, slot_id)";
302
- export declare const TOOL_SLOT_INSTALLS_UNIQUE_INDEX_SQL = "CREATE UNIQUE INDEX IF NOT EXISTS tool_slot_installs_unique_idx ON tool_slot_installs (owner_email, tool_id, slot_id)";
297
+ export declare const EXTENSION_SLOTS_CREATE_SQL = "CREATE TABLE IF NOT EXISTS tool_slots (\n id TEXT PRIMARY KEY,\n tool_id TEXT NOT NULL,\n slot_id TEXT NOT NULL,\n config TEXT,\n created_at TEXT NOT NULL DEFAULT (datetime('now'))\n)";
298
+ export declare const EXTENSION_SLOTS_CREATE_SQL_PG = "CREATE TABLE IF NOT EXISTS tool_slots (\n id TEXT PRIMARY KEY,\n tool_id TEXT NOT NULL,\n slot_id TEXT NOT NULL,\n config TEXT,\n created_at TEXT NOT NULL DEFAULT now()\n)";
299
+ export declare const EXTENSION_SLOTS_BY_SLOT_INDEX_SQL = "CREATE INDEX IF NOT EXISTS tool_slots_by_slot_idx ON tool_slots (slot_id)";
300
+ export declare const EXTENSION_SLOTS_BY_EXTENSION_INDEX_SQL = "CREATE INDEX IF NOT EXISTS tool_slots_by_tool_idx ON tool_slots (tool_id)";
301
+ export declare const EXTENSION_SLOTS_UNIQUE_INDEX_SQL = "CREATE UNIQUE INDEX IF NOT EXISTS tool_slots_unique_idx ON tool_slots (tool_id, slot_id)";
302
+ export declare const EXTENSION_SLOT_INSTALLS_CREATE_SQL = "CREATE TABLE IF NOT EXISTS tool_slot_installs (\n id TEXT PRIMARY KEY,\n tool_id TEXT NOT NULL,\n slot_id TEXT NOT NULL,\n owner_email TEXT NOT NULL,\n org_id TEXT,\n position INTEGER NOT NULL DEFAULT 0,\n config TEXT,\n created_at TEXT NOT NULL DEFAULT (datetime('now')),\n updated_at TEXT NOT NULL DEFAULT (datetime('now'))\n)";
303
+ export declare const EXTENSION_SLOT_INSTALLS_CREATE_SQL_PG = "CREATE TABLE IF NOT EXISTS tool_slot_installs (\n id TEXT PRIMARY KEY,\n tool_id TEXT NOT NULL,\n slot_id TEXT NOT NULL,\n owner_email TEXT NOT NULL,\n org_id TEXT,\n position INTEGER NOT NULL DEFAULT 0,\n config TEXT,\n created_at TEXT NOT NULL DEFAULT now(),\n updated_at TEXT NOT NULL DEFAULT now()\n)";
304
+ export declare const EXTENSION_SLOT_INSTALLS_BY_USER_SLOT_INDEX_SQL = "CREATE INDEX IF NOT EXISTS tool_slot_installs_by_user_slot_idx ON tool_slot_installs (owner_email, slot_id)";
305
+ export declare const EXTENSION_SLOT_INSTALLS_UNIQUE_INDEX_SQL = "CREATE UNIQUE INDEX IF NOT EXISTS tool_slot_installs_unique_idx ON tool_slot_installs (owner_email, tool_id, slot_id)";
303
306
  //# sourceMappingURL=schema.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../../src/extensions/slots/schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAIH,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAMzB,CAAC;AAEH,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAUhC,CAAC;AAEH,eAAO,MAAM,0BAA0B,iMAMrC,CAAC;AAEH,eAAO,MAAM,6BAA6B,qLAMxC,CAAC;AAEH,eAAO,MAAM,iCAAiC,8EAA8E,CAAC;AAC7H,eAAO,MAAM,sCAAsC,8EAA8E,CAAC;AAClI,eAAO,MAAM,gCAAgC,6FAA6F,CAAC;AAE3I,eAAO,MAAM,kCAAkC,sVAU7C,CAAC;AAEH,eAAO,MAAM,qCAAqC,8TAUhD,CAAC;AAEH,eAAO,MAAM,8CAA8C,gHAAgH,CAAC;AAC5K,eAAO,MAAM,wCAAwC,0HAA0H,CAAC"}
@@ -0,0 +1,79 @@
1
+ /**
2
+ * Drizzle schema for the extension-points slot system.
3
+ *
4
+ * Two tables:
5
+ *
6
+ * - `extension_slots` — declarations: "extension X can render in slot Y".
7
+ * Authored once per extension, regardless of installer.
8
+ * Physical SQL name stays `tool_slots` (additive-only).
9
+ * - `extension_slot_installs` — per-user installs: "user U wants extension X in
10
+ * slot Y at position N". Always scoped by
11
+ * owner_email. Physical SQL name stays
12
+ * `tool_slot_installs`.
13
+ *
14
+ * Neither table spreads `ownableColumns()` — they're not first-class shareable
15
+ * resources. Access to the underlying extension flows through the existing
16
+ * `extensions` table sharing model; install rows are personal preferences
17
+ * scoped to the installing user.
18
+ */
19
+ import { table, text, integer, now } from "../../db/schema.js";
20
+ export const extensionSlots = table("tool_slots", {
21
+ id: text("id").primaryKey(),
22
+ extensionId: text("tool_id").notNull(),
23
+ slotId: text("slot_id").notNull(),
24
+ config: text("config"),
25
+ createdAt: text("created_at").notNull().default(now()),
26
+ });
27
+ export const extensionSlotInstalls = table("tool_slot_installs", {
28
+ id: text("id").primaryKey(),
29
+ extensionId: text("tool_id").notNull(),
30
+ slotId: text("slot_id").notNull(),
31
+ ownerEmail: text("owner_email").notNull(),
32
+ orgId: text("org_id"),
33
+ position: integer("position").notNull().default(0),
34
+ config: text("config"),
35
+ createdAt: text("created_at").notNull().default(now()),
36
+ updatedAt: text("updated_at").notNull().default(now()),
37
+ });
38
+ export const EXTENSION_SLOTS_CREATE_SQL = `CREATE TABLE IF NOT EXISTS tool_slots (
39
+ id TEXT PRIMARY KEY,
40
+ tool_id TEXT NOT NULL,
41
+ slot_id TEXT NOT NULL,
42
+ config TEXT,
43
+ created_at TEXT NOT NULL DEFAULT (datetime('now'))
44
+ )`;
45
+ export const EXTENSION_SLOTS_CREATE_SQL_PG = `CREATE TABLE IF NOT EXISTS tool_slots (
46
+ id TEXT PRIMARY KEY,
47
+ tool_id TEXT NOT NULL,
48
+ slot_id TEXT NOT NULL,
49
+ config TEXT,
50
+ created_at TEXT NOT NULL DEFAULT now()
51
+ )`;
52
+ export const EXTENSION_SLOTS_BY_SLOT_INDEX_SQL = `CREATE INDEX IF NOT EXISTS tool_slots_by_slot_idx ON tool_slots (slot_id)`;
53
+ export const EXTENSION_SLOTS_BY_EXTENSION_INDEX_SQL = `CREATE INDEX IF NOT EXISTS tool_slots_by_tool_idx ON tool_slots (tool_id)`;
54
+ export const EXTENSION_SLOTS_UNIQUE_INDEX_SQL = `CREATE UNIQUE INDEX IF NOT EXISTS tool_slots_unique_idx ON tool_slots (tool_id, slot_id)`;
55
+ export const EXTENSION_SLOT_INSTALLS_CREATE_SQL = `CREATE TABLE IF NOT EXISTS tool_slot_installs (
56
+ id TEXT PRIMARY KEY,
57
+ tool_id TEXT NOT NULL,
58
+ slot_id TEXT NOT NULL,
59
+ owner_email TEXT NOT NULL,
60
+ org_id TEXT,
61
+ position INTEGER NOT NULL DEFAULT 0,
62
+ config TEXT,
63
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
64
+ updated_at TEXT NOT NULL DEFAULT (datetime('now'))
65
+ )`;
66
+ export const EXTENSION_SLOT_INSTALLS_CREATE_SQL_PG = `CREATE TABLE IF NOT EXISTS tool_slot_installs (
67
+ id TEXT PRIMARY KEY,
68
+ tool_id TEXT NOT NULL,
69
+ slot_id TEXT NOT NULL,
70
+ owner_email TEXT NOT NULL,
71
+ org_id TEXT,
72
+ position INTEGER NOT NULL DEFAULT 0,
73
+ config TEXT,
74
+ created_at TEXT NOT NULL DEFAULT now(),
75
+ updated_at TEXT NOT NULL DEFAULT now()
76
+ )`;
77
+ export const EXTENSION_SLOT_INSTALLS_BY_USER_SLOT_INDEX_SQL = `CREATE INDEX IF NOT EXISTS tool_slot_installs_by_user_slot_idx ON tool_slot_installs (owner_email, slot_id)`;
78
+ export const EXTENSION_SLOT_INSTALLS_UNIQUE_INDEX_SQL = `CREATE UNIQUE INDEX IF NOT EXISTS tool_slot_installs_unique_idx ON tool_slot_installs (owner_email, tool_id, slot_id)`;
79
+ //# sourceMappingURL=schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.js","sourceRoot":"","sources":["../../../src/extensions/slots/schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,oBAAoB,CAAC;AAE/D,MAAM,CAAC,MAAM,cAAc,GAAG,KAAK,CAAC,YAAY,EAAE;IAChD,EAAE,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC,UAAU,EAAE;IAC3B,WAAW,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE;IACtC,MAAM,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE;IACjC,MAAM,EAAE,IAAI,CAAC,QAAQ,CAAC;IACtB,SAAS,EAAE,IAAI,CAAC,YAAY,CAAC,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC;CACvD,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,qBAAqB,GAAG,KAAK,CAAC,oBAAoB,EAAE;IAC/D,EAAE,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC,UAAU,EAAE;IAC3B,WAAW,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE;IACtC,MAAM,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE;IACjC,UAAU,EAAE,IAAI,CAAC,aAAa,CAAC,CAAC,OAAO,EAAE;IACzC,KAAK,EAAE,IAAI,CAAC,QAAQ,CAAC;IACrB,QAAQ,EAAE,OAAO,CAAC,UAAU,CAAC,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC;IAClD,MAAM,EAAE,IAAI,CAAC,QAAQ,CAAC;IACtB,SAAS,EAAE,IAAI,CAAC,YAAY,CAAC,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC;IACtD,SAAS,EAAE,IAAI,CAAC,YAAY,CAAC,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC;CACvD,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,0BAA0B,GAAG;;;;;;EAMxC,CAAC;AAEH,MAAM,CAAC,MAAM,6BAA6B,GAAG;;;;;;EAM3C,CAAC;AAEH,MAAM,CAAC,MAAM,iCAAiC,GAAG,2EAA2E,CAAC;AAC7H,MAAM,CAAC,MAAM,sCAAsC,GAAG,2EAA2E,CAAC;AAClI,MAAM,CAAC,MAAM,gCAAgC,GAAG,0FAA0F,CAAC;AAE3I,MAAM,CAAC,MAAM,kCAAkC,GAAG;;;;;;;;;;EAUhD,CAAC;AAEH,MAAM,CAAC,MAAM,qCAAqC,GAAG;;;;;;;;;;EAUnD,CAAC;AAEH,MAAM,CAAC,MAAM,8CAA8C,GAAG,6GAA6G,CAAC;AAC5K,MAAM,CAAC,MAAM,wCAAwC,GAAG,uHAAuH,CAAC","sourcesContent":["/**\n * Drizzle schema for the extension-points slot system.\n *\n * Two tables:\n *\n * - `extension_slots` — declarations: \"extension X can render in slot Y\".\n * Authored once per extension, regardless of installer.\n * Physical SQL name stays `tool_slots` (additive-only).\n * - `extension_slot_installs` — per-user installs: \"user U wants extension X in\n * slot Y at position N\". Always scoped by\n * owner_email. Physical SQL name stays\n * `tool_slot_installs`.\n *\n * Neither table spreads `ownableColumns()` — they're not first-class shareable\n * resources. Access to the underlying extension flows through the existing\n * `extensions` table sharing model; install rows are personal preferences\n * scoped to the installing user.\n */\n\nimport { table, text, integer, now } from \"../../db/schema.js\";\n\nexport const extensionSlots = table(\"tool_slots\", {\n id: text(\"id\").primaryKey(),\n extensionId: text(\"tool_id\").notNull(),\n slotId: text(\"slot_id\").notNull(),\n config: text(\"config\"),\n createdAt: text(\"created_at\").notNull().default(now()),\n});\n\nexport const extensionSlotInstalls = table(\"tool_slot_installs\", {\n id: text(\"id\").primaryKey(),\n extensionId: text(\"tool_id\").notNull(),\n slotId: text(\"slot_id\").notNull(),\n ownerEmail: text(\"owner_email\").notNull(),\n orgId: text(\"org_id\"),\n position: integer(\"position\").notNull().default(0),\n config: text(\"config\"),\n createdAt: text(\"created_at\").notNull().default(now()),\n updatedAt: text(\"updated_at\").notNull().default(now()),\n});\n\nexport const EXTENSION_SLOTS_CREATE_SQL = `CREATE TABLE IF NOT EXISTS tool_slots (\n id TEXT PRIMARY KEY,\n tool_id TEXT NOT NULL,\n slot_id TEXT NOT NULL,\n config TEXT,\n created_at TEXT NOT NULL DEFAULT (datetime('now'))\n)`;\n\nexport const EXTENSION_SLOTS_CREATE_SQL_PG = `CREATE TABLE IF NOT EXISTS tool_slots (\n id TEXT PRIMARY KEY,\n tool_id TEXT NOT NULL,\n slot_id TEXT NOT NULL,\n config TEXT,\n created_at TEXT NOT NULL DEFAULT now()\n)`;\n\nexport const EXTENSION_SLOTS_BY_SLOT_INDEX_SQL = `CREATE INDEX IF NOT EXISTS tool_slots_by_slot_idx ON tool_slots (slot_id)`;\nexport const EXTENSION_SLOTS_BY_EXTENSION_INDEX_SQL = `CREATE INDEX IF NOT EXISTS tool_slots_by_tool_idx ON tool_slots (tool_id)`;\nexport const EXTENSION_SLOTS_UNIQUE_INDEX_SQL = `CREATE UNIQUE INDEX IF NOT EXISTS tool_slots_unique_idx ON tool_slots (tool_id, slot_id)`;\n\nexport const EXTENSION_SLOT_INSTALLS_CREATE_SQL = `CREATE TABLE IF NOT EXISTS tool_slot_installs (\n id TEXT PRIMARY KEY,\n tool_id TEXT NOT NULL,\n slot_id TEXT NOT NULL,\n owner_email TEXT NOT NULL,\n org_id TEXT,\n position INTEGER NOT NULL DEFAULT 0,\n config TEXT,\n created_at TEXT NOT NULL DEFAULT (datetime('now')),\n updated_at TEXT NOT NULL DEFAULT (datetime('now'))\n)`;\n\nexport const EXTENSION_SLOT_INSTALLS_CREATE_SQL_PG = `CREATE TABLE IF NOT EXISTS tool_slot_installs (\n id TEXT PRIMARY KEY,\n tool_id TEXT NOT NULL,\n slot_id TEXT NOT NULL,\n owner_email TEXT NOT NULL,\n org_id TEXT,\n position INTEGER NOT NULL DEFAULT 0,\n config TEXT,\n created_at TEXT NOT NULL DEFAULT now(),\n updated_at TEXT NOT NULL DEFAULT now()\n)`;\n\nexport const EXTENSION_SLOT_INSTALLS_BY_USER_SLOT_INDEX_SQL = `CREATE INDEX IF NOT EXISTS tool_slot_installs_by_user_slot_idx ON tool_slot_installs (owner_email, slot_id)`;\nexport const EXTENSION_SLOT_INSTALLS_UNIQUE_INDEX_SQL = `CREATE UNIQUE INDEX IF NOT EXISTS tool_slot_installs_unique_idx ON tool_slot_installs (owner_email, tool_id, slot_id)`;\n"]}
@@ -0,0 +1,66 @@
1
+ export declare function ensureSlotTables(): Promise<void>;
2
+ export interface ExtensionSlotRow {
3
+ id: string;
4
+ extensionId: string;
5
+ slotId: string;
6
+ config: string | null;
7
+ createdAt: string;
8
+ }
9
+ export interface ExtensionSlotInstallRow {
10
+ id: string;
11
+ extensionId: string;
12
+ slotId: string;
13
+ ownerEmail: string;
14
+ orgId: string | null;
15
+ position: number;
16
+ config: string | null;
17
+ createdAt: string;
18
+ updatedAt: string;
19
+ }
20
+ /**
21
+ * Declare that a extension can render in a slot. Caller must have editor access on
22
+ * the extension (only people who can edit a extension can change its slot targets).
23
+ */
24
+ export declare function addExtensionSlotTarget(extensionId: string, slotId: string, config?: string): Promise<ExtensionSlotRow>;
25
+ export declare function removeExtensionSlotTarget(extensionId: string, slotId: string): Promise<boolean>;
26
+ export declare function listSlotsForExtension(extensionId: string): Promise<ExtensionSlotRow[]>;
27
+ /**
28
+ * List extensions that declare a slot — but only extensions the current user has access
29
+ * to. Joins through the extensions access filter.
30
+ */
31
+ export declare function listExtensionsForSlot(slotId: string): Promise<Array<{
32
+ extensionId: string;
33
+ name: string;
34
+ description: string;
35
+ icon: string | null;
36
+ config: string | null;
37
+ }>>;
38
+ /**
39
+ * Install a extension into a slot for the current user. Verifies the user has at
40
+ * least viewer access to the extension. Idempotent — re-installing returns the
41
+ * existing row.
42
+ */
43
+ export declare function installExtensionSlot(extensionId: string, slotId: string, opts?: {
44
+ position?: number;
45
+ config?: string;
46
+ }): Promise<ExtensionSlotInstallRow>;
47
+ export declare function uninstallExtensionSlot(extensionId: string, slotId: string): Promise<boolean>;
48
+ /**
49
+ * List the current user's installs for a slot. Joins with `extensions` so the
50
+ * caller gets extension name/description/icon/updatedAt without a second query.
51
+ * Extensions the user has lost access to are silently skipped (lazy garbage
52
+ * collection).
53
+ */
54
+ export declare function listSlotInstallsForUser(slotId: string): Promise<Array<{
55
+ installId: string;
56
+ extensionId: string;
57
+ name: string;
58
+ description: string;
59
+ icon: string | null;
60
+ updatedAt: string;
61
+ position: number;
62
+ config: string | null;
63
+ }>>;
64
+ /** Delete every slot/install row referencing a extension. Called from deleteExtension. */
65
+ export declare function cascadeDeleteExtensionSlots(extensionId: string): Promise<void>;
66
+ //# sourceMappingURL=store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../../src/extensions/slots/store.ts"],"names":[],"mappings":"AAiCA,wBAAsB,gBAAgB,IAAI,OAAO,CAAC,IAAI,CAAC,CAqBtD;AAED,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,uBAAuB;IACtC,EAAE,EAAE,MAAM,CAAC;IACX,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;GAGG;AACH,wBAAsB,sBAAsB,CAC1C,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,MAAM,EACd,MAAM,CAAC,EAAE,MAAM,GACd,OAAO,CAAC,gBAAgB,CAAC,CAoC3B;AAED,wBAAsB,yBAAyB,CAC7C,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,OAAO,CAAC,CAalB;AAED,wBAAsB,qBAAqB,CACzC,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAS7B;AAED;;;GAGG;AACH,wBAAsB,qBAAqB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAClE,KAAK,CAAC;IACJ,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB,CAAC,CACH,CAmCA;AAED;;;;GAIG;AACH,wBAAsB,oBAAoB,CACxC,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,MAAM,EACd,IAAI,CAAC,EAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GAC5C,OAAO,CAAC,uBAAuB,CAAC,CA+ClC;AAED,wBAAsB,sBAAsB,CAC1C,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,OAAO,CAAC,CAclB;AAED;;;;;GAKG;AACH,wBAAsB,uBAAuB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CACpE,KAAK,CAAC;IACJ,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB,CAAC,CACH,CA4CA;AAED,0FAA0F;AAC1F,wBAAsB,2BAA2B,CAC/C,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,IAAI,CAAC,CASf"}
@@ -0,0 +1,238 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { and, eq, inArray, sql } from "drizzle-orm";
3
+ import { getDbExec, isPostgres } from "../../db/client.js";
4
+ import { createGetDb } from "../../db/create-get-db.js";
5
+ import { accessFilter, assertAccess } from "../../sharing/access.js";
6
+ import { getRequestUserEmail, getRequestOrgId, } from "../../server/request-context.js";
7
+ import { extensions, extensionShares } from "../schema.js";
8
+ import { extensionSlots, extensionSlotInstalls, EXTENSION_SLOTS_CREATE_SQL, EXTENSION_SLOTS_CREATE_SQL_PG, EXTENSION_SLOTS_BY_SLOT_INDEX_SQL, EXTENSION_SLOTS_BY_EXTENSION_INDEX_SQL, EXTENSION_SLOTS_UNIQUE_INDEX_SQL, EXTENSION_SLOT_INSTALLS_CREATE_SQL, EXTENSION_SLOT_INSTALLS_CREATE_SQL_PG, EXTENSION_SLOT_INSTALLS_BY_USER_SLOT_INDEX_SQL, EXTENSION_SLOT_INSTALLS_UNIQUE_INDEX_SQL, } from "./schema.js";
9
+ const getDb = createGetDb({
10
+ extensions,
11
+ extensionShares,
12
+ extensionSlots,
13
+ extensionSlotInstalls,
14
+ });
15
+ let _initPromise;
16
+ export async function ensureSlotTables() {
17
+ if (!_initPromise) {
18
+ _initPromise = (async () => {
19
+ const client = getDbExec();
20
+ const pg = isPostgres();
21
+ await client.execute(pg ? EXTENSION_SLOTS_CREATE_SQL_PG : EXTENSION_SLOTS_CREATE_SQL);
22
+ await client.execute(EXTENSION_SLOTS_BY_SLOT_INDEX_SQL);
23
+ await client.execute(EXTENSION_SLOTS_BY_EXTENSION_INDEX_SQL);
24
+ await client.execute(EXTENSION_SLOTS_UNIQUE_INDEX_SQL);
25
+ await client.execute(pg
26
+ ? EXTENSION_SLOT_INSTALLS_CREATE_SQL_PG
27
+ : EXTENSION_SLOT_INSTALLS_CREATE_SQL);
28
+ await client.execute(EXTENSION_SLOT_INSTALLS_BY_USER_SLOT_INDEX_SQL);
29
+ await client.execute(EXTENSION_SLOT_INSTALLS_UNIQUE_INDEX_SQL);
30
+ })();
31
+ }
32
+ return _initPromise;
33
+ }
34
+ /**
35
+ * Declare that a extension can render in a slot. Caller must have editor access on
36
+ * the extension (only people who can edit a extension can change its slot targets).
37
+ */
38
+ export async function addExtensionSlotTarget(extensionId, slotId, config) {
39
+ await ensureSlotTables();
40
+ await assertAccess("extension", extensionId, "editor");
41
+ const db = getDb();
42
+ const id = randomUUID();
43
+ const createdAt = new Date().toISOString();
44
+ const row = {
45
+ id,
46
+ extensionId,
47
+ slotId,
48
+ config: config ?? null,
49
+ createdAt,
50
+ };
51
+ try {
52
+ await db.insert(extensionSlots).values(row);
53
+ }
54
+ catch (err) {
55
+ // Unique index hit — already declared. Treat as idempotent: return existing.
56
+ if (String(err?.message ?? err)
57
+ .toLowerCase()
58
+ .includes("unique")) {
59
+ const existing = await db
60
+ .select()
61
+ .from(extensionSlots)
62
+ .where(and(eq(extensionSlots.extensionId, extensionId), eq(extensionSlots.slotId, slotId)));
63
+ if (existing[0])
64
+ return existing[0];
65
+ }
66
+ throw err;
67
+ }
68
+ return row;
69
+ }
70
+ export async function removeExtensionSlotTarget(extensionId, slotId) {
71
+ await ensureSlotTables();
72
+ await assertAccess("extension", extensionId, "editor");
73
+ const db = getDb();
74
+ await db
75
+ .delete(extensionSlots)
76
+ .where(and(eq(extensionSlots.extensionId, extensionId), eq(extensionSlots.slotId, slotId)));
77
+ return true;
78
+ }
79
+ export async function listSlotsForExtension(extensionId) {
80
+ await ensureSlotTables();
81
+ await assertAccess("extension", extensionId, "viewer");
82
+ const db = getDb();
83
+ const rows = await db
84
+ .select()
85
+ .from(extensionSlots)
86
+ .where(eq(extensionSlots.extensionId, extensionId));
87
+ return rows;
88
+ }
89
+ /**
90
+ * List extensions that declare a slot — but only extensions the current user has access
91
+ * to. Joins through the extensions access filter.
92
+ */
93
+ export async function listExtensionsForSlot(slotId) {
94
+ await ensureSlotTables();
95
+ const db = getDb();
96
+ // Pull extensions the user can see, then narrow to ones declaring this slot.
97
+ const accessible = await db
98
+ .select({
99
+ id: extensions.id,
100
+ name: extensions.name,
101
+ description: extensions.description,
102
+ icon: extensions.icon,
103
+ })
104
+ .from(extensions)
105
+ .where(accessFilter(extensions, extensionShares));
106
+ if (accessible.length === 0)
107
+ return [];
108
+ const ids = accessible.map((t) => t.id);
109
+ const declarations = await db
110
+ .select()
111
+ .from(extensionSlots)
112
+ .where(and(eq(extensionSlots.slotId, slotId), inArray(extensionSlots.extensionId, ids)));
113
+ const byId = new Map(accessible.map((t) => [t.id, t]));
114
+ return declarations.map((d) => {
115
+ const t = byId.get(d.extensionId);
116
+ return {
117
+ extensionId: d.extensionId,
118
+ name: t.name,
119
+ description: t.description,
120
+ icon: t.icon,
121
+ config: d.config,
122
+ };
123
+ });
124
+ }
125
+ /**
126
+ * Install a extension into a slot for the current user. Verifies the user has at
127
+ * least viewer access to the extension. Idempotent — re-installing returns the
128
+ * existing row.
129
+ */
130
+ export async function installExtensionSlot(extensionId, slotId, opts) {
131
+ await ensureSlotTables();
132
+ await assertAccess("extension", extensionId, "viewer");
133
+ const userEmail = requireUserEmail();
134
+ const orgId = getRequestOrgId();
135
+ const db = getDb();
136
+ const existing = await db
137
+ .select()
138
+ .from(extensionSlotInstalls)
139
+ .where(and(eq(extensionSlotInstalls.ownerEmail, userEmail), eq(extensionSlotInstalls.extensionId, extensionId), eq(extensionSlotInstalls.slotId, slotId)));
140
+ if (existing[0])
141
+ return existing[0];
142
+ const id = randomUUID();
143
+ const now = new Date().toISOString();
144
+ let position = opts?.position;
145
+ if (position === undefined) {
146
+ const rows = await db
147
+ .select({ pos: sql `MAX(${extensionSlotInstalls.position})` })
148
+ .from(extensionSlotInstalls)
149
+ .where(and(eq(extensionSlotInstalls.ownerEmail, userEmail), eq(extensionSlotInstalls.slotId, slotId)));
150
+ const maxPos = Number(rows[0]?.pos ?? -1);
151
+ position = Number.isFinite(maxPos) ? maxPos + 1 : 0;
152
+ }
153
+ const row = {
154
+ id,
155
+ extensionId,
156
+ slotId,
157
+ ownerEmail: userEmail,
158
+ orgId: orgId ?? null,
159
+ position,
160
+ config: opts?.config ?? null,
161
+ createdAt: now,
162
+ updatedAt: now,
163
+ };
164
+ await db.insert(extensionSlotInstalls).values(row);
165
+ return row;
166
+ }
167
+ export async function uninstallExtensionSlot(extensionId, slotId) {
168
+ await ensureSlotTables();
169
+ const userEmail = requireUserEmail();
170
+ const db = getDb();
171
+ await db
172
+ .delete(extensionSlotInstalls)
173
+ .where(and(eq(extensionSlotInstalls.ownerEmail, userEmail), eq(extensionSlotInstalls.extensionId, extensionId), eq(extensionSlotInstalls.slotId, slotId)));
174
+ return true;
175
+ }
176
+ /**
177
+ * List the current user's installs for a slot. Joins with `extensions` so the
178
+ * caller gets extension name/description/icon/updatedAt without a second query.
179
+ * Extensions the user has lost access to are silently skipped (lazy garbage
180
+ * collection).
181
+ */
182
+ export async function listSlotInstallsForUser(slotId) {
183
+ await ensureSlotTables();
184
+ const userEmail = requireUserEmail();
185
+ const db = getDb();
186
+ const installs = await db
187
+ .select()
188
+ .from(extensionSlotInstalls)
189
+ .where(and(eq(extensionSlotInstalls.ownerEmail, userEmail), eq(extensionSlotInstalls.slotId, slotId)));
190
+ if (installs.length === 0)
191
+ return [];
192
+ const accessible = await db
193
+ .select({
194
+ id: extensions.id,
195
+ name: extensions.name,
196
+ description: extensions.description,
197
+ icon: extensions.icon,
198
+ updatedAt: extensions.updatedAt,
199
+ })
200
+ .from(extensions)
201
+ .where(accessFilter(extensions, extensionShares));
202
+ const byId = new Map(accessible.map((t) => [t.id, t]));
203
+ return installs
204
+ .filter((i) => byId.has(i.extensionId))
205
+ .sort((a, b) => a.position - b.position)
206
+ .map((i) => {
207
+ const t = byId.get(i.extensionId);
208
+ return {
209
+ installId: i.id,
210
+ extensionId: i.extensionId,
211
+ name: t.name,
212
+ description: t.description,
213
+ icon: t.icon,
214
+ updatedAt: t.updatedAt,
215
+ position: i.position,
216
+ config: i.config,
217
+ };
218
+ });
219
+ }
220
+ /** Delete every slot/install row referencing a extension. Called from deleteExtension. */
221
+ export async function cascadeDeleteExtensionSlots(extensionId) {
222
+ await ensureSlotTables();
223
+ const db = getDb();
224
+ await db
225
+ .delete(extensionSlots)
226
+ .where(eq(extensionSlots.extensionId, extensionId));
227
+ await db
228
+ .delete(extensionSlotInstalls)
229
+ .where(eq(extensionSlotInstalls.extensionId, extensionId));
230
+ }
231
+ function requireUserEmail() {
232
+ const email = getRequestUserEmail();
233
+ if (!email) {
234
+ throw new Error("Slot operations require an authenticated user.");
235
+ }
236
+ return email;
237
+ }
238
+ //# sourceMappingURL=store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.js","sourceRoot":"","sources":["../../../src/extensions/slots/store.ts"],"names":[],"mappings":"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","sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport { and, eq, inArray, sql } from \"drizzle-orm\";\nimport { getDbExec, isPostgres } from \"../../db/client.js\";\nimport { createGetDb } from \"../../db/create-get-db.js\";\nimport { accessFilter, assertAccess } from \"../../sharing/access.js\";\nimport {\n getRequestUserEmail,\n getRequestOrgId,\n} from \"../../server/request-context.js\";\nimport { extensions, extensionShares } from \"../schema.js\";\nimport {\n extensionSlots,\n extensionSlotInstalls,\n EXTENSION_SLOTS_CREATE_SQL,\n EXTENSION_SLOTS_CREATE_SQL_PG,\n EXTENSION_SLOTS_BY_SLOT_INDEX_SQL,\n EXTENSION_SLOTS_BY_EXTENSION_INDEX_SQL,\n EXTENSION_SLOTS_UNIQUE_INDEX_SQL,\n EXTENSION_SLOT_INSTALLS_CREATE_SQL,\n EXTENSION_SLOT_INSTALLS_CREATE_SQL_PG,\n EXTENSION_SLOT_INSTALLS_BY_USER_SLOT_INDEX_SQL,\n EXTENSION_SLOT_INSTALLS_UNIQUE_INDEX_SQL,\n} from \"./schema.js\";\n\nconst getDb = createGetDb({\n extensions,\n extensionShares,\n extensionSlots,\n extensionSlotInstalls,\n});\n\nlet _initPromise: Promise<void> | undefined;\n\nexport async function ensureSlotTables(): Promise<void> {\n if (!_initPromise) {\n _initPromise = (async () => {\n const client = getDbExec();\n const pg = isPostgres();\n await client.execute(\n pg ? EXTENSION_SLOTS_CREATE_SQL_PG : EXTENSION_SLOTS_CREATE_SQL,\n );\n await client.execute(EXTENSION_SLOTS_BY_SLOT_INDEX_SQL);\n await client.execute(EXTENSION_SLOTS_BY_EXTENSION_INDEX_SQL);\n await client.execute(EXTENSION_SLOTS_UNIQUE_INDEX_SQL);\n await client.execute(\n pg\n ? EXTENSION_SLOT_INSTALLS_CREATE_SQL_PG\n : EXTENSION_SLOT_INSTALLS_CREATE_SQL,\n );\n await client.execute(EXTENSION_SLOT_INSTALLS_BY_USER_SLOT_INDEX_SQL);\n await client.execute(EXTENSION_SLOT_INSTALLS_UNIQUE_INDEX_SQL);\n })();\n }\n return _initPromise;\n}\n\nexport interface ExtensionSlotRow {\n id: string;\n extensionId: string;\n slotId: string;\n config: string | null;\n createdAt: string;\n}\n\nexport interface ExtensionSlotInstallRow {\n id: string;\n extensionId: string;\n slotId: string;\n ownerEmail: string;\n orgId: string | null;\n position: number;\n config: string | null;\n createdAt: string;\n updatedAt: string;\n}\n\n/**\n * Declare that a extension can render in a slot. Caller must have editor access on\n * the extension (only people who can edit a extension can change its slot targets).\n */\nexport async function addExtensionSlotTarget(\n extensionId: string,\n slotId: string,\n config?: string,\n): Promise<ExtensionSlotRow> {\n await ensureSlotTables();\n await assertAccess(\"extension\", extensionId, \"editor\");\n const db = getDb();\n const id = randomUUID();\n const createdAt = new Date().toISOString();\n const row: ExtensionSlotRow = {\n id,\n extensionId,\n slotId,\n config: config ?? null,\n createdAt,\n };\n try {\n await db.insert(extensionSlots).values(row);\n } catch (err: any) {\n // Unique index hit — already declared. Treat as idempotent: return existing.\n if (\n String(err?.message ?? err)\n .toLowerCase()\n .includes(\"unique\")\n ) {\n const existing = await db\n .select()\n .from(extensionSlots)\n .where(\n and(\n eq(extensionSlots.extensionId, extensionId),\n eq(extensionSlots.slotId, slotId),\n ),\n );\n if (existing[0]) return existing[0] as ExtensionSlotRow;\n }\n throw err;\n }\n return row;\n}\n\nexport async function removeExtensionSlotTarget(\n extensionId: string,\n slotId: string,\n): Promise<boolean> {\n await ensureSlotTables();\n await assertAccess(\"extension\", extensionId, \"editor\");\n const db = getDb();\n await db\n .delete(extensionSlots)\n .where(\n and(\n eq(extensionSlots.extensionId, extensionId),\n eq(extensionSlots.slotId, slotId),\n ),\n );\n return true;\n}\n\nexport async function listSlotsForExtension(\n extensionId: string,\n): Promise<ExtensionSlotRow[]> {\n await ensureSlotTables();\n await assertAccess(\"extension\", extensionId, \"viewer\");\n const db = getDb();\n const rows = await db\n .select()\n .from(extensionSlots)\n .where(eq(extensionSlots.extensionId, extensionId));\n return rows as ExtensionSlotRow[];\n}\n\n/**\n * List extensions that declare a slot — but only extensions the current user has access\n * to. Joins through the extensions access filter.\n */\nexport async function listExtensionsForSlot(slotId: string): Promise<\n Array<{\n extensionId: string;\n name: string;\n description: string;\n icon: string | null;\n config: string | null;\n }>\n> {\n await ensureSlotTables();\n const db = getDb();\n // Pull extensions the user can see, then narrow to ones declaring this slot.\n const accessible = await db\n .select({\n id: extensions.id,\n name: extensions.name,\n description: extensions.description,\n icon: extensions.icon,\n })\n .from(extensions)\n .where(accessFilter(extensions, extensionShares));\n if (accessible.length === 0) return [];\n const ids = accessible.map((t: any) => t.id);\n const declarations = await db\n .select()\n .from(extensionSlots)\n .where(\n and(\n eq(extensionSlots.slotId, slotId),\n inArray(extensionSlots.extensionId, ids),\n ),\n );\n const byId = new Map(accessible.map((t: any) => [t.id, t]));\n return (declarations as ExtensionSlotRow[]).map((d) => {\n const t = byId.get(d.extensionId)!;\n return {\n extensionId: d.extensionId,\n name: t.name,\n description: t.description,\n icon: t.icon,\n config: d.config,\n };\n });\n}\n\n/**\n * Install a extension into a slot for the current user. Verifies the user has at\n * least viewer access to the extension. Idempotent — re-installing returns the\n * existing row.\n */\nexport async function installExtensionSlot(\n extensionId: string,\n slotId: string,\n opts?: { position?: number; config?: string },\n): Promise<ExtensionSlotInstallRow> {\n await ensureSlotTables();\n await assertAccess(\"extension\", extensionId, \"viewer\");\n const userEmail = requireUserEmail();\n const orgId = getRequestOrgId();\n const db = getDb();\n const existing = await db\n .select()\n .from(extensionSlotInstalls)\n .where(\n and(\n eq(extensionSlotInstalls.ownerEmail, userEmail),\n eq(extensionSlotInstalls.extensionId, extensionId),\n eq(extensionSlotInstalls.slotId, slotId),\n ),\n );\n if (existing[0]) return existing[0] as ExtensionSlotInstallRow;\n\n const id = randomUUID();\n const now = new Date().toISOString();\n let position = opts?.position;\n if (position === undefined) {\n const rows = await db\n .select({ pos: sql<number>`MAX(${extensionSlotInstalls.position})` })\n .from(extensionSlotInstalls)\n .where(\n and(\n eq(extensionSlotInstalls.ownerEmail, userEmail),\n eq(extensionSlotInstalls.slotId, slotId),\n ),\n );\n const maxPos = Number((rows[0] as any)?.pos ?? -1);\n position = Number.isFinite(maxPos) ? maxPos + 1 : 0;\n }\n const row: ExtensionSlotInstallRow = {\n id,\n extensionId,\n slotId,\n ownerEmail: userEmail,\n orgId: orgId ?? null,\n position,\n config: opts?.config ?? null,\n createdAt: now,\n updatedAt: now,\n };\n await db.insert(extensionSlotInstalls).values(row);\n return row;\n}\n\nexport async function uninstallExtensionSlot(\n extensionId: string,\n slotId: string,\n): Promise<boolean> {\n await ensureSlotTables();\n const userEmail = requireUserEmail();\n const db = getDb();\n await db\n .delete(extensionSlotInstalls)\n .where(\n and(\n eq(extensionSlotInstalls.ownerEmail, userEmail),\n eq(extensionSlotInstalls.extensionId, extensionId),\n eq(extensionSlotInstalls.slotId, slotId),\n ),\n );\n return true;\n}\n\n/**\n * List the current user's installs for a slot. Joins with `extensions` so the\n * caller gets extension name/description/icon/updatedAt without a second query.\n * Extensions the user has lost access to are silently skipped (lazy garbage\n * collection).\n */\nexport async function listSlotInstallsForUser(slotId: string): Promise<\n Array<{\n installId: string;\n extensionId: string;\n name: string;\n description: string;\n icon: string | null;\n updatedAt: string;\n position: number;\n config: string | null;\n }>\n> {\n await ensureSlotTables();\n const userEmail = requireUserEmail();\n const db = getDb();\n\n const installs = await db\n .select()\n .from(extensionSlotInstalls)\n .where(\n and(\n eq(extensionSlotInstalls.ownerEmail, userEmail),\n eq(extensionSlotInstalls.slotId, slotId),\n ),\n );\n if (installs.length === 0) return [];\n\n const accessible = await db\n .select({\n id: extensions.id,\n name: extensions.name,\n description: extensions.description,\n icon: extensions.icon,\n updatedAt: extensions.updatedAt,\n })\n .from(extensions)\n .where(accessFilter(extensions, extensionShares));\n const byId = new Map(accessible.map((t: any) => [t.id, t]));\n\n return (installs as ExtensionSlotInstallRow[])\n .filter((i) => byId.has(i.extensionId))\n .sort((a, b) => a.position - b.position)\n .map((i) => {\n const t = byId.get(i.extensionId)!;\n return {\n installId: i.id,\n extensionId: i.extensionId,\n name: t.name,\n description: t.description,\n icon: t.icon,\n updatedAt: t.updatedAt,\n position: i.position,\n config: i.config,\n };\n });\n}\n\n/** Delete every slot/install row referencing a extension. Called from deleteExtension. */\nexport async function cascadeDeleteExtensionSlots(\n extensionId: string,\n): Promise<void> {\n await ensureSlotTables();\n const db = getDb();\n await db\n .delete(extensionSlots)\n .where(eq(extensionSlots.extensionId, extensionId));\n await db\n .delete(extensionSlotInstalls)\n .where(eq(extensionSlotInstalls.extensionId, extensionId));\n}\n\nfunction requireUserEmail(): string {\n const email = getRequestUserEmail();\n if (!email) {\n throw new Error(\"Slot operations require an authenticated user.\");\n }\n return email;\n}\n"]}
@@ -0,0 +1,40 @@
1
+ export declare function ensureExtensionsTables(): Promise<void>;
2
+ export declare function registerExtensionsShareable(): void;
3
+ export interface ExtensionRow {
4
+ id: string;
5
+ name: string;
6
+ description: string;
7
+ content: string;
8
+ icon: string | null;
9
+ createdAt: string;
10
+ updatedAt: string;
11
+ ownerEmail: string;
12
+ orgId: string | null;
13
+ visibility: "private" | "org" | "public";
14
+ }
15
+ export declare function listExtensions(): Promise<ExtensionRow[]>;
16
+ export declare function getExtension(id: string): Promise<ExtensionRow | null>;
17
+ export interface CreateExtensionData {
18
+ name: string;
19
+ description?: string;
20
+ content?: string;
21
+ icon?: string;
22
+ }
23
+ export declare function createExtension(data: CreateExtensionData): Promise<ExtensionRow>;
24
+ export interface UpdateExtensionData {
25
+ name?: string;
26
+ description?: string;
27
+ icon?: string;
28
+ visibility?: "private" | "org" | "public";
29
+ }
30
+ export declare function updateExtension(id: string, data: UpdateExtensionData): Promise<ExtensionRow | null>;
31
+ export interface UpdateExtensionContentOpts {
32
+ content?: string;
33
+ patches?: Array<{
34
+ find: string;
35
+ replace: string;
36
+ }>;
37
+ }
38
+ export declare function updateExtensionContent(id: string, opts: UpdateExtensionContentOpts): Promise<ExtensionRow | null>;
39
+ export declare function deleteExtension(id: string): Promise<boolean>;
40
+ //# sourceMappingURL=store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/extensions/store.ts"],"names":[],"mappings":"AAuCA,wBAAsB,sBAAsB,IAAI,OAAO,CAAC,IAAI,CAAC,CAmD5D;AA4DD,wBAAgB,2BAA2B,SAS1C;AAED,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,UAAU,EAAE,SAAS,GAAG,KAAK,GAAG,QAAQ,CAAC;CAC1C;AAED,wBAAsB,cAAc,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC,CAS9D;AAED,wBAAsB,YAAY,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAI3E;AAED,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,wBAAsB,eAAe,CACnC,IAAI,EAAE,mBAAmB,GACxB,OAAO,CAAC,YAAY,CAAC,CA2BvB;AAED,MAAM,WAAW,mBAAmB;IAClC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,SAAS,GAAG,KAAK,GAAG,QAAQ,CAAC;CAC3C;AAED,wBAAsB,eAAe,CACnC,EAAE,EAAE,MAAM,EACV,IAAI,EAAE,mBAAmB,GACxB,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAc9B;AAED,MAAM,WAAW,0BAA0B;IACzC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACpD;AAED,wBAAsB,sBAAsB,CAC1C,EAAE,EAAE,MAAM,EACV,IAAI,EAAE,0BAA0B,GAC/B,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CA4B9B;AAED,wBAAsB,eAAe,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAelE"}