@databricks/appkit 0.64.0 → 0.66.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.
- package/CLAUDE.md +19 -0
- package/dist/appkit/package.js +1 -1
- package/dist/beta.d.ts +9 -1
- package/dist/beta.js +6 -1
- package/dist/cli/commands/doctor/resolve-targets.js +1 -1
- package/dist/cli/commands/generate-types.js +12 -3
- package/dist/cli/commands/generate-types.js.map +1 -1
- package/dist/cli/commands/plugin/create/scaffold.js +2 -2
- package/dist/cli/commands/plugin/create/scaffold.js.map +1 -1
- package/dist/core/appkit.js +1 -1
- package/dist/database/contract/index.js +3 -0
- package/dist/database/contract/registry.d.ts +26 -0
- package/dist/database/contract/registry.d.ts.map +1 -0
- package/dist/database/contract/wire.d.ts +8 -0
- package/dist/database/contract/wire.d.ts.map +1 -0
- package/dist/database/contract/wire.js +37 -0
- package/dist/database/contract/wire.js.map +1 -0
- package/dist/database/errors.js +71 -0
- package/dist/database/errors.js.map +1 -0
- package/dist/database/runtime/data-path.js +39 -0
- package/dist/database/runtime/data-path.js.map +1 -0
- package/dist/database/runtime/engine/drizzle-data-path.js +194 -0
- package/dist/database/runtime/engine/drizzle-data-path.js.map +1 -0
- package/dist/database/runtime/engine/translate.js +176 -0
- package/dist/database/runtime/engine/translate.js.map +1 -0
- package/dist/database/schema-builder/columns.d.ts +39 -0
- package/dist/database/schema-builder/columns.d.ts.map +1 -0
- package/dist/database/schema-builder/columns.js +162 -0
- package/dist/database/schema-builder/columns.js.map +1 -0
- package/dist/database/schema-builder/define-schema.d.ts +12 -0
- package/dist/database/schema-builder/define-schema.d.ts.map +1 -0
- package/dist/database/schema-builder/define-schema.js +207 -0
- package/dist/database/schema-builder/define-schema.js.map +1 -0
- package/dist/database/schema-builder/engine/relations.js +37 -0
- package/dist/database/schema-builder/engine/relations.js.map +1 -0
- package/dist/database/schema-builder/engine/tables.js +124 -0
- package/dist/database/schema-builder/engine/tables.js.map +1 -0
- package/dist/database/schema-builder/fk.d.ts +9 -0
- package/dist/database/schema-builder/fk.d.ts.map +1 -0
- package/dist/database/schema-builder/fk.js +71 -0
- package/dist/database/schema-builder/fk.js.map +1 -0
- package/dist/database/schema-builder/index.js +6 -0
- package/dist/database/schema-builder/relations.js +51 -0
- package/dist/database/schema-builder/relations.js.map +1 -0
- package/dist/database/schema-builder/types.d.ts +121 -0
- package/dist/database/schema-builder/types.d.ts.map +1 -0
- package/dist/database/schema-builder/types.js +43 -0
- package/dist/database/schema-builder/types.js.map +1 -0
- package/dist/database/schema-builder/validators.js +53 -0
- package/dist/database/schema-builder/validators.js.map +1 -0
- package/dist/index.d.ts +13 -12
- package/dist/index.js +2 -2
- package/dist/plugin/plugin.d.ts +2 -2
- package/dist/plugins/agents/agents.d.ts +3 -3
- package/dist/plugins/agents/agents.d.ts.map +1 -1
- package/dist/plugins/agents/agents.js +3 -1
- package/dist/plugins/agents/agents.js.map +1 -1
- package/dist/plugins/ai-search/ai-search.d.ts +1 -1
- package/dist/plugins/ai-search/ai-search.d.ts.map +1 -1
- package/dist/plugins/ai-search/ai-search.js +3 -1
- package/dist/plugins/ai-search/ai-search.js.map +1 -1
- package/dist/plugins/analytics/analytics.d.ts +2 -2
- package/dist/plugins/analytics/analytics.d.ts.map +1 -1
- package/dist/plugins/analytics/analytics.js +3 -1
- package/dist/plugins/analytics/analytics.js.map +1 -1
- package/dist/plugins/beta-exports.generated.d.ts +3 -1
- package/dist/plugins/beta-exports.generated.js +2 -0
- package/dist/plugins/database/database.d.ts +37 -0
- package/dist/plugins/database/database.d.ts.map +1 -0
- package/dist/plugins/database/database.js +68 -0
- package/dist/plugins/database/database.js.map +1 -0
- package/dist/plugins/database/defaults.js +12 -0
- package/dist/plugins/database/defaults.js.map +1 -0
- package/dist/plugins/database/entity-client.js +148 -0
- package/dist/plugins/database/entity-client.js.map +1 -0
- package/dist/plugins/database/entity-types.d.ts +76 -0
- package/dist/plugins/database/entity-types.d.ts.map +1 -0
- package/dist/plugins/database/index.d.ts +3 -0
- package/dist/plugins/database/index.js +3 -0
- package/dist/plugins/database/lifecycle.js +107 -0
- package/dist/plugins/database/lifecycle.js.map +1 -0
- package/dist/plugins/database/manifest.js +84 -0
- package/dist/plugins/database/manifest.js.map +1 -0
- package/dist/plugins/database/types.d.ts +10 -0
- package/dist/plugins/database/types.d.ts.map +1 -0
- package/dist/plugins/files/plugin.d.ts +5 -4
- package/dist/plugins/files/plugin.d.ts.map +1 -1
- package/dist/plugins/files/plugin.js +2 -1
- package/dist/plugins/files/plugin.js.map +1 -1
- package/dist/plugins/genie/genie.d.ts +2 -2
- package/dist/plugins/genie/genie.d.ts.map +1 -1
- package/dist/plugins/genie/genie.js +3 -1
- package/dist/plugins/genie/genie.js.map +1 -1
- package/dist/plugins/jobs/plugin.d.ts +3 -2
- package/dist/plugins/jobs/plugin.d.ts.map +1 -1
- package/dist/plugins/jobs/plugin.js +2 -1
- package/dist/plugins/jobs/plugin.js.map +1 -1
- package/dist/plugins/lakebase/lakebase.d.ts +4 -4
- package/dist/plugins/lakebase/lakebase.d.ts.map +1 -1
- package/dist/plugins/lakebase/lakebase.js +3 -1
- package/dist/plugins/lakebase/lakebase.js.map +1 -1
- package/dist/plugins/server/index.d.ts +1 -1
- package/dist/plugins/server/index.d.ts.map +1 -1
- package/dist/plugins/server/index.js +3 -1
- package/dist/plugins/server/index.js.map +1 -1
- package/dist/plugins/serving/serving.d.ts +1 -0
- package/dist/plugins/serving/serving.d.ts.map +1 -1
- package/dist/plugins/serving/serving.js +2 -1
- package/dist/plugins/serving/serving.js.map +1 -1
- package/dist/plugins/ui-variants/index.js +3 -1
- package/dist/plugins/ui-variants/index.js.map +1 -1
- package/dist/registry/index.d.ts +1 -1
- package/dist/registry/index.js +1 -1
- package/dist/registry/manifest-loader.d.ts +20 -1
- package/dist/registry/manifest-loader.d.ts.map +1 -1
- package/dist/registry/manifest-loader.js +23 -1
- package/dist/registry/manifest-loader.js.map +1 -1
- package/dist/schemas/manifest.d.ts +1 -1
- package/dist/shared/src/index.d.ts +1 -1
- package/dist/shared/src/naming.js +13 -1
- package/dist/shared/src/naming.js.map +1 -1
- package/dist/shared/src/schemas/manifest.d.ts +1 -1
- package/dist/shared/src/schemas/manifest.js +463 -0
- package/dist/shared/src/schemas/manifest.js.map +1 -0
- package/dist/testing/fixtures.d.ts +1 -1
- package/dist/type-generator/database/generate.d.ts +15 -0
- package/dist/type-generator/database/generate.d.ts.map +1 -0
- package/dist/type-generator/database/generate.js +104 -0
- package/dist/type-generator/database/generate.js.map +1 -0
- package/dist/type-generator/database/index.js +3 -0
- package/dist/type-generator/database/walk-schema.js +65 -0
- package/dist/type-generator/database/walk-schema.js.map +1 -0
- package/dist/type-generator/index.d.ts +148 -0
- package/dist/type-generator/index.d.ts.map +1 -0
- package/dist/type-generator/index.js +3 -1
- package/dist/type-generator/index.js.map +1 -1
- package/dist/type-generator/mv-registry/types.d.ts +99 -0
- package/dist/type-generator/mv-registry/types.d.ts.map +1 -0
- package/dist/type-generator/preflight.d.ts +16 -0
- package/dist/type-generator/preflight.d.ts.map +1 -0
- package/dist/type-generator/serving/generator.d.ts +20 -0
- package/dist/type-generator/serving/generator.d.ts.map +1 -0
- package/dist/type-generator/types.d.ts +72 -0
- package/dist/type-generator/types.d.ts.map +1 -0
- package/dist/type-generator/vite-plugin.d.ts +1 -0
- package/dist/type-generator/vite-plugin.d.ts.map +1 -1
- package/dist/type-generator/vite-plugin.js +41 -8
- package/dist/type-generator/vite-plugin.js.map +1 -1
- package/dist/type-generator/warehouse-status.d.ts +1 -0
- package/docs/api/appkit/Function.bigid.md +10 -0
- package/docs/api/appkit/Function.bigint.md +10 -0
- package/docs/api/appkit/Function.boolean.md +10 -0
- package/docs/api/appkit/Function.database.md +56 -0
- package/docs/api/appkit/Function.defineManifest.md +32 -0
- package/docs/api/appkit/Function.defineSchema.md +17 -0
- package/docs/api/appkit/Function.enumColumn.md +17 -0
- package/docs/api/appkit/Function.fk.md +18 -0
- package/docs/api/appkit/Function.id.md +10 -0
- package/docs/api/appkit/Function.integer.md +10 -0
- package/docs/api/appkit/Function.jsonb.md +10 -0
- package/docs/api/appkit/Function.text.md +10 -0
- package/docs/api/appkit/Function.timestamp.md +19 -0
- package/docs/api/appkit/Function.uuid.md +10 -0
- package/docs/api/appkit/Function.varchar.md +16 -0
- package/docs/api/appkit/Interface.DatabaseRegistry.md +3 -0
- package/docs/api/appkit/Interface.Schema.md +28 -0
- package/docs/api/appkit/TypeAlias.DatabaseExports.md +35 -0
- package/docs/api/appkit/TypeAlias.IDatabaseConfig.md +25 -0
- package/docs/api/appkit/Variable.agents.md +1 -1
- package/docs/api/appkit.md +19 -0
- package/llms.txt +19 -0
- package/package.json +4 -3
- package/sbom.cdx.json +1 -1
package/CLAUDE.md
CHANGED
|
@@ -83,15 +83,23 @@ npx @databricks/appkit docs <query>
|
|
|
83
83
|
- [Function: agentIdFromMarkdownPath()](./docs/api/appkit/Function.agentIdFromMarkdownPath.md): Derives the logical agent id from a markdown path. When the file is named
|
|
84
84
|
- [Function: appKitServingTypesPlugin()](./docs/api/appkit/Function.appKitServingTypesPlugin.md): Vite plugin to generate TypeScript types for AppKit serving endpoints.
|
|
85
85
|
- [Function: appKitTypesPlugin()](./docs/api/appkit/Function.appKitTypesPlugin.md): Vite plugin to generate types for AppKit queries.
|
|
86
|
+
- [Function: bigid()](./docs/api/appkit/Function.bigid.md): Returns
|
|
87
|
+
- [Function: bigint()](./docs/api/appkit/Function.bigint.md): Returns
|
|
88
|
+
- [Function: boolean()](./docs/api/appkit/Function.boolean.md): Returns
|
|
86
89
|
- [Function: createAgent()](./docs/api/appkit/Function.createAgent.md): Pure factory for agent definitions: cycle-detects the sub-agent graph and
|
|
87
90
|
- [Function: createApp()](./docs/api/appkit/Function.createApp.md): Bootstraps AppKit with the provided configuration.
|
|
88
91
|
- [Function: createLakebasePool()](./docs/api/appkit/Function.createLakebasePool.md): Create a Lakebase pool with appkit's logger integration.
|
|
89
92
|
- [Function: createLakebasePoolManager()](./docs/api/appkit/Function.createLakebasePoolManager.md): Create a pool manager that maintains per-key Lakebase connection pools.
|
|
90
93
|
- [Function: createWorkspaceClient()](./docs/api/appkit/Function.createWorkspaceClient.md): Construct an AppKit workspace client.
|
|
94
|
+
- [Function: database()](./docs/api/appkit/Function.database.md): Create a typed database plugin registration for a finalized schema.
|
|
95
|
+
- [Function: defineManifest()](./docs/api/appkit/Function.defineManifest.md): Validates a raw manifest (typically a manifest.json import) against the
|
|
96
|
+
- [Function: defineSchema()](./docs/api/appkit/Function.defineSchema.md): Parameters
|
|
91
97
|
- [Function: defineTool()](./docs/api/appkit/Function.defineTool.md): Defines a single tool entry for a plugin's internal registry.
|
|
98
|
+
- [Function: enumColumn()](./docs/api/appkit/Function.enumColumn.md): Parameters
|
|
92
99
|
- [Function: executeFromRegistry()](./docs/api/appkit/Function.executeFromRegistry.md): Validates tool-call arguments against the entry's schema and invokes its
|
|
93
100
|
- [Function: extractServingEndpoints()](./docs/api/appkit/Function.extractServingEndpoints.md): Extract serving endpoint config from a server file by AST-parsing it.
|
|
94
101
|
- [Function: findServerFile()](./docs/api/appkit/Function.findServerFile.md): Find the server entry file by checking candidate paths in order.
|
|
102
|
+
- [Function: fk()](./docs/api/appkit/Function.fk.md): Declare foreign-key to another column.
|
|
95
103
|
- [Function: fromSupervisorApi()](./docs/api/appkit/Function.fromSupervisorApi.md): Creates an AgentAdapter backed by the Databricks AI Gateway
|
|
96
104
|
- [Function: functionToolToDefinition()](./docs/api/appkit/Function.functionToolToDefinition.md): Parameters
|
|
97
105
|
- [Function: generateDatabaseCredential()](./docs/api/appkit/Function.generateDatabaseCredential.md): Generate OAuth credentials for Postgres database connection using the proper Postgres API.
|
|
@@ -102,19 +110,26 @@ npx @databricks/appkit docs <query>
|
|
|
102
110
|
- [Function: getResourceRequirements()](./docs/api/appkit/Function.getResourceRequirements.md): Gets the resource requirements from a plugin's manifest.
|
|
103
111
|
- [Function: getUsernameWithApiLookup()](./docs/api/appkit/Function.getUsernameWithApiLookup.md): Resolves the PostgreSQL username for a Lakebase connection.
|
|
104
112
|
- [Function: getWorkspaceClient()](./docs/api/appkit/Function.getWorkspaceClient.md): Get workspace client from config or SDK default auth chain
|
|
113
|
+
- [Function: id()](./docs/api/appkit/Function.id.md): Returns
|
|
114
|
+
- [Function: integer()](./docs/api/appkit/Function.integer.md): Returns
|
|
105
115
|
- [Function: isFunctionTool()](./docs/api/appkit/Function.isFunctionTool.md): Parameters
|
|
106
116
|
- [Function: isHostedTool()](./docs/api/appkit/Function.isHostedTool.md): Parameters
|
|
107
117
|
- [Function: isSQLTypeMarker()](./docs/api/appkit/Function.isSQLTypeMarker.md): Type guard to check if a value is a SQL type marker
|
|
108
118
|
- [Function: isSupervisorTool()](./docs/api/appkit/Function.isSupervisorTool.md): Type guard for HostedSupervisorTool. Used by the agents plugin
|
|
109
119
|
- [Function: isToolkitEntry()](./docs/api/appkit/Function.isToolkitEntry.md): Type guard for ToolkitEntry — used by the agents plugin to differentiate
|
|
120
|
+
- [Function: jsonb()](./docs/api/appkit/Function.jsonb.md): Returns
|
|
110
121
|
- [Function: loadAgentFromFile()](./docs/api/appkit/Function.loadAgentFromFile.md): Loads a single markdown agent file and resolves its frontmatter against
|
|
111
122
|
- [Function: loadAgentsFromDir()](./docs/api/appkit/Function.loadAgentsFromDir.md): Scans a directory for one subdirectory per agent, each containing
|
|
112
123
|
- [Function: mcpServer()](./docs/api/appkit/Function.mcpServer.md): Factory for declaring a custom MCP server tool.
|
|
113
124
|
- [Function: parseTextToolCalls()](./docs/api/appkit/Function.parseTextToolCalls.md): Parses text-based tool calls from model output.
|
|
114
125
|
- [Function: resolveHostedTools()](./docs/api/appkit/Function.resolveHostedTools.md): Parameters
|
|
115
126
|
- [Function: runAgent()](./docs/api/appkit/Function.runAgent.md): Standalone agent execution without createApp. Resolves the adapter, binds
|
|
127
|
+
- [Function: text()](./docs/api/appkit/Function.text.md): Returns
|
|
128
|
+
- [Function: timestamp()](./docs/api/appkit/Function.timestamp.md): Parameters
|
|
116
129
|
- [Function: tool()](./docs/api/appkit/Function.tool.md): Factory for defining function tools with Zod schemas.
|
|
117
130
|
- [Function: toolsFromRegistry()](./docs/api/appkit/Function.toolsFromRegistry.md): Produces the AgentToolDefinition[] a ToolProvider exposes to the LLM,
|
|
131
|
+
- [Function: uuid()](./docs/api/appkit/Function.uuid.md): Returns
|
|
132
|
+
- [Function: varchar()](./docs/api/appkit/Function.varchar.md): Parameters
|
|
118
133
|
- [Interface: AgentAdapter](./docs/api/appkit/Interface.AgentAdapter.md): Properties
|
|
119
134
|
- [Interface: AgentDefinition](./docs/api/appkit/Interface.AgentDefinition.md): Properties
|
|
120
135
|
- [Interface: AgentInput](./docs/api/appkit/Interface.AgentInput.md): Properties
|
|
@@ -125,6 +140,7 @@ npx @databricks/appkit docs <query>
|
|
|
125
140
|
- [Interface: BasePluginConfig](./docs/api/appkit/Interface.BasePluginConfig.md): Base configuration interface for AppKit plugins
|
|
126
141
|
- [Interface: CacheConfig](./docs/api/appkit/Interface.CacheConfig.md): Configuration for the CacheInterceptor. Controls TTL, size limits, storage backend, and probabilistic cleanup.
|
|
127
142
|
- [Interface: DatabaseCredential](./docs/api/appkit/Interface.DatabaseCredential.md): Database credentials with OAuth token for Postgres connection
|
|
143
|
+
- [Interface: DatabaseRegistry](./docs/api/appkit/Interface.DatabaseRegistry.md): CANONICAL augmentation target. Empty by default; the generated database.d.ts
|
|
128
144
|
- [Interface: EndpointConfig](./docs/api/appkit/Interface.EndpointConfig.md): Properties
|
|
129
145
|
- [Interface: FilePolicyUser](./docs/api/appkit/Interface.FilePolicyUser.md): Minimal user identity passed to the policy function.
|
|
130
146
|
- [Interface: FileResource](./docs/api/appkit/Interface.FileResource.md): Describes the file or directory being acted upon.
|
|
@@ -155,6 +171,7 @@ npx @databricks/appkit docs <query>
|
|
|
155
171
|
- [Interface: ResourceRequirement](./docs/api/appkit/Interface.ResourceRequirement.md): Declares a resource requirement for a plugin.
|
|
156
172
|
- [Interface: RunAgentInput](./docs/api/appkit/Interface.RunAgentInput.md): Properties
|
|
157
173
|
- [Interface: RunAgentResult](./docs/api/appkit/Interface.RunAgentResult.md): Properties
|
|
174
|
+
- [Interface: Schema](./docs/api/appkit/Interface.Schema.md): Properties
|
|
158
175
|
- [Interface: SearchRequest](./docs/api/appkit/Interface.SearchRequest.md): Properties
|
|
159
176
|
- [Interface: SearchResponse<T>](./docs/api/appkit/Interface.SearchResponse.md): Type Parameters
|
|
160
177
|
- [Interface: SearchResult<T>](./docs/api/appkit/Interface.SearchResult.md): Type Parameters
|
|
@@ -182,11 +199,13 @@ npx @databricks/appkit docs <query>
|
|
|
182
199
|
- [Type Alias: AgentToolsFn()](./docs/api/appkit/TypeAlias.AgentToolsFn.md): Function form of AgentDefinition.tools. Receives the typed
|
|
183
200
|
- [Type Alias: BaseSystemPromptOption](./docs/api/appkit/TypeAlias.BaseSystemPromptOption.md)
|
|
184
201
|
- [Type Alias: ConfigSchema](./docs/api/appkit/TypeAlias.ConfigSchema.md): Configuration schema definition for plugin config.
|
|
202
|
+
- [Type Alias: DatabaseExports](./docs/api/appkit/TypeAlias.DatabaseExports.md): Typed database API published by the plugin.
|
|
185
203
|
- [Type Alias: ExecutionResult<T>](./docs/api/appkit/TypeAlias.ExecutionResult.md): Discriminated union for plugin execution results.
|
|
186
204
|
- [Type Alias: FileAction](./docs/api/appkit/TypeAlias.FileAction.md): Every action the files plugin can perform.
|
|
187
205
|
- [Type Alias: FilePolicy()](./docs/api/appkit/TypeAlias.FilePolicy.md): A policy function that decides whether user may perform action on
|
|
188
206
|
- [Type Alias: HostedTool](./docs/api/appkit/TypeAlias.HostedTool.md)
|
|
189
207
|
- [Type Alias: IAppRouter](./docs/api/appkit/TypeAlias.IAppRouter.md): Express router type for plugin route registration
|
|
208
|
+
- [Type Alias: IDatabaseConfig<TSchema>](./docs/api/appkit/TypeAlias.IDatabaseConfig.md): Configuration for one schema-bound DatabasePlugin instance.
|
|
190
209
|
- [Type Alias: JobsExport()](./docs/api/appkit/TypeAlias.JobsExport.md): Public API shape of the jobs plugin.
|
|
191
210
|
- [Type Alias: PluginData<T, U, N>](./docs/api/appkit/TypeAlias.PluginData.md): Tuple of plugin class, config, and name. Created by toPlugin() and passed to createApp().
|
|
192
211
|
- [Type Alias: Plugins](./docs/api/appkit/TypeAlias.Plugins.md): Plugin map passed to the function form of AgentDefinition.tools.
|
package/dist/appkit/package.js
CHANGED
package/dist/beta.d.ts
CHANGED
|
@@ -11,10 +11,18 @@ import { RunAgentInput, RunAgentResult, runAgent } from "./core/agent/run-agent.
|
|
|
11
11
|
import { ToolEntry, ToolRegistry, defineTool, executeFromRegistry, toolsFromRegistry } from "./core/agent/tools/define-tool.js";
|
|
12
12
|
import { ToolConfig, tool } from "./core/agent/tools/tool.js";
|
|
13
13
|
import "./core/agent/tools/index.js";
|
|
14
|
+
import { Schema } from "./database/schema-builder/types.js";
|
|
15
|
+
import { bigid, bigint, boolean, enumColumn, id, integer, jsonb, text, timestamp, uuid, varchar } from "./database/schema-builder/columns.js";
|
|
16
|
+
import { defineSchema } from "./database/schema-builder/define-schema.js";
|
|
17
|
+
import { fk } from "./database/schema-builder/fk.js";
|
|
14
18
|
import { agentIdFromMarkdownPath, loadAgentFromFile, loadAgentsFromDir } from "./core/agent/load-agents.js";
|
|
15
19
|
import { agents } from "./plugins/agents/agents.js";
|
|
16
20
|
import "./plugins/agents/index.js";
|
|
17
21
|
import { IAiSearchConfig, IndexConfig, RerankerConfig, SearchFilters, SearchRequest, SearchResponse, SearchResult } from "./plugins/ai-search/types.js";
|
|
18
22
|
import { aiSearch } from "./plugins/ai-search/ai-search.js";
|
|
23
|
+
import { DatabaseExports } from "./plugins/database/entity-types.js";
|
|
24
|
+
import { IDatabaseConfig } from "./plugins/database/types.js";
|
|
25
|
+
import { database } from "./plugins/database/database.js";
|
|
26
|
+
import "./plugins/database/index.js";
|
|
19
27
|
import "./plugins/beta-exports.generated.js";
|
|
20
|
-
export { type AgentAdapter, type AgentDefinition, type AgentEvent, type AgentInput, type AgentRunContext, type AgentTool, type AgentToolDefinition, type AgentTools, type AgentToolsFn, type AgentsPluginConfig, AppKitMcpClient, type AutoInheritToolsConfig, type BaseSystemPromptOption, DatabricksAdapter, type FunctionTool, type GenerationParams, type HostedSupervisorTool, type HostedTool, type IAiSearchConfig, type IndexConfig, type McpConnectAllResult, type Message, type PluginToolkitProvider, type Plugins, type PromptContext, type RegisteredAgent, type RerankerConfig, type ResolvedToolEntry, type RunAgentInput, type RunAgentResult, SUPERVISOR_EXTENSION_KEY, type SearchFilters, type SearchRequest, type SearchResponse, type SearchResult, SupervisorApiAdapter, type SupervisorApiAdapterOptions, type SupervisorExtension, type SupervisorTool, type Thread, type ThreadStore, type ToolAnnotations, type ToolConfig, type ToolEntry, type ToolProvider, type ToolRegistry, type ToolkitEntry, type ToolkitOptions, type WorkspaceClientLike, agentIdFromMarkdownPath, agents, aiSearch, createAgent, defineTool, executeFromRegistry, fromSupervisorApi, functionToolToDefinition, isFunctionTool, isHostedTool, isSupervisorTool, isToolkitEntry, loadAgentFromFile, loadAgentsFromDir, mcpServer, parseTextToolCalls, resolveHostedTools, runAgent, supervisorTools, tool, toolsFromRegistry };
|
|
28
|
+
export { type AgentAdapter, type AgentDefinition, type AgentEvent, type AgentInput, type AgentRunContext, type AgentTool, type AgentToolDefinition, type AgentTools, type AgentToolsFn, type AgentsPluginConfig, AppKitMcpClient, type AutoInheritToolsConfig, type BaseSystemPromptOption, type DatabaseExports, DatabricksAdapter, type FunctionTool, type GenerationParams, type HostedSupervisorTool, type HostedTool, type IAiSearchConfig, type IDatabaseConfig, type IndexConfig, type McpConnectAllResult, type Message, type PluginToolkitProvider, type Plugins, type PromptContext, type RegisteredAgent, type RerankerConfig, type ResolvedToolEntry, type RunAgentInput, type RunAgentResult, SUPERVISOR_EXTENSION_KEY, type Schema, type SearchFilters, type SearchRequest, type SearchResponse, type SearchResult, SupervisorApiAdapter, type SupervisorApiAdapterOptions, type SupervisorExtension, type SupervisorTool, type Thread, type ThreadStore, type ToolAnnotations, type ToolConfig, type ToolEntry, type ToolProvider, type ToolRegistry, type ToolkitEntry, type ToolkitOptions, type WorkspaceClientLike, agentIdFromMarkdownPath, agents, aiSearch, bigid, bigint, boolean, createAgent, database, defineSchema, defineTool, enumColumn, executeFromRegistry, fk, fromSupervisorApi, functionToolToDefinition, id, integer, isFunctionTool, isHostedTool, isSupervisorTool, isToolkitEntry, jsonb, loadAgentFromFile, loadAgentsFromDir, mcpServer, parseTextToolCalls, resolveHostedTools, runAgent, supervisorTools, text, timestamp, tool, toolsFromRegistry, uuid, varchar };
|
package/dist/beta.js
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { AppKitMcpClient } from "./connectors/mcp/client.js";
|
|
2
2
|
import { tool } from "./core/agent/tools/tool.js";
|
|
3
3
|
import { defineTool, executeFromRegistry, toolsFromRegistry } from "./core/agent/tools/define-tool.js";
|
|
4
|
+
import { bigid, bigint, boolean, enumColumn, id, integer, jsonb, text, timestamp, uuid, varchar } from "./database/schema-builder/columns.js";
|
|
5
|
+
import { fk } from "./database/schema-builder/fk.js";
|
|
6
|
+
import { defineSchema } from "./database/schema-builder/define-schema.js";
|
|
4
7
|
import { DatabricksAdapter, parseTextToolCalls } from "./agents/databricks.js";
|
|
5
8
|
import { SUPERVISOR_EXTENSION_KEY, SupervisorApiAdapter, fromSupervisorApi, isSupervisorTool, supervisorTools } from "./agents/supervisor-api.js";
|
|
6
9
|
import { createAgent } from "./core/agent/create-agent.js";
|
|
@@ -9,10 +12,12 @@ import { isHostedTool, mcpServer, resolveHostedTools } from "./core/agent/tools/
|
|
|
9
12
|
import { isToolkitEntry } from "./core/agent/types.js";
|
|
10
13
|
import { runAgent } from "./core/agent/run-agent.js";
|
|
11
14
|
import "./core/agent/tools/index.js";
|
|
15
|
+
import "./database/schema-builder/index.js";
|
|
12
16
|
import { agentIdFromMarkdownPath, loadAgentFromFile, loadAgentsFromDir } from "./core/agent/load-agents.js";
|
|
13
17
|
import { agents } from "./plugins/agents/agents.js";
|
|
14
18
|
import "./plugins/agents/index.js";
|
|
15
19
|
import { aiSearch } from "./plugins/ai-search/ai-search.js";
|
|
20
|
+
import { database } from "./plugins/database/database.js";
|
|
16
21
|
import "./plugins/beta-exports.generated.js";
|
|
17
22
|
|
|
18
|
-
export { AppKitMcpClient, DatabricksAdapter, SUPERVISOR_EXTENSION_KEY, SupervisorApiAdapter, agentIdFromMarkdownPath, agents, aiSearch, createAgent, defineTool, executeFromRegistry, fromSupervisorApi, functionToolToDefinition, isFunctionTool, isHostedTool, isSupervisorTool, isToolkitEntry, loadAgentFromFile, loadAgentsFromDir, mcpServer, parseTextToolCalls, resolveHostedTools, runAgent, supervisorTools, tool, toolsFromRegistry };
|
|
23
|
+
export { AppKitMcpClient, DatabricksAdapter, SUPERVISOR_EXTENSION_KEY, SupervisorApiAdapter, agentIdFromMarkdownPath, agents, aiSearch, bigid, bigint, boolean, createAgent, database, defineSchema, defineTool, enumColumn, executeFromRegistry, fk, fromSupervisorApi, functionToolToDefinition, id, integer, isFunctionTool, isHostedTool, isSupervisorTool, isToolkitEntry, jsonb, loadAgentFromFile, loadAgentsFromDir, mcpServer, parseTextToolCalls, resolveHostedTools, runAgent, supervisorTools, text, timestamp, tool, toolsFromRegistry, uuid, varchar };
|
|
@@ -30,9 +30,9 @@ async function runGenerateTypes(rootDir, outFile, warehouseId, options) {
|
|
|
30
30
|
const noCache = options?.noCache || false;
|
|
31
31
|
const mode = resolveTypegenMode(options);
|
|
32
32
|
const typeGen = await import("@databricks/appkit/type-generator");
|
|
33
|
+
const resolvedOutFile = outFile || path.join(process.cwd(), "shared/appkit-types/analytics.d.ts");
|
|
33
34
|
const resolvedWarehouseId = warehouseId || process.env.DATABRICKS_WAREHOUSE_ID;
|
|
34
35
|
if (resolvedWarehouseId) {
|
|
35
|
-
const resolvedOutFile = outFile || path.join(process.cwd(), "shared/appkit-types/analytics.d.ts");
|
|
36
36
|
const queryFolder = path.join(resolvedRootDir, "config/queries");
|
|
37
37
|
const metricViewsFolder = path.join(resolvedRootDir, "config/metric-views");
|
|
38
38
|
const hasQueries = fs.existsSync(queryFolder);
|
|
@@ -60,13 +60,22 @@ async function runGenerateTypes(rootDir, outFile, warehouseId, options) {
|
|
|
60
60
|
noCache
|
|
61
61
|
});
|
|
62
62
|
console.log(`Generated serving types: ${servingOutFile}`);
|
|
63
|
+
const databaseSchemaFile = path.join(resolvedRootDir, "config/database/schema.ts");
|
|
64
|
+
const databaseOutFile = path.join(path.dirname(resolvedOutFile), typeGen.DATABASE_TYPES_FILE);
|
|
65
|
+
if (fs.existsSync(databaseSchemaFile) || fs.existsSync(databaseOutFile)) {
|
|
66
|
+
await typeGen.generateDatabaseTypes({
|
|
67
|
+
schemaFile: databaseSchemaFile,
|
|
68
|
+
outFile: databaseOutFile
|
|
69
|
+
});
|
|
70
|
+
console.log(`Generated database types: ${databaseOutFile}`);
|
|
71
|
+
}
|
|
63
72
|
} catch (error) {
|
|
64
73
|
if (error instanceof Error && error.message.includes("Cannot find module")) {
|
|
65
74
|
console.error("Error: The 'generate-types' command is only available in @databricks/appkit.");
|
|
66
75
|
console.error("Please install @databricks/appkit to use this command.");
|
|
67
76
|
process.exit(1);
|
|
68
77
|
}
|
|
69
|
-
if (error instanceof Error && (error.name === "TypegenSyntaxError" || error.name === "TypegenFatalError")) {
|
|
78
|
+
if (error instanceof Error && (error.name === "TypegenSyntaxError" || error.name === "TypegenFatalError" || error.name === "DatabaseTypegenError")) {
|
|
70
79
|
console.error(error.message);
|
|
71
80
|
process.exit(1);
|
|
72
81
|
}
|
|
@@ -158,7 +167,7 @@ async function generateTypesAction(rootDir, outFile, warehouseId, options) {
|
|
|
158
167
|
else console.log("Type refresh already in progress, skipping.");
|
|
159
168
|
}
|
|
160
169
|
}
|
|
161
|
-
const generateTypesCommand = new Command("generate-types").description("Generate TypeScript types from
|
|
170
|
+
const generateTypesCommand = new Command("generate-types").description("Generate TypeScript types from AppKit configuration").argument("[rootDir]", "Root directory of the project", process.cwd()).argument("[outFile]", "Output file path", path.join(process.cwd(), "shared/appkit-types/analytics.d.ts")).argument("[warehouseId]", "Databricks warehouse ID").option("--no-cache", "Disable caching for type generation").option("--wait", "Wait for warehouse readiness instead of degrading (use for CI)").addOption(new Option("--worker-lock <path>", "Internal: detached worker lock path").hideHelp()).addHelpText("after", `
|
|
162
171
|
Examples:
|
|
163
172
|
$ appkit generate-types
|
|
164
173
|
$ appkit generate-types . shared/appkit-types/analytics.d.ts
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"generate-types.js","names":[],"sources":["../../../src/cli/commands/generate-types.ts"],"sourcesContent":["import { spawn } from \"node:child_process\";\nimport fs from \"node:fs\";\nimport path from \"node:path\";\n\nimport { Command, Option } from \"commander\";\n\nimport { METRIC_CONFIG_FILE } from \"../../schemas/metric-fqn\";\nimport {\n acquireSpawnLock,\n getSpawnLockPath,\n releaseSpawnLock,\n} from \"./spawn-lock.js\";\n\n/**\n * Resolve the typegen pre-flight mode for the CLI. Defaults to \"non-blocking\" —\n * a one-shot CLI can't describe in the background, so by default it never\n * describes at all: it skips the warehouse probe AND every DESCRIBE, emits\n * best-available types (cache where the SQL hash matches, else `result: unknown`)\n * and returns immediately, never blocking on — or failing because of — a\n * warehouse, even a RUNNING one. Pass `--wait` (commander sets `wait: true`)\n * for a deliberate/CI invocation that should wait for a starting warehouse and\n * fail fast on a stopped one.\n */\nexport function resolveTypegenMode(options?: {\n wait?: boolean;\n}): \"non-blocking\" | \"blocking\" {\n return options?.wait ? \"blocking\" : \"non-blocking\";\n}\n\n/** Options parsed by commander for the generate-types command. */\ninterface GenerateTypesOptions {\n noCache?: boolean;\n wait?: boolean;\n /**\n * Internal: present only on the detached worker invocation. Carries the path\n * of the single-flight lock this worker must release when it finishes. Its\n * presence is what marks an invocation as \"the worker\" — workers always run\n * with `--wait`, so they never spawn another worker (only non-blocking runs\n * spawn), which terminates the recursion.\n */\n workerLock?: string;\n}\n\n/**\n * Generate types command implementation. Runs the library generate (which, in\n * non-blocking mode, writes degraded types and returns immediately). This is the\n * SAME work the worker performs in blocking mode in the background.\n */\nasync function runGenerateTypes(\n rootDir?: string,\n outFile?: string,\n warehouseId?: string,\n options?: GenerateTypesOptions,\n) {\n try {\n const resolvedRootDir = rootDir || process.cwd();\n const noCache = options?.noCache || false;\n const mode = resolveTypegenMode(options);\n\n const typeGen = await import(\"@databricks/appkit/type-generator\");\n\n // Generate analytics query types (requires warehouse ID)\n const resolvedWarehouseId =\n warehouseId || process.env.DATABRICKS_WAREHOUSE_ID;\n\n if (resolvedWarehouseId) {\n const resolvedOutFile =\n outFile ||\n path.join(process.cwd(), \"shared/appkit-types/analytics.d.ts\");\n\n const queryFolder = path.join(resolvedRootDir, \"config/queries\");\n const metricViewsFolder = path.join(\n resolvedRootDir,\n \"config/metric-views\",\n );\n const hasQueries = fs.existsSync(queryFolder);\n const hasMetricViews = fs.existsSync(metricViewsFolder);\n\n // Generate when either config surface exists. Metric-view types are\n // independent of `.sql` queries — an app can declare metric views in\n // `config/metric-views/` without a `config/queries/` folder.\n if (hasQueries || hasMetricViews) {\n await typeGen.generateFromEntryPoint({\n queryFolder: hasQueries ? queryFolder : undefined,\n metricViewsFolder: hasMetricViews ? metricViewsFolder : undefined,\n outFile: resolvedOutFile,\n warehouseId: resolvedWarehouseId,\n noCache,\n mode,\n });\n\n if (hasQueries) {\n console.log(`Generated query types: ${resolvedOutFile}`);\n }\n\n const metricConfig = path.join(metricViewsFolder, METRIC_CONFIG_FILE);\n if (fs.existsSync(metricConfig)) {\n const typesDir = path.dirname(resolvedOutFile);\n console.log(\n `Generated metric types: ${path.join(typesDir, \"metric-views.ts\")}`,\n );\n }\n }\n } else {\n console.error(\n \"Skipping query type generation: no warehouse ID. Set DATABRICKS_WAREHOUSE_ID or pass as argument.\",\n );\n }\n\n // Generate serving endpoint types (no warehouse required)\n const servingOutFile = path.join(\n process.cwd(),\n \"shared/appkit-types/serving.d.ts\",\n );\n await typeGen.generateServingTypes({\n outFile: servingOutFile,\n noCache,\n });\n console.log(`Generated serving types: ${servingOutFile}`);\n } catch (error) {\n if (\n error instanceof Error &&\n error.message.includes(\"Cannot find module\")\n ) {\n console.error(\n \"Error: The 'generate-types' command is only available in @databricks/appkit.\",\n );\n console.error(\"Please install @databricks/appkit to use this command.\");\n process.exit(1);\n }\n // TypegenSyntaxError / TypegenFatalError carry a complete, actionable\n // message (which queries failed and how to debug them). The stack trace\n // points into appkit internals and is noise for app developers, so print\n // only the message and exit non-zero instead of letting it bubble up.\n if (\n error instanceof Error &&\n (error.name === \"TypegenSyntaxError\" ||\n error.name === \"TypegenFatalError\")\n ) {\n console.error(error.message);\n process.exit(1);\n }\n throw error;\n }\n}\n\n/**\n * Spawn the detached blocking worker that refreshes real types in the background\n * after the foreground non-blocking generate has already written degraded types.\n *\n * Re-invokes THIS CLI (`process.execPath` + `process.argv[1]` — the bin entry\n * that launched us) with `generate-types --wait --worker-lock <lockPath>` plus\n * the same positional target options the foreground used, so the worker writes\n * to the same out file / reads the same query folder. The worker is:\n * - `detached: true` + `.unref()` so it outlives this process (install/dev-setup\n * can finish and exit while the worker keeps warming the warehouse).\n * - `stdio: \"ignore\"` so it never holds the parent's pipes open or interleaves\n * output into the install/dev log.\n *\n * Spawning is wrapped so any failure is non-fatal: the caller still has degraded\n * types and exits 0.\n *\n * @param lockPath - the acquired single-flight lock; passed to the worker so it\n * releases the SAME lock when it finishes.\n * @param targets - the foreground's positional args, forwarded verbatim.\n * @returns true if the worker was spawned, false if spawning threw.\n */\nexport function spawnTypegenWorker(\n lockPath: string,\n targets: { rootDir?: string; outFile?: string; warehouseId?: string },\n): boolean {\n // The script the runtime launched us with (the `appkit` bin shim). Re-running\n // it under the same node binary reproduces this exact CLI in the worker.\n const cliEntry = process.argv[1];\n\n // Forward the positionals in declaration order (rootDir, outFile,\n // warehouseId). Stop at the first undefined so we never pass a literal\n // \"undefined\" — commander would treat it as a positional value. (rootDir is\n // effectively always set by commander's default, but guard anyway.)\n const positionals: string[] = [];\n for (const value of [targets.rootDir, targets.outFile, targets.warehouseId]) {\n if (value === undefined) break;\n positionals.push(value);\n }\n\n const args = [\n // Forward the parent's node/loader flags so the worker runs under the same\n // runtime. Critically this carries tsx's `--require`/`--import` when the CLI\n // is run from source (`tsx index.ts …`); without them the worker would be\n // `node index.ts …`, which can't parse TypeScript and dies silently — the\n // degraded types would then never refresh. Empty for the built bin (plain\n // `node bin/appkit.js`), so production behaviour is unchanged.\n ...process.execArgv,\n cliEntry,\n \"generate-types\",\n \"--wait\",\n \"--worker-lock\",\n lockPath,\n ...positionals,\n ];\n\n try {\n const child = spawn(process.execPath, args, {\n detached: true,\n stdio: \"ignore\",\n });\n child.unref();\n return true;\n } catch (error) {\n // Non-fatal: the foreground already wrote degraded types. Log and move on.\n console.error(\n `Could not start background type refresh: ${\n error instanceof Error ? error.message : String(error)\n }`,\n );\n return false;\n }\n}\n\n/**\n * The command action. Orchestrates the non-blocking foreground contract:\n * 1. Run the library generate (writes degraded types immediately in non-blocking\n * mode; does the full blocking lifecycle when this is the worker).\n * 2. If this is a non-blocking, non-worker invocation, try to spawn the detached\n * blocking worker behind the single-flight lock. If the lock is already held\n * by a live worker, skip (single-flight) with a one-line note. Either way the\n * foreground returns normally (exit 0).\n * 3. If this IS the worker (`--worker-lock` present), it ran blocking above and\n * releases the lock here (and via a process-exit guard, so a hard failure /\n * process.exit still frees it).\n */\nasync function generateTypesAction(\n rootDir: string | undefined,\n outFile: string | undefined,\n warehouseId: string | undefined,\n options: GenerateTypesOptions,\n) {\n const isWorker = typeof options.workerLock === \"string\";\n\n // A worker must always free its lock, even if the blocking generate throws or\n // calls process.exit (TypegenFatalError → exit 1). The exit handler covers the\n // process.exit / uncaught paths; the finally covers the normal return.\n if (isWorker && options.workerLock) {\n const lockPath = options.workerLock;\n process.once(\"exit\", () => releaseSpawnLock(lockPath));\n }\n\n try {\n await runGenerateTypes(rootDir, outFile, warehouseId, options);\n } finally {\n if (isWorker && options.workerLock) {\n releaseSpawnLock(options.workerLock);\n }\n }\n\n // Only a non-blocking, non-worker invocation spawns. A worker is always\n // --wait (so resolveTypegenMode → \"blocking\"), which both prevents recursion\n // and means we never get here for a worker.\n if (!isWorker && resolveTypegenMode(options) === \"non-blocking\") {\n const resolvedRootDir = rootDir || process.cwd();\n const lockPath = getSpawnLockPath(resolvedRootDir);\n\n if (acquireSpawnLock(lockPath)) {\n spawnTypegenWorker(lockPath, { rootDir, outFile, warehouseId });\n } else {\n console.log(\"Type refresh already in progress, skipping.\");\n }\n }\n}\n\nexport const generateTypesCommand = new Command(\"generate-types\")\n .description(\"Generate TypeScript types from SQL queries\")\n .argument(\"[rootDir]\", \"Root directory of the project\", process.cwd())\n .argument(\n \"[outFile]\",\n \"Output file path\",\n path.join(process.cwd(), \"shared/appkit-types/analytics.d.ts\"),\n )\n .argument(\"[warehouseId]\", \"Databricks warehouse ID\")\n .option(\"--no-cache\", \"Disable caching for type generation\")\n .option(\n \"--wait\",\n \"Wait for warehouse readiness instead of degrading (use for CI)\",\n )\n // Internal: marks the detached background worker and carries the lock it must\n // release. Hidden from --help; users should never pass it.\n .addOption(\n new Option(\n \"--worker-lock <path>\",\n \"Internal: detached worker lock path\",\n ).hideHelp(),\n )\n .addHelpText(\n \"after\",\n `\nExamples:\n $ appkit generate-types\n $ appkit generate-types . shared/appkit-types/analytics.d.ts\n $ appkit generate-types . shared/appkit-types/analytics.d.ts my-warehouse-id\n $ appkit generate-types --no-cache\n $ appkit generate-types --wait # CI: wait for the warehouse and fail on a cold one`,\n )\n .action(generateTypesAction);\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"generate-types.js","names":[],"sources":["../../../src/cli/commands/generate-types.ts"],"sourcesContent":["import { spawn } from \"node:child_process\";\nimport fs from \"node:fs\";\nimport path from \"node:path\";\n\nimport { Command, Option } from \"commander\";\n\nimport { METRIC_CONFIG_FILE } from \"../../schemas/metric-fqn\";\nimport {\n acquireSpawnLock,\n getSpawnLockPath,\n releaseSpawnLock,\n} from \"./spawn-lock.js\";\n\n/**\n * Resolve the typegen pre-flight mode for the CLI. Defaults to \"non-blocking\" —\n * a one-shot CLI can't describe in the background, so by default it never\n * describes at all: it skips the warehouse probe AND every DESCRIBE, emits\n * best-available types (cache where the SQL hash matches, else `result: unknown`)\n * and returns immediately, never blocking on — or failing because of — a\n * warehouse, even a RUNNING one. Pass `--wait` (commander sets `wait: true`)\n * for a deliberate/CI invocation that should wait for a starting warehouse and\n * fail fast on a stopped one.\n */\nexport function resolveTypegenMode(options?: {\n wait?: boolean;\n}): \"non-blocking\" | \"blocking\" {\n return options?.wait ? \"blocking\" : \"non-blocking\";\n}\n\n/** Options parsed by commander for the generate-types command. */\ninterface GenerateTypesOptions {\n noCache?: boolean;\n wait?: boolean;\n /**\n * Internal: present only on the detached worker invocation. Carries the path\n * of the single-flight lock this worker must release when it finishes. Its\n * presence is what marks an invocation as \"the worker\" — workers always run\n * with `--wait`, so they never spawn another worker (only non-blocking runs\n * spawn), which terminates the recursion.\n */\n workerLock?: string;\n}\n\n/**\n * Generate types command implementation. Runs the library generate (which, in\n * non-blocking mode, writes degraded types and returns immediately). This is the\n * SAME work the worker performs in blocking mode in the background.\n */\nasync function runGenerateTypes(\n rootDir?: string,\n outFile?: string,\n warehouseId?: string,\n options?: GenerateTypesOptions,\n) {\n try {\n const resolvedRootDir = rootDir || process.cwd();\n const noCache = options?.noCache || false;\n const mode = resolveTypegenMode(options);\n\n const typeGen = await import(\"@databricks/appkit/type-generator\");\n const resolvedOutFile =\n outFile || path.join(process.cwd(), \"shared/appkit-types/analytics.d.ts\");\n\n // Generate analytics query types (requires warehouse ID)\n const resolvedWarehouseId =\n warehouseId || process.env.DATABRICKS_WAREHOUSE_ID;\n\n if (resolvedWarehouseId) {\n const queryFolder = path.join(resolvedRootDir, \"config/queries\");\n const metricViewsFolder = path.join(\n resolvedRootDir,\n \"config/metric-views\",\n );\n const hasQueries = fs.existsSync(queryFolder);\n const hasMetricViews = fs.existsSync(metricViewsFolder);\n\n // Generate when either config surface exists. Metric-view types are\n // independent of `.sql` queries — an app can declare metric views in\n // `config/metric-views/` without a `config/queries/` folder.\n if (hasQueries || hasMetricViews) {\n await typeGen.generateFromEntryPoint({\n queryFolder: hasQueries ? queryFolder : undefined,\n metricViewsFolder: hasMetricViews ? metricViewsFolder : undefined,\n outFile: resolvedOutFile,\n warehouseId: resolvedWarehouseId,\n noCache,\n mode,\n });\n\n if (hasQueries) {\n console.log(`Generated query types: ${resolvedOutFile}`);\n }\n\n const metricConfig = path.join(metricViewsFolder, METRIC_CONFIG_FILE);\n if (fs.existsSync(metricConfig)) {\n const typesDir = path.dirname(resolvedOutFile);\n console.log(\n `Generated metric types: ${path.join(typesDir, \"metric-views.ts\")}`,\n );\n }\n }\n } else {\n console.error(\n \"Skipping query type generation: no warehouse ID. Set DATABRICKS_WAREHOUSE_ID or pass as argument.\",\n );\n }\n\n // Generate serving endpoint types (no warehouse required)\n const servingOutFile = path.join(\n process.cwd(),\n \"shared/appkit-types/serving.d.ts\",\n );\n await typeGen.generateServingTypes({\n outFile: servingOutFile,\n noCache,\n });\n console.log(`Generated serving types: ${servingOutFile}`);\n\n // Generate database declarations.\n const databaseSchemaFile = path.join(\n resolvedRootDir,\n \"config/database/schema.ts\",\n );\n const databaseOutFile = path.join(\n path.dirname(resolvedOutFile),\n typeGen.DATABASE_TYPES_FILE,\n );\n if (fs.existsSync(databaseSchemaFile) || fs.existsSync(databaseOutFile)) {\n await typeGen.generateDatabaseTypes({\n schemaFile: databaseSchemaFile,\n outFile: databaseOutFile,\n });\n console.log(`Generated database types: ${databaseOutFile}`);\n }\n } catch (error) {\n if (\n error instanceof Error &&\n error.message.includes(\"Cannot find module\")\n ) {\n console.error(\n \"Error: The 'generate-types' command is only available in @databricks/appkit.\",\n );\n console.error(\"Please install @databricks/appkit to use this command.\");\n process.exit(1);\n }\n // TypegenSyntaxError / TypegenFatalError carry a complete, actionable\n // message (which queries failed and how to debug them). The stack trace\n // points into appkit internals and is noise for app developers, so print\n // only the message and exit non-zero instead of letting it bubble up.\n if (\n error instanceof Error &&\n (error.name === \"TypegenSyntaxError\" ||\n error.name === \"TypegenFatalError\" ||\n error.name === \"DatabaseTypegenError\")\n ) {\n console.error(error.message);\n process.exit(1);\n }\n throw error;\n }\n}\n\n/**\n * Spawn the detached blocking worker that refreshes real types in the background\n * after the foreground non-blocking generate has already written degraded types.\n *\n * Re-invokes THIS CLI (`process.execPath` + `process.argv[1]` — the bin entry\n * that launched us) with `generate-types --wait --worker-lock <lockPath>` plus\n * the same positional target options the foreground used, so the worker writes\n * to the same out file / reads the same query folder. The worker is:\n * - `detached: true` + `.unref()` so it outlives this process (install/dev-setup\n * can finish and exit while the worker keeps warming the warehouse).\n * - `stdio: \"ignore\"` so it never holds the parent's pipes open or interleaves\n * output into the install/dev log.\n *\n * Spawning is wrapped so any failure is non-fatal: the caller still has degraded\n * types and exits 0.\n *\n * @param lockPath - the acquired single-flight lock; passed to the worker so it\n * releases the SAME lock when it finishes.\n * @param targets - the foreground's positional args, forwarded verbatim.\n * @returns true if the worker was spawned, false if spawning threw.\n */\nexport function spawnTypegenWorker(\n lockPath: string,\n targets: { rootDir?: string; outFile?: string; warehouseId?: string },\n): boolean {\n // The script the runtime launched us with (the `appkit` bin shim). Re-running\n // it under the same node binary reproduces this exact CLI in the worker.\n const cliEntry = process.argv[1];\n\n // Forward the positionals in declaration order (rootDir, outFile,\n // warehouseId). Stop at the first undefined so we never pass a literal\n // \"undefined\" — commander would treat it as a positional value. (rootDir is\n // effectively always set by commander's default, but guard anyway.)\n const positionals: string[] = [];\n for (const value of [targets.rootDir, targets.outFile, targets.warehouseId]) {\n if (value === undefined) break;\n positionals.push(value);\n }\n\n const args = [\n // Forward the parent's node/loader flags so the worker runs under the same\n // runtime. Critically this carries tsx's `--require`/`--import` when the CLI\n // is run from source (`tsx index.ts …`); without them the worker would be\n // `node index.ts …`, which can't parse TypeScript and dies silently — the\n // degraded types would then never refresh. Empty for the built bin (plain\n // `node bin/appkit.js`), so production behaviour is unchanged.\n ...process.execArgv,\n cliEntry,\n \"generate-types\",\n \"--wait\",\n \"--worker-lock\",\n lockPath,\n ...positionals,\n ];\n\n try {\n const child = spawn(process.execPath, args, {\n detached: true,\n stdio: \"ignore\",\n });\n child.unref();\n return true;\n } catch (error) {\n // Non-fatal: the foreground already wrote degraded types. Log and move on.\n console.error(\n `Could not start background type refresh: ${\n error instanceof Error ? error.message : String(error)\n }`,\n );\n return false;\n }\n}\n\n/**\n * The command action. Orchestrates the non-blocking foreground contract:\n * 1. Run the library generate (writes degraded types immediately in non-blocking\n * mode; does the full blocking lifecycle when this is the worker).\n * 2. If this is a non-blocking, non-worker invocation, try to spawn the detached\n * blocking worker behind the single-flight lock. If the lock is already held\n * by a live worker, skip (single-flight) with a one-line note. Either way the\n * foreground returns normally (exit 0).\n * 3. If this IS the worker (`--worker-lock` present), it ran blocking above and\n * releases the lock here (and via a process-exit guard, so a hard failure /\n * process.exit still frees it).\n */\nasync function generateTypesAction(\n rootDir: string | undefined,\n outFile: string | undefined,\n warehouseId: string | undefined,\n options: GenerateTypesOptions,\n) {\n const isWorker = typeof options.workerLock === \"string\";\n\n // A worker must always free its lock, even if the blocking generate throws or\n // calls process.exit (TypegenFatalError → exit 1). The exit handler covers the\n // process.exit / uncaught paths; the finally covers the normal return.\n if (isWorker && options.workerLock) {\n const lockPath = options.workerLock;\n process.once(\"exit\", () => releaseSpawnLock(lockPath));\n }\n\n try {\n await runGenerateTypes(rootDir, outFile, warehouseId, options);\n } finally {\n if (isWorker && options.workerLock) {\n releaseSpawnLock(options.workerLock);\n }\n }\n\n // Only a non-blocking, non-worker invocation spawns. A worker is always\n // --wait (so resolveTypegenMode → \"blocking\"), which both prevents recursion\n // and means we never get here for a worker.\n if (!isWorker && resolveTypegenMode(options) === \"non-blocking\") {\n const resolvedRootDir = rootDir || process.cwd();\n const lockPath = getSpawnLockPath(resolvedRootDir);\n\n if (acquireSpawnLock(lockPath)) {\n spawnTypegenWorker(lockPath, { rootDir, outFile, warehouseId });\n } else {\n console.log(\"Type refresh already in progress, skipping.\");\n }\n }\n}\n\nexport const generateTypesCommand = new Command(\"generate-types\")\n .description(\"Generate TypeScript types from AppKit configuration\")\n .argument(\"[rootDir]\", \"Root directory of the project\", process.cwd())\n .argument(\n \"[outFile]\",\n \"Output file path\",\n path.join(process.cwd(), \"shared/appkit-types/analytics.d.ts\"),\n )\n .argument(\"[warehouseId]\", \"Databricks warehouse ID\")\n .option(\"--no-cache\", \"Disable caching for type generation\")\n .option(\n \"--wait\",\n \"Wait for warehouse readiness instead of degrading (use for CI)\",\n )\n // Internal: marks the detached background worker and carries the lock it must\n // release. Hidden from --help; users should never pass it.\n .addOption(\n new Option(\n \"--worker-lock <path>\",\n \"Internal: detached worker lock path\",\n ).hideHelp(),\n )\n .addHelpText(\n \"after\",\n `\nExamples:\n $ appkit generate-types\n $ appkit generate-types . shared/appkit-types/analytics.d.ts\n $ appkit generate-types . shared/appkit-types/analytics.d.ts my-warehouse-id\n $ appkit generate-types --no-cache\n $ appkit generate-types --wait # CI: wait for the warehouse and fail on a cold one`,\n )\n .action(generateTypesAction);\n"],"mappings":"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"}
|
|
@@ -85,15 +85,15 @@ function scaffoldPlugin(targetDir, answers, options) {
|
|
|
85
85
|
const exportName = toCamelCase(answers.name);
|
|
86
86
|
writeTracked(path.join(targetDir, "manifest.json"), `${JSON.stringify(manifest, null, 2)}\n`, written);
|
|
87
87
|
const pluginTs = `import {
|
|
88
|
+
defineManifest,
|
|
88
89
|
Plugin,
|
|
89
90
|
toPlugin,
|
|
90
91
|
type IAppRouter,
|
|
91
|
-
type PluginManifest,
|
|
92
92
|
} from "@databricks/appkit";
|
|
93
93
|
import manifest from "./manifest.json";
|
|
94
94
|
|
|
95
95
|
export class ${className} extends Plugin {
|
|
96
|
-
static manifest =
|
|
96
|
+
static manifest = defineManifest<"${answers.name}">(manifest);
|
|
97
97
|
|
|
98
98
|
injectRoutes(router: IAppRouter): void {
|
|
99
99
|
// Add your routes here, e.g.:
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scaffold.js","names":[],"sources":["../../../../../src/cli/commands/plugin/create/scaffold.ts"],"sourcesContent":["import fs from \"node:fs\";\nimport path from \"node:path\";\n\nimport { humanizeResourceType, MANIFEST_SCHEMA_ID } from \"./resource-defaults\";\nimport type { CreateAnswers } from \"./types\";\n\n/** Convert kebab-name to PascalCase (e.g. my-plugin -> MyPlugin). */\nfunction toPascalCase(name: string): string {\n return name\n .split(\"-\")\n .map((s) => s.charAt(0).toUpperCase() + s.slice(1).toLowerCase())\n .join(\"\");\n}\n\n/** Convert kebab-name to camelCase (e.g. my-plugin -> myPlugin). */\nfunction toCamelCase(name: string): string {\n const pascal = toPascalCase(name);\n return pascal.charAt(0).toLowerCase() + pascal.slice(1);\n}\n\n/** Build manifest.json resources from selected resources. */\nfunction buildManifestResources(answers: CreateAnswers) {\n const required: unknown[] = [];\n const optional: unknown[] = [];\n\n for (const r of answers.resources) {\n const alias = humanizeResourceType(r.type);\n const entry = {\n type: r.type,\n alias,\n resourceKey: r.resourceKey,\n description: r.description || `Required for ${alias} functionality.`,\n permission: r.permission,\n fields: r.fields,\n };\n if (r.required) {\n required.push(entry);\n } else {\n optional.push(entry);\n }\n }\n\n return { required, optional };\n}\n\n/** Build full manifest object for manifest.json. */\nfunction buildManifest(answers: CreateAnswers): Record<string, unknown> {\n const { required, optional } = buildManifestResources(answers);\n const manifest: Record<string, unknown> = {\n $schema: MANIFEST_SCHEMA_ID,\n name: answers.name,\n displayName: answers.displayName,\n description: answers.description,\n resources: { required, optional },\n };\n if (answers.stability) manifest.stability = answers.stability;\n if (answers.author) manifest.author = answers.author;\n manifest.version = answers.version || \"0.1.0\";\n if (answers.license) manifest.license = answers.license;\n return manifest;\n}\n\n/** Resolve absolute target directory from cwd and answers. */\nexport function resolveTargetDir(cwd: string, answers: CreateAnswers): string {\n return path.resolve(cwd, answers.targetPath);\n}\n\n/** Track files written during scaffolding for rollback on failure. */\nfunction writeTracked(\n filePath: string,\n content: string,\n written: string[],\n): void {\n fs.writeFileSync(filePath, content);\n written.push(filePath);\n}\n\n/** Remove files written during a failed scaffold attempt. */\nfunction rollback(written: string[], targetDir: string): void {\n for (const filePath of written.reverse()) {\n try {\n fs.unlinkSync(filePath);\n } catch {\n // best-effort cleanup\n }\n }\n try {\n const remaining = fs.readdirSync(targetDir);\n if (remaining.length === 0) fs.rmdirSync(targetDir);\n } catch {\n // directory may not be empty or may have been removed already\n }\n}\n\n/**\n * Scaffold plugin files into targetDir. Pure: no interactive I/O.\n * Writes manifest.json, {name}.ts, index.ts; for isolated also package.json, tsconfig.json, README.md.\n * On failure, rolls back any files already written.\n */\nexport function scaffoldPlugin(\n targetDir: string,\n answers: CreateAnswers,\n options: { isolated: boolean },\n): void {\n fs.mkdirSync(targetDir, { recursive: true });\n\n const written: string[] = [];\n\n try {\n const manifest = buildManifest(answers);\n const className = toPascalCase(answers.name);\n const exportName = toCamelCase(answers.name);\n\n writeTracked(\n path.join(targetDir, \"manifest.json\"),\n `${JSON.stringify(manifest, null, 2)}\\n`,\n written,\n );\n\n const pluginTs = `import {\n Plugin,\n toPlugin,\n type IAppRouter,\n
|
|
1
|
+
{"version":3,"file":"scaffold.js","names":[],"sources":["../../../../../src/cli/commands/plugin/create/scaffold.ts"],"sourcesContent":["import fs from \"node:fs\";\nimport path from \"node:path\";\n\nimport { humanizeResourceType, MANIFEST_SCHEMA_ID } from \"./resource-defaults\";\nimport type { CreateAnswers } from \"./types\";\n\n/** Convert kebab-name to PascalCase (e.g. my-plugin -> MyPlugin). */\nfunction toPascalCase(name: string): string {\n return name\n .split(\"-\")\n .map((s) => s.charAt(0).toUpperCase() + s.slice(1).toLowerCase())\n .join(\"\");\n}\n\n/** Convert kebab-name to camelCase (e.g. my-plugin -> myPlugin). */\nfunction toCamelCase(name: string): string {\n const pascal = toPascalCase(name);\n return pascal.charAt(0).toLowerCase() + pascal.slice(1);\n}\n\n/** Build manifest.json resources from selected resources. */\nfunction buildManifestResources(answers: CreateAnswers) {\n const required: unknown[] = [];\n const optional: unknown[] = [];\n\n for (const r of answers.resources) {\n const alias = humanizeResourceType(r.type);\n const entry = {\n type: r.type,\n alias,\n resourceKey: r.resourceKey,\n description: r.description || `Required for ${alias} functionality.`,\n permission: r.permission,\n fields: r.fields,\n };\n if (r.required) {\n required.push(entry);\n } else {\n optional.push(entry);\n }\n }\n\n return { required, optional };\n}\n\n/** Build full manifest object for manifest.json. */\nfunction buildManifest(answers: CreateAnswers): Record<string, unknown> {\n const { required, optional } = buildManifestResources(answers);\n const manifest: Record<string, unknown> = {\n $schema: MANIFEST_SCHEMA_ID,\n name: answers.name,\n displayName: answers.displayName,\n description: answers.description,\n resources: { required, optional },\n };\n if (answers.stability) manifest.stability = answers.stability;\n if (answers.author) manifest.author = answers.author;\n manifest.version = answers.version || \"0.1.0\";\n if (answers.license) manifest.license = answers.license;\n return manifest;\n}\n\n/** Resolve absolute target directory from cwd and answers. */\nexport function resolveTargetDir(cwd: string, answers: CreateAnswers): string {\n return path.resolve(cwd, answers.targetPath);\n}\n\n/** Track files written during scaffolding for rollback on failure. */\nfunction writeTracked(\n filePath: string,\n content: string,\n written: string[],\n): void {\n fs.writeFileSync(filePath, content);\n written.push(filePath);\n}\n\n/** Remove files written during a failed scaffold attempt. */\nfunction rollback(written: string[], targetDir: string): void {\n for (const filePath of written.reverse()) {\n try {\n fs.unlinkSync(filePath);\n } catch {\n // best-effort cleanup\n }\n }\n try {\n const remaining = fs.readdirSync(targetDir);\n if (remaining.length === 0) fs.rmdirSync(targetDir);\n } catch {\n // directory may not be empty or may have been removed already\n }\n}\n\n/**\n * Scaffold plugin files into targetDir. Pure: no interactive I/O.\n * Writes manifest.json, {name}.ts, index.ts; for isolated also package.json, tsconfig.json, README.md.\n * On failure, rolls back any files already written.\n */\nexport function scaffoldPlugin(\n targetDir: string,\n answers: CreateAnswers,\n options: { isolated: boolean },\n): void {\n fs.mkdirSync(targetDir, { recursive: true });\n\n const written: string[] = [];\n\n try {\n const manifest = buildManifest(answers);\n const className = toPascalCase(answers.name);\n const exportName = toCamelCase(answers.name);\n\n writeTracked(\n path.join(targetDir, \"manifest.json\"),\n `${JSON.stringify(manifest, null, 2)}\\n`,\n written,\n );\n\n const pluginTs = `import {\n defineManifest,\n Plugin,\n toPlugin,\n type IAppRouter,\n} from \"@databricks/appkit\";\nimport manifest from \"./manifest.json\";\n\nexport class ${className} extends Plugin {\n static manifest = defineManifest<\"${answers.name}\">(manifest);\n\n injectRoutes(router: IAppRouter): void {\n // Add your routes here, e.g.:\n // this.route(router, {\n // name: \"example\",\n // method: \"get\",\n // path: \"/\",\n // handler: async (_req, res) => {\n // res.json({ message: \"Hello from ${answers.name}\" });\n // },\n // });\n }\n}\n\nexport const ${exportName} = toPlugin(${className});\n`;\n\n writeTracked(path.join(targetDir, `${answers.name}.ts`), pluginTs, written);\n\n const indexTs = `export { ${className}, ${exportName} } from \"./${answers.name}\";\n`;\n\n writeTracked(path.join(targetDir, \"index.ts\"), indexTs, written);\n\n if (options.isolated) {\n const packageName =\n answers.name.includes(\"/\") || answers.name.startsWith(\"@\")\n ? answers.name\n : `appkit-plugin-${answers.name}`;\n\n const packageJson = {\n name: packageName,\n version: answers.version || \"0.1.0\",\n type: \"module\",\n main: \"./dist/index.js\",\n types: \"./dist/index.d.ts\",\n files: [\"dist\"],\n scripts: {\n build: \"tsc\",\n typecheck: \"tsc --noEmit\",\n },\n peerDependencies: {\n \"@databricks/appkit\": \">=0.5.0\",\n },\n devDependencies: {\n typescript: \"^5.0.0\",\n },\n };\n\n writeTracked(\n path.join(targetDir, \"package.json\"),\n `${JSON.stringify(packageJson, null, 2)}\\n`,\n written,\n );\n\n const tsconfigJson = {\n compilerOptions: {\n target: \"ES2022\",\n module: \"NodeNext\",\n moduleResolution: \"NodeNext\",\n outDir: \"dist\",\n rootDir: \".\",\n declaration: true,\n strict: true,\n skipLibCheck: true,\n },\n include: [\"*.ts\"],\n exclude: [\"node_modules\", \"dist\"],\n };\n\n writeTracked(\n path.join(targetDir, \"tsconfig.json\"),\n `${JSON.stringify(tsconfigJson, null, 2)}\\n`,\n written,\n );\n\n const readme = `# ${answers.displayName}\n\n${answers.description}\n\n## Installation\n\n\\`\\`\\`bash\npnpm add ${packageName} @databricks/appkit\n\\`\\`\\`\n\n## Usage\n\nRegister the plugin in your AppKit app:\n\n\\`\\`\\`ts\nimport { createApp } from \"@databricks/appkit\";\nimport { ${exportName} } from \"${packageName}\";\n\ncreateApp({\n plugins: [\n ${exportName}(),\n // ... other plugins\n ],\n}).then((app) => { /* ... */ });\n\\`\\`\\`\n`;\n\n writeTracked(path.join(targetDir, \"README.md\"), readme, written);\n }\n } catch (err) {\n rollback(written, targetDir);\n throw err;\n }\n}\n"],"mappings":"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"}
|
package/dist/core/appkit.js
CHANGED
|
@@ -9,10 +9,10 @@ import { isInternalTelemetryEnabled } from "../internal-telemetry/config.js";
|
|
|
9
9
|
import { TelemetryReporter } from "../internal-telemetry/reporter.js";
|
|
10
10
|
import "../internal-telemetry/index.js";
|
|
11
11
|
import { isPlainObject } from "../plugin/plugin.js";
|
|
12
|
-
import { uiVariants } from "../plugins/ui-variants/index.js";
|
|
13
12
|
import { ResourceType } from "../registry/types.generated.js";
|
|
14
13
|
import { ResourceRegistry } from "../registry/resource-registry.js";
|
|
15
14
|
import "../registry/index.js";
|
|
15
|
+
import { uiVariants } from "../plugins/ui-variants/index.js";
|
|
16
16
|
import { LifecycleManager } from "./lifecycle-manager.js";
|
|
17
17
|
import { PluginContext, isToolProvider } from "./plugin-context.js";
|
|
18
18
|
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
//#region src/database/contract/registry.d.ts
|
|
2
|
+
/** The shape of a single generated registry entry. */
|
|
3
|
+
interface DatabaseRegistryEntry {
|
|
4
|
+
/** Full server-side row (includes private columns). */
|
|
5
|
+
row: Record<string, unknown>;
|
|
6
|
+
/** Default private-safe row returned by collection reads. */
|
|
7
|
+
publicRow: Record<string, unknown>;
|
|
8
|
+
/** Trusted insert payload (includes private fields; omits generated columns). */
|
|
9
|
+
insert: Record<string, unknown>;
|
|
10
|
+
/** Trusted update payload (includes private fields; omits PK/generated columns). */
|
|
11
|
+
update: Record<string, unknown>;
|
|
12
|
+
/** Per-column filter operators usable in `where`. */
|
|
13
|
+
filters: Record<string, unknown>;
|
|
14
|
+
/** Relations that can be passed to `include`. */
|
|
15
|
+
includes: Record<string, unknown>;
|
|
16
|
+
/** Literal capability used to omit keyed methods from keyless entities. */
|
|
17
|
+
hasPrimaryKey: boolean;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* CANONICAL augmentation target. Empty by default; the generated `database.d.ts`
|
|
21
|
+
* augments it via `declare module "@databricks/appkit" { interface DatabaseRegistry { ... } }`.
|
|
22
|
+
*/
|
|
23
|
+
interface DatabaseRegistry {}
|
|
24
|
+
//#endregion
|
|
25
|
+
export { DatabaseRegistry, DatabaseRegistryEntry };
|
|
26
|
+
//# sourceMappingURL=registry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.d.ts","names":[],"sources":["../../../src/database/contract/registry.ts"],"mappings":";;UACiB,qBAAA;EAAqB;EAEpC,GAAA,EAAK,MAAA;EAAA;EAEL,SAAA,EAAW,MAAA;EAEH;EAAR,MAAA,EAAQ,MAAA;EAIC;EAFT,MAAA,EAAQ,MAAA;EAIQ;EAFhB,OAAA,EAAS,MAAA;EART;EAUA,QAAA,EAAU,MAAA;EARV;EAUA,aAAA;AAAA;;;;;UAQe,gBAAA"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
//#region src/database/contract/wire.d.ts
|
|
2
|
+
/** Scalar values accepted by primary-key operations. */
|
|
3
|
+
type IdValue = string | number | bigint;
|
|
4
|
+
/** Ordering accepted by typed clients and the runtime adapter. */
|
|
5
|
+
type OrderDirection = "asc" | "desc";
|
|
6
|
+
//#endregion
|
|
7
|
+
export { IdValue, OrderDirection };
|
|
8
|
+
//# sourceMappingURL=wire.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wire.d.ts","names":[],"sources":["../../../src/database/contract/wire.ts"],"mappings":";;KAkBY,OAAA;;KAEA,cAAA"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
//#region src/database/contract/wire.ts
|
|
2
|
+
/** Max number of values allowed in an `in.(…)` list. */
|
|
3
|
+
const IN_CAP = 100;
|
|
4
|
+
/** Hard ceiling for a runtime query limit. */
|
|
5
|
+
const MAX_LIMIT = 500;
|
|
6
|
+
/** Hard ceiling for a runtime query offset. OFFSET scans are unbounded in cost. */
|
|
7
|
+
const MAX_OFFSET = 1e4;
|
|
8
|
+
/** Default page size when no `.limit()` is supplied. */
|
|
9
|
+
const DEFAULT_LIMIT = 50;
|
|
10
|
+
/** Max number of relations resolvable in a single `.include()`. */
|
|
11
|
+
const MAX_INCLUDES = 10;
|
|
12
|
+
/** Max nesting depth of `and`/`or` groups in one runtime predicate. */
|
|
13
|
+
const MAX_WHERE_DEPTH = 5;
|
|
14
|
+
/** Max members accepted by one runtime `and`/`or` group. */
|
|
15
|
+
const MAX_WHERE_GROUP_ITEMS = 20;
|
|
16
|
+
/** Max column conditions accepted across one runtime predicate tree. */
|
|
17
|
+
const MAX_WHERE_CONDITIONS = 50;
|
|
18
|
+
/** Filter operators usable in the runtime WHERE translator and the `where` spec type. */
|
|
19
|
+
const FILTER_OPERATORS = Object.freeze([
|
|
20
|
+
"eq",
|
|
21
|
+
"neq",
|
|
22
|
+
"gt",
|
|
23
|
+
"gte",
|
|
24
|
+
"lt",
|
|
25
|
+
"lte",
|
|
26
|
+
"like",
|
|
27
|
+
"ilike",
|
|
28
|
+
"in",
|
|
29
|
+
"is"
|
|
30
|
+
]);
|
|
31
|
+
function isFilterOperator(token) {
|
|
32
|
+
return FILTER_OPERATORS.includes(token);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
//#endregion
|
|
36
|
+
export { DEFAULT_LIMIT, FILTER_OPERATORS, IN_CAP, MAX_INCLUDES, MAX_LIMIT, MAX_OFFSET, MAX_WHERE_CONDITIONS, MAX_WHERE_DEPTH, MAX_WHERE_GROUP_ITEMS, isFilterOperator };
|
|
37
|
+
//# sourceMappingURL=wire.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wire.js","names":[],"sources":["../../../src/database/contract/wire.ts"],"sourcesContent":["/** Max number of values allowed in an `in.(…)` list. */\nexport const IN_CAP = 100;\n/** Hard ceiling for a runtime query limit. */\nexport const MAX_LIMIT = 500;\n/** Hard ceiling for a runtime query offset. OFFSET scans are unbounded in cost. */\nexport const MAX_OFFSET = 10_000;\n/** Default page size when no `.limit()` is supplied. */\nexport const DEFAULT_LIMIT = 50;\n/** Max number of relations resolvable in a single `.include()`. */\nexport const MAX_INCLUDES = 10;\n/** Max nesting depth of `and`/`or` groups in one runtime predicate. */\nexport const MAX_WHERE_DEPTH = 5;\n/** Max members accepted by one runtime `and`/`or` group. */\nexport const MAX_WHERE_GROUP_ITEMS = 20;\n/** Max column conditions accepted across one runtime predicate tree. */\nexport const MAX_WHERE_CONDITIONS = 50;\n\n/** Scalar values accepted by primary-key operations. */\nexport type IdValue = string | number | bigint;\n/** Ordering accepted by typed clients and the runtime adapter. */\nexport type OrderDirection = \"asc\" | \"desc\";\n/** Filter operators usable in the runtime WHERE translator and the `where` spec type. */\nexport const FILTER_OPERATORS = Object.freeze([\n \"eq\",\n \"neq\",\n \"gt\",\n \"gte\",\n \"lt\",\n \"lte\",\n \"like\",\n \"ilike\",\n \"in\",\n \"is\",\n] as const);\n\nexport type FilterOperator = (typeof FILTER_OPERATORS)[number];\n\nexport function isFilterOperator(token: string): token is FilterOperator {\n return (FILTER_OPERATORS as readonly string[]).includes(token);\n}\n"],"mappings":";;AACA,MAAa,SAAS;;AAEtB,MAAa,YAAY;;AAEzB,MAAa,aAAa;;AAE1B,MAAa,gBAAgB;;AAE7B,MAAa,eAAe;;AAE5B,MAAa,kBAAkB;;AAE/B,MAAa,wBAAwB;;AAErC,MAAa,uBAAuB;;AAOpC,MAAa,mBAAmB,OAAO,OAAO;CAC5C;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD,CAAU;AAIX,SAAgB,iBAAiB,OAAwC;AACvE,QAAQ,iBAAuC,SAAS,MAAM"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { createLogger } from "../logging/logger.js";
|
|
2
|
+
import { AppKitError } from "../errors/base.js";
|
|
3
|
+
import "../errors/index.js";
|
|
4
|
+
|
|
5
|
+
//#region src/database/errors.ts
|
|
6
|
+
const logger = createLogger("database");
|
|
7
|
+
const definitions = {
|
|
8
|
+
INVALID_REQUEST: {
|
|
9
|
+
message: "Invalid database request",
|
|
10
|
+
statusCode: 400
|
|
11
|
+
},
|
|
12
|
+
CONFLICT: {
|
|
13
|
+
message: "Database conflict",
|
|
14
|
+
statusCode: 409
|
|
15
|
+
},
|
|
16
|
+
FORBIDDEN: {
|
|
17
|
+
message: "Database operation forbidden",
|
|
18
|
+
statusCode: 403
|
|
19
|
+
},
|
|
20
|
+
TRANSIENT: {
|
|
21
|
+
message: "Database operation temporarily unavailable",
|
|
22
|
+
statusCode: 503
|
|
23
|
+
},
|
|
24
|
+
INTERNAL: {
|
|
25
|
+
message: "Database operation failed",
|
|
26
|
+
statusCode: 500
|
|
27
|
+
},
|
|
28
|
+
SETUP_FAILED: {
|
|
29
|
+
message: "Database setup failed",
|
|
30
|
+
statusCode: 500
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
const categoryByStatus = {
|
|
34
|
+
400: "INVALID_REQUEST",
|
|
35
|
+
403: "FORBIDDEN",
|
|
36
|
+
409: "CONFLICT",
|
|
37
|
+
503: "TRANSIENT"
|
|
38
|
+
};
|
|
39
|
+
/** AppKit-facing database failure with stable metadata and no driver details. */
|
|
40
|
+
var DatabasePluginError = class extends AppKitError {
|
|
41
|
+
code = "DATABASE_PLUGIN_ERROR";
|
|
42
|
+
isRetryable;
|
|
43
|
+
statusCode;
|
|
44
|
+
constructor(category, phase, runtimeMessage) {
|
|
45
|
+
const definition = definitions[category];
|
|
46
|
+
super(phase === "runtime" && runtimeMessage ? runtimeMessage : definition.message, { clientMessage: definition.message });
|
|
47
|
+
this.category = category;
|
|
48
|
+
this.phase = phase;
|
|
49
|
+
this.statusCode = definition.statusCode;
|
|
50
|
+
this.isRetryable = category === "TRANSIENT";
|
|
51
|
+
this.name = "DatabasePluginError";
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
/** Keep runtime diagnostics internal until a plugin boundary classifies them. */
|
|
55
|
+
function invalidDatabaseRequest(runtimeMessage) {
|
|
56
|
+
return new DatabasePluginError("INVALID_REQUEST", "runtime", runtimeMessage);
|
|
57
|
+
}
|
|
58
|
+
/** Add operation context without retaining an unknown error's details. */
|
|
59
|
+
function classifyDatabaseError(error, phase) {
|
|
60
|
+
if (error instanceof DatabasePluginError) return error.phase === phase ? error : new DatabasePluginError(error.category, phase);
|
|
61
|
+
logger.error("Unclassified database error during %s: %O", phase, error);
|
|
62
|
+
return new DatabasePluginError("INTERNAL", phase);
|
|
63
|
+
}
|
|
64
|
+
/** Restore the safe database category carried through `Plugin.execute()`. */
|
|
65
|
+
function databaseErrorFromStatus(status, phase) {
|
|
66
|
+
return new DatabasePluginError(categoryByStatus[status] ?? "INTERNAL", phase);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
//#endregion
|
|
70
|
+
export { DatabasePluginError, classifyDatabaseError, databaseErrorFromStatus, invalidDatabaseRequest };
|
|
71
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","names":[],"sources":["../../src/database/errors.ts"],"sourcesContent":["import { AppKitError } from \"../errors\";\nimport { createLogger } from \"../logging/logger\";\n\nconst logger = createLogger(\"database\");\n\nexport type DatabaseErrorCategory =\n | \"INVALID_REQUEST\"\n | \"CONFLICT\"\n | \"FORBIDDEN\"\n | \"TRANSIENT\"\n | \"INTERNAL\"\n | \"SETUP_FAILED\";\n\ntype DatabaseErrorPhase =\n | \"setup\"\n | \"shutdown\"\n | \"read\"\n | \"write\"\n | \"transaction\"\n | \"runtime\";\n\nconst definitions: Record<\n DatabaseErrorCategory,\n { readonly message: string; readonly statusCode: number }\n> = {\n INVALID_REQUEST: { message: \"Invalid database request\", statusCode: 400 },\n CONFLICT: { message: \"Database conflict\", statusCode: 409 },\n FORBIDDEN: { message: \"Database operation forbidden\", statusCode: 403 },\n TRANSIENT: {\n message: \"Database operation temporarily unavailable\",\n statusCode: 503,\n },\n INTERNAL: { message: \"Database operation failed\", statusCode: 500 },\n SETUP_FAILED: { message: \"Database setup failed\", statusCode: 500 },\n};\n\nconst categoryByStatus: Readonly<Record<number, DatabaseErrorCategory>> = {\n 400: \"INVALID_REQUEST\",\n 403: \"FORBIDDEN\",\n 409: \"CONFLICT\",\n 503: \"TRANSIENT\",\n};\n\n/** AppKit-facing database failure with stable metadata and no driver details. */\nexport class DatabasePluginError extends AppKitError {\n readonly code = \"DATABASE_PLUGIN_ERROR\";\n readonly isRetryable: boolean;\n readonly statusCode: number;\n\n constructor(\n readonly category: DatabaseErrorCategory,\n readonly phase: DatabaseErrorPhase,\n runtimeMessage?: string,\n ) {\n const definition = definitions[category];\n // Plugin boundaries replace runtime diagnostics with the stable message.\n super(\n phase === \"runtime\" && runtimeMessage\n ? runtimeMessage\n : definition.message,\n {\n clientMessage: definition.message,\n },\n );\n this.statusCode = definition.statusCode;\n this.isRetryable = category === \"TRANSIENT\";\n this.name = \"DatabasePluginError\";\n }\n}\n\n/** Keep runtime diagnostics internal until a plugin boundary classifies them. */\nexport function invalidDatabaseRequest(\n runtimeMessage?: string,\n): DatabasePluginError {\n return new DatabasePluginError(\"INVALID_REQUEST\", \"runtime\", runtimeMessage);\n}\n\n/** Add operation context without retaining an unknown error's details. */\nexport function classifyDatabaseError(\n error: unknown,\n phase: DatabaseErrorPhase,\n): DatabasePluginError {\n if (error instanceof DatabasePluginError) {\n return error.phase === phase\n ? error\n : new DatabasePluginError(error.category, phase);\n }\n logger.error(\"Unclassified database error during %s: %O\", phase, error);\n return new DatabasePluginError(\"INTERNAL\", phase);\n}\n\n/** Restore the safe database category carried through `Plugin.execute()`. */\nexport function databaseErrorFromStatus(\n status: number,\n phase: DatabaseErrorPhase,\n): DatabasePluginError {\n const category = categoryByStatus[status] ?? \"INTERNAL\";\n return new DatabasePluginError(category, phase);\n}\n"],"mappings":";;;;;AAGA,MAAM,SAAS,aAAa,WAAW;AAkBvC,MAAM,cAGF;CACF,iBAAiB;EAAE,SAAS;EAA4B,YAAY;EAAK;CACzE,UAAU;EAAE,SAAS;EAAqB,YAAY;EAAK;CAC3D,WAAW;EAAE,SAAS;EAAgC,YAAY;EAAK;CACvE,WAAW;EACT,SAAS;EACT,YAAY;EACb;CACD,UAAU;EAAE,SAAS;EAA6B,YAAY;EAAK;CACnE,cAAc;EAAE,SAAS;EAAyB,YAAY;EAAK;CACpE;AAED,MAAM,mBAAoE;CACxE,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACN;;AAGD,IAAa,sBAAb,cAAyC,YAAY;CACnD,AAAS,OAAO;CAChB,AAAS;CACT,AAAS;CAET,YACE,AAAS,UACT,AAAS,OACT,gBACA;EACA,MAAM,aAAa,YAAY;AAE/B,QACE,UAAU,aAAa,iBACnB,iBACA,WAAW,SACf,EACE,eAAe,WAAW,SAC3B,CACF;EAbQ;EACA;AAaT,OAAK,aAAa,WAAW;AAC7B,OAAK,cAAc,aAAa;AAChC,OAAK,OAAO;;;;AAKhB,SAAgB,uBACd,gBACqB;AACrB,QAAO,IAAI,oBAAoB,mBAAmB,WAAW,eAAe;;;AAI9E,SAAgB,sBACd,OACA,OACqB;AACrB,KAAI,iBAAiB,oBACnB,QAAO,MAAM,UAAU,QACnB,QACA,IAAI,oBAAoB,MAAM,UAAU,MAAM;AAEpD,QAAO,MAAM,6CAA6C,OAAO,MAAM;AACvE,QAAO,IAAI,oBAAoB,YAAY,MAAM;;;AAInD,SAAgB,wBACd,QACA,OACqB;AAErB,QAAO,IAAI,oBADM,iBAAiB,WAAW,YACJ,MAAM"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { invalidDatabaseRequest } from "../errors.js";
|
|
2
|
+
import { DEFAULT_LIMIT, MAX_LIMIT, MAX_OFFSET } from "../contract/wire.js";
|
|
3
|
+
import "../contract/index.js";
|
|
4
|
+
|
|
5
|
+
//#region src/database/runtime/data-path.ts
|
|
6
|
+
/** Combine predicates without making callers understand the wire shape. */
|
|
7
|
+
function andWhere(existing, next) {
|
|
8
|
+
return existing === void 0 ? next : { and: [existing, next] };
|
|
9
|
+
}
|
|
10
|
+
/** Validate an explicit root or relation row limit. */
|
|
11
|
+
function validateLimit(limit) {
|
|
12
|
+
if (!Number.isInteger(limit) || limit < 0 || limit > MAX_LIMIT) throw invalidDatabaseRequest(`limit must be an integer between 0 and ${MAX_LIMIT}`);
|
|
13
|
+
return limit;
|
|
14
|
+
}
|
|
15
|
+
/** Apply the conservative collection default when no limit is supplied. */
|
|
16
|
+
function limitOrDefault(limit) {
|
|
17
|
+
return limit === void 0 ? DEFAULT_LIMIT : validateLimit(limit);
|
|
18
|
+
}
|
|
19
|
+
/** Validate an explicit root or relation row offset. */
|
|
20
|
+
function validateOffset(offset) {
|
|
21
|
+
if (!Number.isSafeInteger(offset) || offset < 0 || offset > MAX_OFFSET) throw invalidDatabaseRequest(`offset must be an integer between 0 and ${MAX_OFFSET}`);
|
|
22
|
+
return offset;
|
|
23
|
+
}
|
|
24
|
+
/** Resolve the sole primary key required by keyed operations. */
|
|
25
|
+
function primaryKeyMeta(table) {
|
|
26
|
+
const primaryKeys = Object.values(table.$columns).filter((column) => column.primaryKey);
|
|
27
|
+
if (primaryKeys.length !== 1) throw invalidDatabaseRequest(`Table "${table.$name}" has no primary key`);
|
|
28
|
+
return primaryKeys[0];
|
|
29
|
+
}
|
|
30
|
+
/** Resolve an upsert target that PostgreSQL can use for conflict detection. */
|
|
31
|
+
function conflictTargetMeta(table, columnName) {
|
|
32
|
+
const column = table.$columns[columnName];
|
|
33
|
+
if (!column || !column.primaryKey && !column.unique) throw invalidDatabaseRequest(`Column "${table.$name}.${columnName}" is not a conflict target`);
|
|
34
|
+
return column;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
//#endregion
|
|
38
|
+
export { andWhere, conflictTargetMeta, limitOrDefault, primaryKeyMeta, validateLimit, validateOffset };
|
|
39
|
+
//# sourceMappingURL=data-path.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"data-path.js","names":[],"sources":["../../../src/database/runtime/data-path.ts"],"sourcesContent":["import {\n DEFAULT_LIMIT,\n type FilterOperator,\n type IdValue,\n MAX_LIMIT,\n MAX_OFFSET,\n type OrderDirection,\n} from \"../contract\";\nimport { invalidDatabaseRequest } from \"../errors\";\nimport type { AppKitTable, ColumnMeta } from \"../schema-builder\";\n\nexport type { IdValue, OrderDirection };\nexport type ScalarValue = string | number | bigint | boolean | null;\n/** Operators for one column; array operands are reserved for `in`. */\nexport type FilterOps = Partial<\n Record<FilterOperator, ScalarValue | readonly ScalarValue[]>\n>;\nexport type WhereValue = ScalarValue | readonly ScalarValue[] | FilterOps;\n/** Direct-column predicates with explicit `and` and `or` predicate groups. */\nexport type WhereClause = Readonly<\n Record<string, WhereValue | readonly WhereClause[]>\n>;\n\nexport type OrderSpec = Readonly<Record<string, OrderDirection>>;\n\nexport interface IncludeOptions {\n readonly select?: readonly string[];\n readonly where?: WhereClause;\n readonly order?: OrderSpec;\n readonly limit?: number;\n}\n\n/** Selection and bounds for one declared relation edge. */\nexport type IncludeSpec = Readonly<Record<string, boolean | IncludeOptions>>;\n\n/** A bounded root read; adapters apply defaults and validate explicit bounds. */\nexport interface QuerySpec {\n readonly where?: WhereClause;\n readonly order?: OrderSpec;\n readonly select?: readonly string[];\n readonly include?: IncludeSpec;\n readonly limit?: number;\n readonly offset?: number;\n}\n\nexport type Row = Record<string, unknown>;\n\n/** Combine predicates without making callers understand the wire shape. */\nexport function andWhere(\n existing: WhereClause | undefined,\n next: WhereClause,\n): WhereClause {\n return existing === undefined ? next : { and: [existing, next] };\n}\n\n/** Internal AppKit execution port; field names are schema-owned identifiers. */\nexport interface DataPath {\n /** Read a bounded collection from one finalized table. */\n select(table: AppKitTable, spec: QuerySpec): Promise<Row[]>;\n /** Read by the sole primary key while preserving supported query state. */\n findOne(\n table: AppKitTable,\n id: IdValue,\n spec?: Pick<QuerySpec, \"where\" | \"select\" | \"include\">,\n ): Promise<Row | null>;\n count(table: AppKitTable, where?: WhereClause): Promise<number>;\n /** Return exactly one inserted row; zero or many is an invariant failure. */\n insert(table: AppKitTable, values: Row): Promise<Row>;\n /** Return null for zero updated rows and reject more than one. */\n update(table: AppKitTable, id: IdValue, values: Row): Promise<Row | null>;\n /** Return exactly one row for a validated primary-key or unique conflict. */\n upsert(table: AppKitTable, values: Row, onConflict: string): Promise<Row>;\n /** Return false for zero deleted rows, true for one, and reject many. */\n delete(table: AppKitTable, id: IdValue): Promise<boolean>;\n /** Execute tagged SQL whose interpolations are parameter values, not SQL. */\n raw<T = Row>(\n strings: TemplateStringsArray,\n ...values: unknown[]\n ): Promise<T[]>;\n /** Run the callback with one transaction-bound DataPath. */\n transaction<T>(callback: (tx: DataPath) => Promise<T>): Promise<T>;\n}\n\n/** Validate an explicit root or relation row limit. */\nexport function validateLimit(limit: number): number {\n if (!Number.isInteger(limit) || limit < 0 || limit > MAX_LIMIT) {\n throw invalidDatabaseRequest(\n `limit must be an integer between 0 and ${MAX_LIMIT}`,\n );\n }\n return limit;\n}\n\n/** Apply the conservative collection default when no limit is supplied. */\nexport function limitOrDefault(limit?: number): number {\n return limit === undefined ? DEFAULT_LIMIT : validateLimit(limit);\n}\n\n/** Validate an explicit root or relation row offset. */\nexport function validateOffset(offset: number): number {\n if (!Number.isSafeInteger(offset) || offset < 0 || offset > MAX_OFFSET) {\n throw invalidDatabaseRequest(\n `offset must be an integer between 0 and ${MAX_OFFSET}`,\n );\n }\n return offset;\n}\n\n/** Resolve the sole primary key required by keyed operations. */\nexport function primaryKeyMeta(table: AppKitTable): ColumnMeta {\n const primaryKeys = Object.values(table.$columns).filter(\n (column) => column.primaryKey,\n );\n if (primaryKeys.length !== 1) {\n throw invalidDatabaseRequest(`Table \"${table.$name}\" has no primary key`);\n }\n return primaryKeys[0];\n}\n\n/** Resolve an upsert target that PostgreSQL can use for conflict detection. */\nexport function conflictTargetMeta(\n table: AppKitTable,\n columnName: string,\n): ColumnMeta {\n const column = table.$columns[columnName];\n if (!column || (!column.primaryKey && !column.unique)) {\n throw invalidDatabaseRequest(\n `Column \"${table.$name}.${columnName}\" is not a conflict target`,\n );\n }\n return column;\n}\n"],"mappings":";;;;;;AAgDA,SAAgB,SACd,UACA,MACa;AACb,QAAO,aAAa,SAAY,OAAO,EAAE,KAAK,CAAC,UAAU,KAAK,EAAE;;;AAgClE,SAAgB,cAAc,OAAuB;AACnD,KAAI,CAAC,OAAO,UAAU,MAAM,IAAI,QAAQ,KAAK,QAAQ,UACnD,OAAM,uBACJ,0CAA0C,YAC3C;AAEH,QAAO;;;AAIT,SAAgB,eAAe,OAAwB;AACrD,QAAO,UAAU,SAAY,gBAAgB,cAAc,MAAM;;;AAInE,SAAgB,eAAe,QAAwB;AACrD,KAAI,CAAC,OAAO,cAAc,OAAO,IAAI,SAAS,KAAK,SAAS,WAC1D,OAAM,uBACJ,2CAA2C,aAC5C;AAEH,QAAO;;;AAIT,SAAgB,eAAe,OAAgC;CAC7D,MAAM,cAAc,OAAO,OAAO,MAAM,SAAS,CAAC,QAC/C,WAAW,OAAO,WACpB;AACD,KAAI,YAAY,WAAW,EACzB,OAAM,uBAAuB,UAAU,MAAM,MAAM,sBAAsB;AAE3E,QAAO,YAAY;;;AAIrB,SAAgB,mBACd,OACA,YACY;CACZ,MAAM,SAAS,MAAM,SAAS;AAC9B,KAAI,CAAC,UAAW,CAAC,OAAO,cAAc,CAAC,OAAO,OAC5C,OAAM,uBACJ,WAAW,MAAM,MAAM,GAAG,WAAW,4BACtC;AAEH,QAAO"}
|