@invect/version-control 0.0.1 → 0.0.2
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/README.md +78 -0
- package/dist/backend/flow-serializer.d.ts.map +1 -1
- package/dist/backend/index.cjs +209 -48
- package/dist/backend/index.cjs.map +1 -1
- package/dist/backend/index.d.cts +8 -3
- package/dist/backend/index.d.cts.map +1 -1
- package/dist/backend/index.d.mts +8 -3
- package/dist/backend/index.d.mts.map +1 -1
- package/dist/backend/index.mjs +208 -47
- package/dist/backend/index.mjs.map +1 -1
- package/dist/backend/plugin.d.ts +2 -2
- package/dist/backend/plugin.d.ts.map +1 -1
- package/dist/backend/sync-service.d.ts.map +1 -1
- package/dist/backend/types.d.ts +5 -0
- package/dist/backend/types.d.ts.map +1 -1
- package/dist/backend/validation.d.ts +19 -0
- package/dist/backend/validation.d.ts.map +1 -0
- package/dist/frontend/components/ConnectFlowForm.d.ts +10 -0
- package/dist/frontend/components/ConnectFlowForm.d.ts.map +1 -0
- package/dist/frontend/components/VcHeaderButton.d.ts +8 -0
- package/dist/frontend/components/VcHeaderButton.d.ts.map +1 -0
- package/dist/frontend/components/VcSyncPanel.d.ts +10 -0
- package/dist/frontend/components/VcSyncPanel.d.ts.map +1 -0
- package/dist/frontend/hooks/useFlowSync.d.ts +37 -0
- package/dist/frontend/hooks/useFlowSync.d.ts.map +1 -0
- package/dist/frontend/index.cjs +717 -0
- package/dist/frontend/index.cjs.map +1 -0
- package/dist/frontend/index.d.cts +43 -2
- package/dist/frontend/index.d.cts.map +1 -0
- package/dist/frontend/index.d.mts +43 -2
- package/dist/frontend/index.d.mts.map +1 -0
- package/dist/frontend/index.d.ts +9 -0
- package/dist/frontend/index.d.ts.map +1 -1
- package/dist/frontend/index.mjs +705 -1
- package/dist/frontend/index.mjs.map +1 -0
- package/dist/providers/github.d.cts +1 -1
- package/dist/providers/github.d.mts +1 -1
- package/dist/shared/types.cjs +19 -0
- package/dist/shared/types.cjs.map +1 -0
- package/dist/shared/types.d.cts +2 -2
- package/dist/shared/types.d.mts +2 -2
- package/dist/shared/types.d.ts +4 -2
- package/dist/shared/types.d.ts.map +1 -1
- package/dist/shared/types.mjs +17 -1
- package/dist/shared/types.mjs.map +1 -0
- package/dist/{types-B32wGtx7.d.cts → types-DACJdSjJ.d.mts} +6 -4
- package/dist/types-DACJdSjJ.d.mts.map +1 -0
- package/dist/{types-B7fFBAOX.d.mts → types-DDMnbS1q.d.cts} +6 -4
- package/dist/types-DDMnbS1q.d.cts.map +1 -0
- package/package.json +31 -4
- package/dist/types-B32wGtx7.d.cts.map +0 -1
- package/dist/types-B7fFBAOX.d.mts.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../../src/backend/schema.ts","../../src/backend/flow-serializer.ts","../../src/backend/sync-service.ts","../../src/backend/plugin.ts"],"sourcesContent":["// =============================================================================\n// Version Control Plugin — Database Schema (abstract, dialect-agnostic)\n// =============================================================================\n\nimport type { InvectPluginSchema } from '@invect/core';\n\nconst SYNC_MODES = ['direct-commit', 'pr-per-save', 'pr-per-publish'] as const;\nconst SYNC_DIRECTIONS = ['push', 'pull', 'bidirectional'] as const;\nconst SYNC_ACTIONS = ['push', 'pull', 'pr-created', 'pr-merged', 'conflict'] as const;\n\nexport const VC_SCHEMA: InvectPluginSchema = {\n vcSyncConfig: {\n tableName: 'vc_sync_config',\n order: 10,\n fields: {\n id: { type: 'string', primaryKey: true },\n flowId: {\n type: 'string',\n required: true,\n unique: true,\n references: { table: 'flows', field: 'id', onDelete: 'cascade' },\n index: true,\n },\n provider: { type: 'string', required: true },\n repo: { type: 'string', required: true },\n branch: { type: 'string', required: true },\n filePath: { type: 'string', required: true },\n mode: { type: [...SYNC_MODES], required: true },\n syncDirection: { type: [...SYNC_DIRECTIONS], required: true, defaultValue: 'push' },\n lastSyncedAt: { type: 'date', required: false },\n lastCommitSha: { type: 'string', required: false },\n lastSyncedVersion: { type: 'number', required: false },\n draftBranch: { type: 'string', required: false },\n activePrNumber: { type: 'number', required: false },\n activePrUrl: { type: 'string', required: false },\n enabled: { type: 'boolean', required: true, defaultValue: true },\n createdAt: { type: 'date', required: true, defaultValue: 'now()' },\n updatedAt: { type: 'date', required: true, defaultValue: 'now()' },\n },\n },\n\n vcSyncHistory: {\n tableName: 'vc_sync_history',\n order: 20,\n fields: {\n id: { type: 'uuid', primaryKey: true, defaultValue: 'uuid()' },\n flowId: {\n type: 'string',\n required: true,\n references: { table: 'flows', field: 'id', onDelete: 'cascade' },\n index: true,\n },\n action: { type: [...SYNC_ACTIONS], required: true },\n commitSha: { type: 'string', required: false },\n prNumber: { type: 'number', required: false },\n version: { type: 'number', required: false },\n message: { type: 'string', required: false },\n createdAt: { type: 'date', required: true, defaultValue: 'now()' },\n createdBy: { type: 'string', required: false },\n },\n },\n};\n","// =============================================================================\n// Flow Serializer — converts InvectDefinition JSON ↔ .flow.ts file content\n// =============================================================================\n\n/**\n * Serializes an InvectDefinition to a readable .flow.ts file.\n *\n * The output uses the Option A (declarative) format from the SDK plan:\n * defineFlow({ name, nodes: [...], edges: [...] })\n *\n * NOTE: This is a standalone serializer — it doesn't depend on the SDK being\n * implemented yet. It generates the .flow.ts text directly from the definition JSON.\n * When the SDK ships, this will import the actual helpers instead.\n */\nexport function serializeFlowToTs(\n definition: FlowDefinitionJson,\n metadata: { name: string; description?: string; tags?: string[] },\n): string {\n const lines: string[] = [];\n\n // Collect which helper functions are needed\n const helpers = new Set<string>();\n const providerImports = new Map<string, Set<string>>();\n\n for (const node of definition.nodes) {\n const { helperName, providerNs } = resolveHelper(node.type);\n if (providerNs) {\n if (!providerImports.has(providerNs)) {\n providerImports.set(providerNs, new Set());\n }\n providerImports.get(providerNs)?.add(helperName);\n } else {\n helpers.add(helperName);\n }\n }\n\n // Always need defineFlow\n helpers.add('defineFlow');\n\n // Build import line\n const coreHelpers = [...helpers].sort();\n lines.push(`import { ${coreHelpers.join(', ')} } from '@invect/core/sdk';`);\n\n for (const [ns, _methods] of providerImports) {\n lines.push(`import { ${ns} } from '@invect/core/sdk/providers';`);\n }\n\n lines.push('');\n lines.push('export default defineFlow({');\n\n // Metadata\n lines.push(` name: ${JSON.stringify(metadata.name)},`);\n if (metadata.description) {\n lines.push(` description: ${JSON.stringify(metadata.description)},`);\n }\n if (metadata.tags && metadata.tags.length > 0) {\n lines.push(` tags: ${JSON.stringify(metadata.tags)},`);\n }\n\n // Nodes\n lines.push('');\n lines.push(' nodes: [');\n for (const node of definition.nodes) {\n const ref = node.referenceId || node.id;\n const { helperCall } = resolveHelper(node.type);\n const params = serializeParams(node.params);\n lines.push(` ${helperCall}(${JSON.stringify(ref)}, ${params}),`);\n lines.push('');\n }\n lines.push(' ],');\n\n // Edges (tuple shorthand)\n lines.push('');\n lines.push(' edges: [');\n for (const edge of definition.edges) {\n const source = resolveNodeRef(edge.source, definition.nodes);\n const target = resolveNodeRef(edge.target, definition.nodes);\n if (edge.sourceHandle) {\n lines.push(\n ` [${JSON.stringify(source)}, ${JSON.stringify(target)}, ${JSON.stringify(edge.sourceHandle)}],`,\n );\n } else {\n lines.push(` [${JSON.stringify(source)}, ${JSON.stringify(target)}],`);\n }\n }\n lines.push(' ],');\n\n lines.push('});');\n lines.push('');\n\n return lines.join('\\n');\n}\n\n// =============================================================================\n// Helpers\n// =============================================================================\n\ninterface FlowDefinitionJson {\n nodes: Array<{\n id: string;\n type: string;\n label?: string;\n referenceId?: string;\n position?: { x: number; y: number };\n params: Record<string, unknown>;\n mapper?: Record<string, unknown>;\n }>;\n edges: Array<{\n id: string;\n source: string;\n target: string;\n sourceHandle?: string;\n targetHandle?: string;\n }>;\n}\n\n/** Map action IDs to SDK helper function names */\nconst ACTION_TO_HELPER: Record<string, { helperName: string; providerNs?: string }> = {\n 'core.input': { helperName: 'input' },\n 'core.output': { helperName: 'output' },\n 'core.model': { helperName: 'model' },\n 'core.jq': { helperName: 'jq' },\n 'core.if_else': { helperName: 'ifElse' },\n 'core.template_string': { helperName: 'template' },\n 'core.javascript': { helperName: 'javascript' },\n 'core.loop': { helperName: 'loop' },\n 'http.request': { helperName: 'httpRequest' },\n AGENT: { helperName: 'agent' },\n};\n\nfunction resolveHelper(nodeType: string): {\n helperName: string;\n helperCall: string;\n providerNs?: string;\n} {\n const known = ACTION_TO_HELPER[nodeType];\n if (known) {\n return {\n helperName: known.helperName,\n helperCall: known.providerNs ? `${known.providerNs}.${known.helperName}` : known.helperName,\n providerNs: known.providerNs,\n };\n }\n\n // For provider actions like \"gmail.send_message\" → gmail.sendMessage\n const dotIdx = nodeType.indexOf('.');\n if (dotIdx > 0) {\n const ns = nodeType.substring(0, dotIdx);\n const action = nodeType.substring(dotIdx + 1);\n const camel = snakeToCamel(action);\n return {\n helperName: camel,\n helperCall: `${ns}.${camel}`,\n providerNs: ns,\n };\n }\n\n // Unknown type — use generic node() helper\n return { helperName: 'node', helperCall: 'node' };\n}\n\nfunction snakeToCamel(s: string): string {\n return s.replace(/_([a-z])/g, (_, c) => c.toUpperCase());\n}\n\n/** Resolve a node ID (e.g. \"node-classify\") back to its referenceId (\"classify\") */\nfunction resolveNodeRef(nodeId: string, nodes: FlowDefinitionJson['nodes']): string {\n const node = nodes.find((n) => n.id === nodeId);\n if (node?.referenceId) {\n return node.referenceId;\n }\n // Strip \"node-\" prefix if present\n if (nodeId.startsWith('node-')) {\n return nodeId.substring(5);\n }\n return nodeId;\n}\n\n/** Serialize params object to formatted string, filtering out credentials by ID */\nfunction serializeParams(params: Record<string, unknown>): string {\n const cleaned = { ...params };\n\n // Replace credential IDs with symbolic env references\n if (typeof cleaned.credentialId === 'string' && !cleaned.credentialId.startsWith('{{')) {\n cleaned.credentialId = `{{env.${toEnvName(cleaned.credentialId)}}}`;\n }\n\n return formatObject(cleaned, 4);\n}\n\nfunction toEnvName(credentialId: string): string {\n // \"cred_openai_123\" → \"OPENAI_CREDENTIAL\"\n // Strip common prefixes, uppercase, append CREDENTIAL\n const name = credentialId\n .replace(/^cred[_-]?/i, '')\n .replace(/[_-]?\\d+$/g, '')\n .toUpperCase();\n return name ? `${name}_CREDENTIAL` : 'CREDENTIAL';\n}\n\n/** Format a JS value as readable code (not JSON — no quoting keys where unnecessary) */\nfunction formatObject(value: unknown, indent: number): string {\n if (value === null || value === undefined) {\n return 'null';\n }\n if (typeof value === 'string') {\n return JSON.stringify(value);\n }\n if (typeof value === 'number' || typeof value === 'boolean') {\n return String(value);\n }\n\n if (Array.isArray(value)) {\n if (value.length === 0) {\n return '[]';\n }\n if (value.every((v) => typeof v === 'string' || typeof v === 'number')) {\n return `[${value.map((v) => JSON.stringify(v)).join(', ')}]`;\n }\n const items = value.map((v) => `${' '.repeat(indent + 2)}${formatObject(v, indent + 2)}`);\n return `[\\n${items.join(',\\n')}\\n${' '.repeat(indent)}]`;\n }\n\n if (typeof value === 'object') {\n const obj = value as Record<string, unknown>;\n const entries = Object.entries(obj).filter(([, v]) => v !== undefined);\n if (entries.length === 0) {\n return '{}';\n }\n\n const lines = entries.map(([key, val]) => {\n const k = isValidIdentifier(key) ? key : JSON.stringify(key);\n return `${' '.repeat(indent + 2)}${k}: ${formatObject(val, indent + 2)}`;\n });\n return `{\\n${lines.join(',\\n')}\\n${' '.repeat(indent)}}`;\n }\n\n return JSON.stringify(value);\n}\n\nfunction isValidIdentifier(s: string): boolean {\n return /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(s);\n}\n","// =============================================================================\n// Version Control Sync Service — orchestrates push/pull/publish operations\n// =============================================================================\n\nimport { randomUUID } from 'node:crypto';\n\nimport type { GitProvider } from './git-provider';\nimport type { PluginDatabaseApi } from '@invect/core';\nimport type { VersionControlPluginOptions } from './types';\nimport type {\n VcSyncConfig,\n VcSyncHistoryRecord,\n VcSyncResult,\n VcSyncStatus,\n ConfigureSyncInput,\n} from '../shared/types';\nimport { serializeFlowToTs } from './flow-serializer';\n\ninterface FlowRow {\n id: string;\n name: string;\n description: string | null;\n tags: string | null;\n}\n\ninterface FlowVersionRow {\n flowId: string;\n version: number;\n invectDefinition: string;\n}\n\ntype Logger = {\n debug(msg: string, meta?: Record<string, unknown>): void;\n info(msg: string, meta?: Record<string, unknown>): void;\n warn(msg: string, meta?: Record<string, unknown>): void;\n error(msg: string, meta?: Record<string, unknown>): void;\n};\n\nexport class VcSyncService {\n constructor(\n private provider: GitProvider,\n private options: VersionControlPluginOptions,\n private logger: Logger,\n ) {}\n\n // =========================================================================\n // Configuration\n // =========================================================================\n\n async configureSyncForFlow(\n db: PluginDatabaseApi,\n flowId: string,\n input: ConfigureSyncInput,\n ): Promise<VcSyncConfig> {\n // Check if flow exists\n const flows = await db.query<FlowRow>('SELECT id, name FROM flows WHERE id = ?', [flowId]);\n if (flows.length === 0) {\n throw new Error(`Flow not found: ${flowId}`);\n }\n\n const flow = flows[0];\n const id = randomUUID();\n const now = new Date().toISOString();\n\n const repo = input.repo ?? this.options.repo;\n const branch = input.branch ?? this.options.defaultBranch ?? 'main';\n const mode = input.mode ?? this.options.mode ?? 'direct-commit';\n const syncDirection = input.syncDirection ?? this.options.syncDirection ?? 'push';\n const filePath = input.filePath ?? this.buildFilePath(flow.name);\n\n // Upsert — delete existing config for this flow first\n await db.execute('DELETE FROM vc_sync_config WHERE flow_id = ?', [flowId]);\n\n await db.execute(\n `INSERT INTO vc_sync_config (id, flow_id, provider, repo, branch, file_path, mode, sync_direction, enabled, created_at, updated_at)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,\n [id, flowId, this.provider.id, repo, branch, filePath, mode, syncDirection, true, now, now],\n );\n\n return this.getSyncConfig(db, flowId) as Promise<VcSyncConfig>;\n }\n\n async getSyncConfig(db: PluginDatabaseApi, flowId: string): Promise<VcSyncConfig | null> {\n const rows = await db.query<VcSyncConfigRow>('SELECT * FROM vc_sync_config WHERE flow_id = ?', [\n flowId,\n ]);\n if (rows.length === 0) {\n return null;\n }\n return mapSyncConfigRow(rows[0]);\n }\n\n async disconnectFlow(db: PluginDatabaseApi, flowId: string): Promise<void> {\n const config = await this.getSyncConfig(db, flowId);\n if (!config) {\n return;\n }\n\n // If there's an active PR, close it\n if (config.activePrNumber) {\n try {\n await this.provider.closePullRequest(\n config.repo,\n config.activePrNumber,\n 'Sync disconnected — flow unlinked from version control.',\n );\n } catch (err) {\n this.logger.warn('Failed to close PR on disconnect', { error: (err as Error).message });\n }\n }\n\n // If there's a draft branch, try to clean it up\n if (config.draftBranch) {\n try {\n await this.provider.deleteBranch(config.repo, config.draftBranch);\n } catch {\n // Ignore — branch may already be deleted\n }\n }\n\n await db.execute('DELETE FROM vc_sync_config WHERE flow_id = ?', [flowId]);\n }\n\n // =========================================================================\n // Push (DB → Remote)\n // =========================================================================\n\n async pushFlow(db: PluginDatabaseApi, flowId: string, identity?: string): Promise<VcSyncResult> {\n const config = await this.requireConfig(db, flowId);\n const { content, version } = await this.exportFlow(db, flowId);\n\n try {\n if (config.mode === 'direct-commit') {\n return await this.directCommit(db, config, content, version, identity);\n } else if (config.mode === 'pr-per-save') {\n return await this.commitToPrBranch(db, config, content, version, identity, true);\n } else {\n // pr-per-publish: commit to draft branch, no PR yet\n return await this.commitToDraftBranch(db, config, content, version, identity);\n }\n } catch (err) {\n const message = (err as Error).message;\n\n // SHA mismatch = conflict\n if (message.includes('409') || message.includes('sha')) {\n await this.recordHistory(db, flowId, 'conflict', { version, message, createdBy: identity });\n return {\n success: false,\n error: 'Conflict: remote file has changed. Use force-push or force-pull.',\n action: 'conflict',\n };\n }\n\n throw err;\n }\n }\n\n async forcePushFlow(\n db: PluginDatabaseApi,\n flowId: string,\n identity?: string,\n ): Promise<VcSyncResult> {\n const config = await this.requireConfig(db, flowId);\n const { content, version } = await this.exportFlow(db, flowId);\n\n // Get current remote SHA (if file exists) to force update\n const remote = await this.provider.getFileContent(config.repo, config.filePath, config.branch);\n const sha = remote?.sha;\n\n const result = await this.provider.createOrUpdateFile(\n config.repo,\n config.filePath,\n content,\n `chore(flow): force-push ${this.flowFileName(config.filePath)} v${version}`,\n { branch: config.branch, sha },\n );\n\n await this.updateConfigAfterSync(db, flowId, result.commitSha, version);\n await this.recordHistory(db, flowId, 'push', {\n commitSha: result.commitSha,\n version,\n message: 'Force push (local wins)',\n createdBy: identity,\n });\n\n return { success: true, commitSha: result.commitSha, action: 'push' };\n }\n\n // =========================================================================\n // Pull (Remote → DB)\n // =========================================================================\n\n async pullFlow(db: PluginDatabaseApi, flowId: string, identity?: string): Promise<VcSyncResult> {\n const config = await this.requireConfig(db, flowId);\n const remote = await this.provider.getFileContent(config.repo, config.filePath, config.branch);\n\n if (!remote) {\n return { success: false, error: 'Remote file not found', action: 'pull' };\n }\n\n // Check if we're already in sync\n if (config.lastCommitSha && remote.sha === config.lastCommitSha) {\n return { success: true, action: 'pull' }; // Already up to date\n }\n\n await this.importFlowContent(db, flowId, remote.content, identity);\n await this.updateConfigAfterSync(db, flowId, remote.sha, null);\n await this.recordHistory(db, flowId, 'pull', {\n commitSha: remote.sha,\n message: 'Pulled from remote',\n createdBy: identity,\n });\n\n return { success: true, commitSha: remote.sha, action: 'pull' };\n }\n\n async forcePullFlow(\n db: PluginDatabaseApi,\n flowId: string,\n identity?: string,\n ): Promise<VcSyncResult> {\n // Same as pull but ignores SHA check — always overwrites local\n const config = await this.requireConfig(db, flowId);\n const remote = await this.provider.getFileContent(config.repo, config.filePath, config.branch);\n\n if (!remote) {\n return { success: false, error: 'Remote file not found', action: 'pull' };\n }\n\n await this.importFlowContent(db, flowId, remote.content, identity);\n await this.updateConfigAfterSync(db, flowId, remote.sha, null);\n await this.recordHistory(db, flowId, 'pull', {\n commitSha: remote.sha,\n message: 'Force pull (remote wins)',\n createdBy: identity,\n });\n\n return { success: true, commitSha: remote.sha, action: 'pull' };\n }\n\n // =========================================================================\n // Publish (pr-per-publish mode — open PR from draft branch)\n // =========================================================================\n\n async publishFlow(\n db: PluginDatabaseApi,\n flowId: string,\n identity?: string,\n ): Promise<VcSyncResult> {\n const config = await this.requireConfig(db, flowId);\n\n if (config.mode !== 'pr-per-publish') {\n return {\n success: false,\n error: 'Publish is only available in pr-per-publish mode',\n action: 'pr-created',\n };\n }\n\n if (!config.draftBranch) {\n return {\n success: false,\n error: 'No draft branch found — push changes first',\n action: 'pr-created',\n };\n }\n\n // Check if there's already an active PR\n if (config.activePrNumber) {\n const pr = await this.provider.getPullRequest(config.repo, config.activePrNumber);\n if (pr.state === 'open') {\n return {\n success: true,\n prNumber: config.activePrNumber,\n prUrl: config.activePrUrl ?? undefined,\n action: 'pr-created',\n };\n }\n // PR was closed/merged — clear it and create a new one\n }\n\n const fileName = this.flowFileName(config.filePath);\n const pr = await this.provider.createPullRequest(config.repo, {\n title: `feat(flow): publish ${fileName}`,\n body: `Automated PR from Invect — publishing flow changes for \\`${fileName}\\`.`,\n head: config.draftBranch,\n base: config.branch,\n });\n\n await db.execute(\n 'UPDATE vc_sync_config SET active_pr_number = ?, active_pr_url = ?, updated_at = ? WHERE flow_id = ?',\n [pr.number, pr.url, new Date().toISOString(), flowId],\n );\n\n await this.recordHistory(db, flowId, 'pr-created', {\n prNumber: pr.number,\n message: `PR #${pr.number} created`,\n createdBy: identity,\n });\n\n return { success: true, prNumber: pr.number, prUrl: pr.url, action: 'pr-created' };\n }\n\n // =========================================================================\n // Status & History\n // =========================================================================\n\n async getFlowSyncStatus(\n db: PluginDatabaseApi,\n flowId: string,\n ): Promise<{\n status: VcSyncStatus;\n config: VcSyncConfig | null;\n lastSync: VcSyncHistoryRecord | null;\n }> {\n const config = await this.getSyncConfig(db, flowId);\n if (!config) {\n return { status: 'not-connected', config: null, lastSync: null };\n }\n\n const history = await db.query<VcSyncHistoryRow>(\n 'SELECT * FROM vc_sync_history WHERE flow_id = ? ORDER BY created_at DESC LIMIT 1',\n [flowId],\n );\n\n const lastSync = history.length > 0 ? mapHistoryRow(history[0]) : null;\n\n let status: VcSyncStatus = 'synced';\n if (!config.enabled) {\n status = 'not-connected';\n } else if (lastSync?.action === 'conflict') {\n status = 'conflict';\n } else if (!config.lastSyncedAt) {\n status = 'pending';\n } else {\n // Check if there are newer versions than what was synced\n const versions = await db.query<{ version: number }>(\n 'SELECT MAX(version) as version FROM flow_versions WHERE flow_id = ?',\n [flowId],\n );\n const latestVersion = versions[0]?.version;\n if (latestVersion && config.lastSyncedVersion && latestVersion > config.lastSyncedVersion) {\n status = 'pending';\n }\n }\n\n return { status, config, lastSync };\n }\n\n async getSyncHistory(\n db: PluginDatabaseApi,\n flowId: string,\n limit = 20,\n ): Promise<VcSyncHistoryRecord[]> {\n const rows = await db.query<VcSyncHistoryRow>(\n 'SELECT * FROM vc_sync_history WHERE flow_id = ? ORDER BY created_at DESC LIMIT ?',\n [flowId, limit],\n );\n return rows.map(mapHistoryRow);\n }\n\n async listSyncedFlows(\n db: PluginDatabaseApi,\n ): Promise<Array<VcSyncConfig & { flowName: string }>> {\n const rows = await db.query<VcSyncConfigRow & { flow_name: string }>(\n `SELECT vc_sync_config.*, flows.name as flow_name\n FROM vc_sync_config\n JOIN flows ON flows.id = vc_sync_config.flow_id\n ORDER BY vc_sync_config.updated_at DESC`,\n );\n return rows.map((r) => ({ ...mapSyncConfigRow(r), flowName: r.flow_name }));\n }\n\n // =========================================================================\n // Flow deletion hook\n // =========================================================================\n\n async onFlowDeleted(db: PluginDatabaseApi, flowId: string): Promise<void> {\n const config = await this.getSyncConfig(db, flowId);\n if (!config) {\n return;\n }\n\n // Delete the file from the remote\n try {\n const remote = await this.provider.getFileContent(\n config.repo,\n config.filePath,\n config.branch,\n );\n if (remote) {\n await this.provider.deleteFile(\n config.repo,\n config.filePath,\n `chore(flow): delete ${this.flowFileName(config.filePath)}`,\n { branch: config.branch, sha: remote.sha },\n );\n this.logger.info('Deleted flow file from remote', { flowId, filePath: config.filePath });\n }\n } catch (err) {\n this.logger.warn('Failed to delete flow file from remote', {\n flowId,\n error: (err as Error).message,\n });\n }\n\n // Close active PR if any\n if (config.activePrNumber) {\n try {\n await this.provider.closePullRequest(\n config.repo,\n config.activePrNumber,\n 'Flow deleted — closing PR.',\n );\n } catch {\n // Ignore\n }\n }\n\n // Clean up draft branch\n if (config.draftBranch) {\n try {\n await this.provider.deleteBranch(config.repo, config.draftBranch);\n } catch {\n // Ignore\n }\n }\n\n // DB records cascade-delete from the flows FK\n }\n\n // =========================================================================\n // Internal — commit strategies\n // =========================================================================\n\n private async directCommit(\n db: PluginDatabaseApi,\n config: VcSyncConfig,\n content: string,\n version: number,\n identity?: string,\n ): Promise<VcSyncResult> {\n const sha = config.lastCommitSha ?? undefined;\n\n // Try to get remote SHA if we don't have one (first push)\n let remoteSha = sha;\n if (!remoteSha) {\n const remote = await this.provider.getFileContent(\n config.repo,\n config.filePath,\n config.branch,\n );\n remoteSha = remote?.sha;\n }\n\n const result = await this.provider.createOrUpdateFile(\n config.repo,\n config.filePath,\n content,\n `chore(flow): update ${this.flowFileName(config.filePath)} v${version}`,\n { branch: config.branch, sha: remoteSha },\n );\n\n await this.updateConfigAfterSync(db, config.flowId, result.commitSha, version);\n await this.recordHistory(db, config.flowId, 'push', {\n commitSha: result.commitSha,\n version,\n message: `Direct commit v${version}`,\n createdBy: identity,\n });\n\n return { success: true, commitSha: result.commitSha, action: 'push' };\n }\n\n private async commitToPrBranch(\n db: PluginDatabaseApi,\n config: VcSyncConfig,\n content: string,\n version: number,\n identity?: string,\n openPr: boolean = true,\n ): Promise<VcSyncResult> {\n const branchName = config.draftBranch ?? `invect/flow/${this.flowSlug(config.filePath)}`;\n\n // Create branch if it doesn't exist\n const existing = await this.provider.getBranch(config.repo, branchName);\n if (!existing) {\n await this.provider.createBranch(config.repo, branchName, config.branch);\n }\n\n // Get current file SHA on the branch\n const remote = await this.provider.getFileContent(config.repo, config.filePath, branchName);\n\n const result = await this.provider.createOrUpdateFile(\n config.repo,\n config.filePath,\n content,\n `chore(flow): update ${this.flowFileName(config.filePath)} v${version}`,\n { branch: branchName, sha: remote?.sha },\n );\n\n // Save draft branch reference\n await db.execute(\n 'UPDATE vc_sync_config SET draft_branch = ?, updated_at = ? WHERE flow_id = ?',\n [branchName, new Date().toISOString(), config.flowId],\n );\n\n let prNumber = config.activePrNumber ?? undefined;\n let prUrl = config.activePrUrl ?? undefined;\n\n // Open PR if needed\n if (openPr && !prNumber) {\n const pr = await this.provider.createPullRequest(config.repo, {\n title: `feat(flow): update ${this.flowFileName(config.filePath)}`,\n body: `Automated PR from Invect — flow changes for \\`${this.flowFileName(config.filePath)}\\`.`,\n head: branchName,\n base: config.branch,\n });\n prNumber = pr.number;\n prUrl = pr.url;\n\n await db.execute(\n 'UPDATE vc_sync_config SET active_pr_number = ?, active_pr_url = ?, updated_at = ? WHERE flow_id = ?',\n [prNumber, prUrl, new Date().toISOString(), config.flowId],\n );\n\n await this.recordHistory(db, config.flowId, 'pr-created', {\n commitSha: result.commitSha,\n prNumber,\n version,\n message: `PR #${prNumber} created`,\n createdBy: identity,\n });\n } else {\n await this.recordHistory(db, config.flowId, 'push', {\n commitSha: result.commitSha,\n version,\n message: `Updated PR branch v${version}`,\n createdBy: identity,\n });\n }\n\n await this.updateConfigAfterSync(db, config.flowId, result.commitSha, version);\n\n return {\n success: true,\n commitSha: result.commitSha,\n prNumber,\n prUrl,\n action: prNumber ? 'pr-created' : 'push',\n };\n }\n\n private async commitToDraftBranch(\n db: PluginDatabaseApi,\n config: VcSyncConfig,\n content: string,\n version: number,\n identity?: string,\n ): Promise<VcSyncResult> {\n // Same as PR branch commit but without opening a PR\n return this.commitToPrBranch(db, config, content, version, identity, false);\n }\n\n // =========================================================================\n // Internal — flow export / import\n // =========================================================================\n\n private async exportFlow(\n db: PluginDatabaseApi,\n flowId: string,\n ): Promise<{ content: string; version: number }> {\n const flows = await db.query<FlowRow>(\n 'SELECT id, name, description, tags FROM flows WHERE id = ?',\n [flowId],\n );\n if (flows.length === 0) {\n throw new Error(`Flow not found: ${flowId}`);\n }\n const flow = flows[0];\n\n const versions = await db.query<FlowVersionRow>(\n 'SELECT flow_id, version, invect_definition FROM flow_versions WHERE flow_id = ? ORDER BY version DESC LIMIT 1',\n [flowId],\n );\n if (versions.length === 0) {\n throw new Error(`No versions found for flow: ${flowId}`);\n }\n const fv = versions[0];\n\n const definition =\n typeof fv.invectDefinition === 'string'\n ? JSON.parse(fv.invectDefinition)\n : fv.invectDefinition;\n\n let tags: string[] | undefined;\n if (flow.tags) {\n try {\n tags = typeof flow.tags === 'string' ? JSON.parse(flow.tags) : flow.tags;\n } catch {\n tags = undefined;\n }\n }\n\n const content = serializeFlowToTs(definition, {\n name: flow.name,\n description: flow.description ?? undefined,\n tags,\n });\n\n return { content, version: fv.version };\n }\n\n private async importFlowContent(\n db: PluginDatabaseApi,\n flowId: string,\n content: string,\n identity?: string,\n ): Promise<void> {\n // 1. Write .flow.ts content to a temp file\n const { writeFileSync, unlinkSync, mkdtempSync } = await import('node:fs');\n const { join } = await import('node:path');\n const { tmpdir } = await import('node:os');\n\n const tmpDir = mkdtempSync(join(tmpdir(), 'invect-vc-'));\n const tmpFile = join(tmpDir, 'import.flow.ts');\n\n try {\n writeFileSync(tmpFile, content, 'utf-8');\n\n // 2. Load the .flow.ts file via jiti (resolves defineFlow + helpers)\n const { createJiti } = await import('jiti');\n const jiti = createJiti(import.meta.url, { interopDefault: true });\n const result = await jiti.import(tmpFile);\n\n // The file's default export should be an InvectDefinition (from defineFlow)\n const definition = (result as Record<string, unknown>).default ?? result;\n\n if (\n !definition ||\n typeof definition !== 'object' ||\n !('nodes' in definition) ||\n !('edges' in definition)\n ) {\n throw new Error(\n 'Imported .flow.ts file did not produce a valid InvectDefinition. ' +\n 'Expected an object with \"nodes\" and \"edges\" arrays.',\n );\n }\n\n // 3. Get current latest version number\n const versions = await db.query<{ version: number }>(\n 'SELECT MAX(version) as version FROM flow_versions WHERE flow_id = ?',\n [flowId],\n );\n const nextVersion = (versions[0]?.version ?? 0) + 1;\n\n // 4. Insert new flow version\n const defJson = typeof definition === 'string' ? definition : JSON.stringify(definition);\n\n await db.execute(\n `INSERT INTO flow_versions (flow_id, version, invect_definition, created_at, created_by)\n VALUES (?, ?, ?, ?, ?)`,\n [flowId, nextVersion, defJson, new Date().toISOString(), identity ?? null],\n );\n\n // 5. Update flow's live version\n await db.execute('UPDATE flows SET live_version_number = ?, updated_at = ? WHERE id = ?', [\n nextVersion,\n new Date().toISOString(),\n flowId,\n ]);\n\n this.logger.info('Flow imported from remote', {\n flowId,\n version: nextVersion,\n });\n } finally {\n // Clean up temp file\n try {\n unlinkSync(tmpFile);\n const { rmdirSync } = await import('node:fs');\n rmdirSync(tmpDir);\n } catch {\n // Ignore cleanup errors\n }\n }\n }\n\n // =========================================================================\n // Internal — helpers\n // =========================================================================\n\n private buildFilePath(flowName: string): string {\n const basePath = this.options.path ?? 'workflows/';\n const slug = flowName\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-|-$/g, '');\n return `${basePath}${slug}.flow.ts`;\n }\n\n private flowFileName(filePath: string): string {\n return filePath.split('/').pop() ?? filePath;\n }\n\n private flowSlug(filePath: string): string {\n const name = this.flowFileName(filePath);\n return name.replace(/\\.flow\\.ts$/, '');\n }\n\n private async requireConfig(db: PluginDatabaseApi, flowId: string): Promise<VcSyncConfig> {\n const config = await this.getSyncConfig(db, flowId);\n if (!config) {\n throw new Error(`Flow ${flowId} is not connected to version control`);\n }\n if (!config.enabled) {\n throw new Error(`Version control sync is disabled for flow ${flowId}`);\n }\n return config;\n }\n\n private async updateConfigAfterSync(\n db: PluginDatabaseApi,\n flowId: string,\n commitSha: string,\n version: number | null,\n ): Promise<void> {\n const now = new Date().toISOString();\n if (version !== null) {\n await db.execute(\n 'UPDATE vc_sync_config SET last_synced_at = ?, last_commit_sha = ?, last_synced_version = ?, updated_at = ? WHERE flow_id = ?',\n [now, commitSha, version, now, flowId],\n );\n } else {\n await db.execute(\n 'UPDATE vc_sync_config SET last_synced_at = ?, last_commit_sha = ?, updated_at = ? WHERE flow_id = ?',\n [now, commitSha, now, flowId],\n );\n }\n }\n\n private async recordHistory(\n db: PluginDatabaseApi,\n flowId: string,\n action: import('../shared/types').VcSyncAction,\n opts: {\n commitSha?: string;\n prNumber?: number;\n version?: number;\n message?: string;\n createdBy?: string;\n },\n ): Promise<void> {\n await db.execute(\n `INSERT INTO vc_sync_history (id, flow_id, action, commit_sha, pr_number, version, message, created_at, created_by)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,\n [\n randomUUID(),\n flowId,\n action,\n opts.commitSha ?? null,\n opts.prNumber ?? null,\n opts.version ?? null,\n opts.message ?? null,\n new Date().toISOString(),\n opts.createdBy ?? null,\n ],\n );\n }\n}\n\n// =============================================================================\n// Row mappers (snake_case DB rows → camelCase types)\n// =============================================================================\n\ninterface VcSyncConfigRow {\n id: string;\n flow_id: string;\n provider: string;\n repo: string;\n branch: string;\n file_path: string;\n mode: string;\n sync_direction: string;\n last_synced_at: string | null;\n last_commit_sha: string | null;\n last_synced_version: number | null;\n draft_branch: string | null;\n active_pr_number: number | null;\n active_pr_url: string | null;\n enabled: boolean | number;\n created_at: string;\n updated_at: string;\n}\n\ninterface VcSyncHistoryRow {\n id: string;\n flow_id: string;\n action: string;\n commit_sha: string | null;\n pr_number: number | null;\n version: number | null;\n message: string | null;\n created_at: string;\n created_by: string | null;\n}\n\nfunction mapSyncConfigRow(r: VcSyncConfigRow): VcSyncConfig {\n return {\n id: r.id,\n flowId: r.flow_id,\n provider: r.provider,\n repo: r.repo,\n branch: r.branch,\n filePath: r.file_path,\n mode: r.mode as VcSyncConfig['mode'],\n syncDirection: r.sync_direction as VcSyncConfig['syncDirection'],\n lastSyncedAt: r.last_synced_at,\n lastCommitSha: r.last_commit_sha,\n lastSyncedVersion: r.last_synced_version,\n draftBranch: r.draft_branch,\n activePrNumber: r.active_pr_number,\n activePrUrl: r.active_pr_url,\n enabled: r.enabled === true || r.enabled === 1,\n };\n}\n\nfunction mapHistoryRow(r: VcSyncHistoryRow): VcSyncHistoryRecord {\n return {\n id: r.id,\n flowId: r.flow_id,\n action: r.action as VcSyncHistoryRecord['action'],\n commitSha: r.commit_sha,\n prNumber: r.pr_number,\n version: r.version,\n message: r.message,\n createdAt: r.created_at,\n createdBy: r.created_by,\n };\n}\n","// =============================================================================\n// Version Control Plugin — Main Entry Point\n// =============================================================================\n\nimport type { InvectPlugin, PluginEndpointContext } from '@invect/core';\n\nimport type { VersionControlPluginOptions } from './types';\nimport type { ConfigureSyncInput } from '../shared/types';\nimport { VC_SCHEMA } from './schema';\nimport { VcSyncService } from './sync-service';\n\n/**\n * Create the Version Control plugin.\n *\n * Syncs Invect flows to a Git remote as readable `.flow.ts` files.\n *\n * ```ts\n * import { versionControl } from '@invect/version-control';\n * import { githubProvider } from '@invect/version-control/providers/github';\n *\n * new Invect({\n * plugins: [\n * versionControl({\n * provider: githubProvider({ auth: { type: 'token', token: process.env.GITHUB_TOKEN! } }),\n * repo: 'acme/workflows',\n * mode: 'pr-per-publish',\n * }),\n * ],\n * });\n * ```\n */\nexport function versionControl(options: VersionControlPluginOptions): InvectPlugin {\n let syncService: VcSyncService;\n let pluginLogger: { debug: Function; info: Function; warn: Function; error: Function } = console;\n\n return {\n id: 'version-control',\n name: 'Version Control',\n\n schema: VC_SCHEMA,\n\n setupInstructions:\n 'Run `npx invect-cli generate` then `npx invect-cli migrate` to create the vc_sync_config and vc_sync_history tables.',\n\n // =======================================================================\n // Initialization\n // =======================================================================\n\n init: async (ctx) => {\n pluginLogger = ctx.logger;\n syncService = new VcSyncService(options.provider, options, ctx.logger);\n ctx.logger.info(\n `Version control plugin initialized (provider: ${options.provider.id}, repo: ${options.repo})`,\n );\n },\n\n // =======================================================================\n // Endpoints\n // =======================================================================\n\n endpoints: [\n // -- Configure sync for a flow --\n {\n method: 'POST',\n path: '/vc/flows/:flowId/configure',\n handler: async (ctx: PluginEndpointContext) => {\n const { flowId } = ctx.params;\n const input = ctx.body as ConfigureSyncInput;\n const config = await syncService.configureSyncForFlow(ctx.database, flowId, input);\n return { status: 200, body: config };\n },\n },\n\n // -- Get sync status for a flow --\n {\n method: 'GET',\n path: '/vc/flows/:flowId/status',\n handler: async (ctx: PluginEndpointContext) => {\n const { flowId } = ctx.params;\n const status = await syncService.getFlowSyncStatus(ctx.database, flowId);\n return { status: 200, body: { flowId, ...status } };\n },\n },\n\n // -- Disconnect sync for a flow --\n {\n method: 'DELETE',\n path: '/vc/flows/:flowId/disconnect',\n handler: async (ctx: PluginEndpointContext) => {\n const { flowId } = ctx.params;\n await syncService.disconnectFlow(ctx.database, flowId);\n return { status: 200, body: { success: true } };\n },\n },\n\n // -- Push (DB → remote) --\n {\n method: 'POST',\n path: '/vc/flows/:flowId/push',\n handler: async (ctx: PluginEndpointContext) => {\n const { flowId } = ctx.params;\n const identity = ctx.identity?.id;\n const result = await syncService.pushFlow(ctx.database, flowId, identity);\n return { status: result.success ? 200 : 409, body: result };\n },\n },\n\n // -- Pull (remote → DB) --\n {\n method: 'POST',\n path: '/vc/flows/:flowId/pull',\n handler: async (ctx: PluginEndpointContext) => {\n const { flowId } = ctx.params;\n const identity = ctx.identity?.id;\n const result = await syncService.pullFlow(ctx.database, flowId, identity);\n return { status: result.success ? 200 : 404, body: result };\n },\n },\n\n // -- Publish (pr-per-publish mode) --\n {\n method: 'POST',\n path: '/vc/flows/:flowId/publish',\n handler: async (ctx: PluginEndpointContext) => {\n const { flowId } = ctx.params;\n const identity = ctx.identity?.id;\n const result = await syncService.publishFlow(ctx.database, flowId, identity);\n return { status: result.success ? 200 : 400, body: result };\n },\n },\n\n // -- Force push (conflict resolution — DB wins) --\n {\n method: 'POST',\n path: '/vc/flows/:flowId/force-push',\n handler: async (ctx: PluginEndpointContext) => {\n const { flowId } = ctx.params;\n const identity = ctx.identity?.id;\n const result = await syncService.forcePushFlow(ctx.database, flowId, identity);\n return { status: 200, body: result };\n },\n },\n\n // -- Force pull (conflict resolution — remote wins) --\n {\n method: 'POST',\n path: '/vc/flows/:flowId/force-pull',\n handler: async (ctx: PluginEndpointContext) => {\n const { flowId } = ctx.params;\n const identity = ctx.identity?.id;\n const result = await syncService.forcePullFlow(ctx.database, flowId, identity);\n return { status: result.success ? 200 : 404, body: result };\n },\n },\n\n // -- Bulk push all synced flows --\n {\n method: 'POST',\n path: '/vc/push-all',\n handler: async (ctx: PluginEndpointContext) => {\n const configs = await syncService.listSyncedFlows(ctx.database);\n const identity = ctx.identity?.id;\n const results = [];\n for (const config of configs) {\n if (!config.enabled) {\n continue;\n }\n try {\n const result = await syncService.pushFlow(ctx.database, config.flowId, identity);\n results.push({ flowId: config.flowId, flowName: config.flowName, ...result });\n } catch (err) {\n results.push({\n flowId: config.flowId,\n flowName: config.flowName,\n success: false,\n error: (err as Error).message,\n action: 'push' as const,\n });\n }\n }\n return { status: 200, body: { results } };\n },\n },\n\n // -- Bulk pull all synced flows --\n {\n method: 'POST',\n path: '/vc/pull-all',\n handler: async (ctx: PluginEndpointContext) => {\n const configs = await syncService.listSyncedFlows(ctx.database);\n const identity = ctx.identity?.id;\n const results = [];\n for (const config of configs) {\n if (!config.enabled) {\n continue;\n }\n try {\n const result = await syncService.pullFlow(ctx.database, config.flowId, identity);\n results.push({ flowId: config.flowId, flowName: config.flowName, ...result });\n } catch (err) {\n results.push({\n flowId: config.flowId,\n flowName: config.flowName,\n success: false,\n error: (err as Error).message,\n action: 'pull' as const,\n });\n }\n }\n return { status: 200, body: { results } };\n },\n },\n\n // -- Webhook receiver --\n {\n method: 'POST',\n path: '/vc/webhook',\n isPublic: true,\n handler: async (ctx: PluginEndpointContext) => {\n if (!options.webhookSecret) {\n return { status: 400, body: { error: 'Webhook secret not configured' } };\n }\n\n // Verify signature\n const signature = ctx.headers['x-hub-signature-256'] ?? '';\n const body = JSON.stringify(ctx.body);\n if (!options.provider.verifyWebhookSignature(body, signature, options.webhookSecret)) {\n return { status: 401, body: { error: 'Invalid webhook signature' } };\n }\n\n // Handle PR merge events\n const action = (ctx.body as Record<string, unknown>).action;\n const pullRequest = (ctx.body as Record<string, unknown>).pull_request as\n | { merged: boolean; number: number }\n | undefined;\n\n if (action === 'closed' && pullRequest?.merged) {\n await handlePrMerged(ctx.database, pullRequest.number);\n }\n\n return { status: 200, body: { received: true } };\n },\n },\n\n // -- List all synced flows --\n {\n method: 'GET',\n path: '/vc/flows',\n handler: async (ctx: PluginEndpointContext) => {\n const flows = await syncService.listSyncedFlows(ctx.database);\n return { status: 200, body: { flows } };\n },\n },\n\n // -- Get sync history for a flow --\n {\n method: 'GET',\n path: '/vc/flows/:flowId/history',\n handler: async (ctx: PluginEndpointContext) => {\n const { flowId } = ctx.params;\n const limit = ctx.query.limit ? parseInt(ctx.query.limit, 10) : 20;\n const history = await syncService.getSyncHistory(ctx.database, flowId, limit);\n return { status: 200, body: { flowId, history } };\n },\n },\n ],\n\n // =======================================================================\n // Hooks\n // =======================================================================\n\n // NOTE: Auto-sync on flow version creation is intentionally NOT implemented\n // as a hook. The onResponse hook context doesn't include database access.\n // Instead, auto-sync is handled by the frontend calling POST /vc/flows/:id/push\n // after saving a flow version. This matches the \"opt-in per flow\" decision —\n // only flows with an enabled vc_sync_config record get synced.\n\n hooks: {},\n };\n\n // =========================================================================\n // Webhook handlers (internal)\n // =========================================================================\n\n async function handlePrMerged(\n db: import('@invect/core').PluginDatabaseApi,\n prNumber: number,\n ): Promise<void> {\n // Find the sync config with this active PR\n const rows = await db.query<{ flow_id: string }>(\n 'SELECT flow_id FROM vc_sync_config WHERE active_pr_number = ?',\n [prNumber],\n );\n\n for (const row of rows) {\n // Clear PR state, update sync status\n await db.execute(\n `UPDATE vc_sync_config\n SET active_pr_number = NULL, active_pr_url = NULL, draft_branch = NULL, updated_at = ?\n WHERE flow_id = ?`,\n [new Date().toISOString(), row.flow_id],\n );\n\n // Record history\n const { randomUUID } = await import('node:crypto');\n await db.execute(\n `INSERT INTO vc_sync_history (id, flow_id, action, pr_number, message, created_at)\n VALUES (?, ?, ?, ?, ?, ?)`,\n [\n randomUUID(),\n row.flow_id,\n 'pr-merged',\n prNumber,\n `PR #${prNumber} merged`,\n new Date().toISOString(),\n ],\n );\n\n // Try to clean up the draft branch\n const configs = await db.query<{ draft_branch: string | null; repo: string }>(\n 'SELECT draft_branch, repo FROM vc_sync_config WHERE flow_id = ?',\n [row.flow_id],\n );\n if (configs[0]?.draft_branch) {\n try {\n await options.provider.deleteBranch(configs[0].repo, configs[0].draft_branch);\n } catch {\n // Branch may already be deleted by the merge\n }\n }\n\n pluginLogger.info('PR merged — sync updated', { flowId: row.flow_id, prNumber });\n }\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../../src/backend/schema.ts","../../src/backend/flow-serializer.ts","../../src/backend/sync-service.ts","../../src/backend/validation.ts","../../src/backend/plugin.ts"],"sourcesContent":["// =============================================================================\n// Version Control Plugin — Database Schema (abstract, dialect-agnostic)\n// =============================================================================\n\nimport type { InvectPluginSchema } from '@invect/core';\n\nconst SYNC_MODES = ['direct-commit', 'pr-per-save', 'pr-per-publish'] as const;\nconst SYNC_DIRECTIONS = ['push', 'pull', 'bidirectional'] as const;\nconst SYNC_ACTIONS = ['push', 'pull', 'pr-created', 'pr-merged', 'conflict'] as const;\n\nexport const VC_SCHEMA: InvectPluginSchema = {\n vcSyncConfig: {\n tableName: 'vc_sync_config',\n order: 10,\n fields: {\n id: { type: 'string', primaryKey: true },\n flowId: {\n type: 'string',\n required: true,\n unique: true,\n references: { table: 'flows', field: 'id', onDelete: 'cascade' },\n index: true,\n },\n provider: { type: 'string', required: true },\n repo: { type: 'string', required: true },\n branch: { type: 'string', required: true },\n filePath: { type: 'string', required: true },\n mode: { type: [...SYNC_MODES], required: true },\n syncDirection: { type: [...SYNC_DIRECTIONS], required: true, defaultValue: 'push' },\n lastSyncedAt: { type: 'date', required: false },\n lastCommitSha: { type: 'string', required: false },\n lastSyncedVersion: { type: 'number', required: false },\n draftBranch: { type: 'string', required: false },\n activePrNumber: { type: 'number', required: false },\n activePrUrl: { type: 'string', required: false },\n enabled: { type: 'boolean', required: true, defaultValue: true },\n createdAt: { type: 'date', required: true, defaultValue: 'now()' },\n updatedAt: { type: 'date', required: true, defaultValue: 'now()' },\n },\n },\n\n vcSyncHistory: {\n tableName: 'vc_sync_history',\n order: 20,\n fields: {\n id: { type: 'uuid', primaryKey: true, defaultValue: 'uuid()' },\n flowId: {\n type: 'string',\n required: true,\n references: { table: 'flows', field: 'id', onDelete: 'cascade' },\n index: true,\n },\n action: { type: [...SYNC_ACTIONS], required: true },\n commitSha: { type: 'string', required: false },\n prNumber: { type: 'number', required: false },\n version: { type: 'number', required: false },\n message: { type: 'string', required: false },\n createdAt: { type: 'date', required: true, defaultValue: 'now()' },\n createdBy: { type: 'string', required: false },\n },\n },\n};\n","// =============================================================================\n// Flow Serializer — converts InvectDefinition JSON ↔ .flow.ts file content\n// =============================================================================\n\n/**\n * Serializes an InvectDefinition to a readable .flow.ts file.\n *\n * The output uses the Option A (declarative) format from the SDK plan:\n * defineFlow({ name, nodes: [...], edges: [...] })\n *\n * NOTE: This is a standalone serializer — it doesn't depend on the SDK being\n * implemented yet. It generates the .flow.ts text directly from the definition JSON.\n * When the SDK ships, this will import the actual helpers instead.\n */\nexport function serializeFlowToTs(\n definition: FlowDefinitionJson,\n metadata: { name: string; description?: string; tags?: string[] },\n): string {\n const lines: string[] = [];\n\n // Collect which helper functions are needed\n const helpers = new Set<string>();\n const providerImports = new Map<string, Set<string>>();\n\n for (const node of definition.nodes) {\n const { helperName, providerNs } = resolveHelper(node.type);\n if (providerNs) {\n if (!providerImports.has(providerNs)) {\n providerImports.set(providerNs, new Set());\n }\n providerImports.get(providerNs)?.add(helperName);\n } else {\n helpers.add(helperName);\n }\n }\n\n // Always need defineFlow\n helpers.add('defineFlow');\n\n // Build import line\n const coreHelpers = [...helpers].sort();\n lines.push(`import { ${coreHelpers.join(', ')} } from '@invect/core/sdk';`);\n\n for (const [ns, _methods] of providerImports) {\n lines.push(`import { ${ns} } from '@invect/core/sdk/providers';`);\n }\n\n lines.push('');\n lines.push('export default defineFlow({');\n\n // Metadata\n lines.push(` name: ${JSON.stringify(metadata.name)},`);\n if (metadata.description) {\n lines.push(` description: ${JSON.stringify(metadata.description)},`);\n }\n if (metadata.tags && metadata.tags.length > 0) {\n lines.push(` tags: ${JSON.stringify(metadata.tags)},`);\n }\n\n // Nodes\n lines.push('');\n lines.push(' nodes: [');\n for (const node of definition.nodes) {\n const ref = node.referenceId || node.id;\n const { helperCall } = resolveHelper(node.type);\n const params = serializeParams(node.params);\n lines.push(` ${helperCall}(${JSON.stringify(ref)}, ${params}),`);\n lines.push('');\n }\n lines.push(' ],');\n\n // Edges (tuple shorthand)\n lines.push('');\n lines.push(' edges: [');\n for (const edge of definition.edges) {\n const source = resolveNodeRef(edge.source, definition.nodes);\n const target = resolveNodeRef(edge.target, definition.nodes);\n if (edge.sourceHandle) {\n lines.push(\n ` [${JSON.stringify(source)}, ${JSON.stringify(target)}, ${JSON.stringify(edge.sourceHandle)}],`,\n );\n } else {\n lines.push(` [${JSON.stringify(source)}, ${JSON.stringify(target)}],`);\n }\n }\n lines.push(' ],');\n\n lines.push('});');\n lines.push('');\n\n // Embed machine-readable JSON for reliable round-tripping on import.\n // The static parser uses this to avoid eval/jiti.\n lines.push('/* @invect-definition');\n lines.push(JSON.stringify(definition));\n lines.push('*/');\n lines.push('');\n\n return lines.join('\\n');\n}\n\n// =============================================================================\n// Helpers\n// =============================================================================\n\ninterface FlowDefinitionJson {\n nodes: Array<{\n id: string;\n type: string;\n label?: string;\n referenceId?: string;\n position?: { x: number; y: number };\n params: Record<string, unknown>;\n mapper?: Record<string, unknown>;\n }>;\n edges: Array<{\n id: string;\n source: string;\n target: string;\n sourceHandle?: string;\n targetHandle?: string;\n }>;\n}\n\n/** Map action IDs to SDK helper function names */\nconst ACTION_TO_HELPER: Record<string, { helperName: string; providerNs?: string }> = {\n 'core.input': { helperName: 'input' },\n 'core.output': { helperName: 'output' },\n 'core.model': { helperName: 'model' },\n 'core.jq': { helperName: 'jq' },\n 'core.if_else': { helperName: 'ifElse' },\n 'core.template_string': { helperName: 'template' },\n 'core.javascript': { helperName: 'javascript' },\n 'core.loop': { helperName: 'loop' },\n 'http.request': { helperName: 'httpRequest' },\n AGENT: { helperName: 'agent' },\n};\n\nfunction resolveHelper(nodeType: string): {\n helperName: string;\n helperCall: string;\n providerNs?: string;\n} {\n const known = ACTION_TO_HELPER[nodeType];\n if (known) {\n return {\n helperName: known.helperName,\n helperCall: known.providerNs ? `${known.providerNs}.${known.helperName}` : known.helperName,\n providerNs: known.providerNs,\n };\n }\n\n // For provider actions like \"gmail.send_message\" → gmail.sendMessage\n const dotIdx = nodeType.indexOf('.');\n if (dotIdx > 0) {\n const ns = nodeType.substring(0, dotIdx);\n const action = nodeType.substring(dotIdx + 1);\n const camel = snakeToCamel(action);\n return {\n helperName: camel,\n helperCall: `${ns}.${camel}`,\n providerNs: ns,\n };\n }\n\n // Unknown type — use generic node() helper\n return { helperName: 'node', helperCall: 'node' };\n}\n\nfunction snakeToCamel(s: string): string {\n return s.replace(/_([a-z])/g, (_, c) => c.toUpperCase());\n}\n\n/** Resolve a node ID (e.g. \"node-classify\") back to its referenceId (\"classify\") */\nfunction resolveNodeRef(nodeId: string, nodes: FlowDefinitionJson['nodes']): string {\n const node = nodes.find((n) => n.id === nodeId);\n if (node?.referenceId) {\n return node.referenceId;\n }\n // Strip \"node-\" prefix if present\n if (nodeId.startsWith('node-')) {\n return nodeId.substring(5);\n }\n return nodeId;\n}\n\n/** Serialize params object to formatted string, filtering out credentials by ID */\nfunction serializeParams(params: Record<string, unknown>): string {\n const cleaned = { ...params };\n\n // Replace credential IDs with symbolic env references\n if (typeof cleaned.credentialId === 'string' && !cleaned.credentialId.startsWith('{{')) {\n cleaned.credentialId = `{{env.${toEnvName(cleaned.credentialId)}}}`;\n }\n\n return formatObject(cleaned, 4);\n}\n\nfunction toEnvName(credentialId: string): string {\n // \"cred_openai_123\" → \"OPENAI_CREDENTIAL\"\n // Strip common prefixes, uppercase, append CREDENTIAL\n const name = credentialId\n .replace(/^cred[_-]?/i, '')\n .replace(/[_-]?\\d+$/g, '')\n .toUpperCase();\n return name ? `${name}_CREDENTIAL` : 'CREDENTIAL';\n}\n\n/** Format a JS value as readable code (not JSON — no quoting keys where unnecessary) */\nfunction formatObject(value: unknown, indent: number): string {\n if (value === null || value === undefined) {\n return 'null';\n }\n if (typeof value === 'string') {\n return JSON.stringify(value);\n }\n if (typeof value === 'number' || typeof value === 'boolean') {\n return String(value);\n }\n\n if (Array.isArray(value)) {\n if (value.length === 0) {\n return '[]';\n }\n if (value.every((v) => typeof v === 'string' || typeof v === 'number')) {\n return `[${value.map((v) => JSON.stringify(v)).join(', ')}]`;\n }\n const items = value.map((v) => `${' '.repeat(indent + 2)}${formatObject(v, indent + 2)}`);\n return `[\\n${items.join(',\\n')}\\n${' '.repeat(indent)}]`;\n }\n\n if (typeof value === 'object') {\n const obj = value as Record<string, unknown>;\n const entries = Object.entries(obj).filter(([, v]) => v !== undefined);\n if (entries.length === 0) {\n return '{}';\n }\n\n const lines = entries.map(([key, val]) => {\n const k = isValidIdentifier(key) ? key : JSON.stringify(key);\n return `${' '.repeat(indent + 2)}${k}: ${formatObject(val, indent + 2)}`;\n });\n return `{\\n${lines.join(',\\n')}\\n${' '.repeat(indent)}}`;\n }\n\n return JSON.stringify(value);\n}\n\nfunction isValidIdentifier(s: string): boolean {\n return /^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(s);\n}\n","// =============================================================================\n// Version Control Sync Service — orchestrates push/pull/publish operations\n// =============================================================================\n\nimport { randomUUID } from 'node:crypto';\n\nimport type { GitProvider } from './git-provider';\nimport type { PluginDatabaseApi } from '@invect/core';\nimport type { VersionControlPluginOptions } from './types';\nimport type {\n VcSyncConfig,\n VcSyncHistoryRecord,\n VcSyncResult,\n VcSyncStatus,\n ConfigureSyncInput,\n} from '../shared/types';\nimport { serializeFlowToTs } from './flow-serializer';\n\ninterface FlowRow {\n id: string;\n name: string;\n description: string | null;\n tags: string | null;\n}\n\ninterface FlowVersionRow {\n flow_id: string;\n version: number;\n invect_definition: string;\n}\n\ntype Logger = {\n debug(msg: string, meta?: Record<string, unknown>): void;\n info(msg: string, meta?: Record<string, unknown>): void;\n warn(msg: string, meta?: Record<string, unknown>): void;\n error(msg: string, meta?: Record<string, unknown>): void;\n};\n\nexport class VcSyncService {\n constructor(\n private provider: GitProvider,\n private options: VersionControlPluginOptions,\n private logger: Logger,\n ) {}\n\n // =========================================================================\n // Configuration\n // =========================================================================\n\n async configureSyncForFlow(\n db: PluginDatabaseApi,\n flowId: string,\n input: ConfigureSyncInput,\n ): Promise<VcSyncConfig> {\n // Check if flow exists\n const flows = await db.query<FlowRow>('SELECT id, name FROM flows WHERE id = ?', [flowId]);\n if (flows.length === 0) {\n throw new Error(`Flow not found: ${flowId}`);\n }\n\n const flow = flows[0];\n const id = randomUUID();\n const now = new Date().toISOString();\n\n const repo = input.repo ?? this.options.repo;\n const branch = input.branch ?? this.options.defaultBranch ?? 'main';\n const mode = input.mode ?? this.options.mode ?? 'direct-commit';\n const syncDirection = input.syncDirection ?? this.options.syncDirection ?? 'push';\n const filePath = input.filePath ?? this.buildFilePath(flow.name);\n\n // Upsert — delete existing config for this flow first\n await db.execute('DELETE FROM vc_sync_config WHERE flow_id = ?', [flowId]);\n\n await db.execute(\n `INSERT INTO vc_sync_config (id, flow_id, provider, repo, branch, file_path, mode, sync_direction, enabled, created_at, updated_at)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,\n [id, flowId, this.provider.id, repo, branch, filePath, mode, syncDirection, true, now, now],\n );\n\n return this.getSyncConfig(db, flowId) as Promise<VcSyncConfig>;\n }\n\n async getSyncConfig(db: PluginDatabaseApi, flowId: string): Promise<VcSyncConfig | null> {\n const rows = await db.query<VcSyncConfigRow>('SELECT * FROM vc_sync_config WHERE flow_id = ?', [\n flowId,\n ]);\n if (rows.length === 0) {\n return null;\n }\n return mapSyncConfigRow(rows[0]);\n }\n\n async disconnectFlow(db: PluginDatabaseApi, flowId: string): Promise<void> {\n const config = await this.getSyncConfig(db, flowId);\n if (!config) {\n return;\n }\n\n // If there's an active PR, close it\n if (config.activePrNumber) {\n try {\n await this.provider.closePullRequest(\n config.repo,\n config.activePrNumber,\n 'Sync disconnected — flow unlinked from version control.',\n );\n } catch (err) {\n this.logger.warn('Failed to close PR on disconnect', { error: (err as Error).message });\n }\n }\n\n // If there's a draft branch, try to clean it up\n if (config.draftBranch) {\n try {\n await this.provider.deleteBranch(config.repo, config.draftBranch);\n } catch {\n // Ignore — branch may already be deleted\n }\n }\n\n await db.execute('DELETE FROM vc_sync_config WHERE flow_id = ?', [flowId]);\n }\n\n // =========================================================================\n // Push (DB → Remote)\n // =========================================================================\n\n async pushFlow(db: PluginDatabaseApi, flowId: string, identity?: string): Promise<VcSyncResult> {\n const config = await this.requireConfig(db, flowId);\n\n if (config.syncDirection === 'pull') {\n return {\n success: false,\n error: 'Push is not allowed — this flow is configured for pull-only sync.',\n action: 'push',\n };\n }\n\n const { content, version } = await this.exportFlow(db, flowId);\n\n try {\n if (config.mode === 'direct-commit') {\n return await this.directCommit(db, config, content, version, identity);\n } else if (config.mode === 'pr-per-save') {\n return await this.commitToPrBranch(db, config, content, version, identity, true);\n } else {\n // pr-per-publish: commit to draft branch, no PR yet\n return await this.commitToDraftBranch(db, config, content, version, identity);\n }\n } catch (err) {\n const message = (err as Error).message;\n\n // SHA mismatch = conflict\n if (message.includes('409') || message.includes('sha')) {\n await this.recordHistory(db, flowId, 'conflict', { version, message, createdBy: identity });\n return {\n success: false,\n error: 'Conflict: remote file has changed. Use force-push or force-pull.',\n action: 'conflict',\n };\n }\n\n throw err;\n }\n }\n\n async forcePushFlow(\n db: PluginDatabaseApi,\n flowId: string,\n identity?: string,\n ): Promise<VcSyncResult> {\n const config = await this.requireConfig(db, flowId);\n const { content, version } = await this.exportFlow(db, flowId);\n\n // Get current remote SHA (if file exists) to force update\n const remote = await this.provider.getFileContent(config.repo, config.filePath, config.branch);\n const sha = remote?.sha;\n\n const result = await this.provider.createOrUpdateFile(\n config.repo,\n config.filePath,\n content,\n `chore(flow): force-push ${this.flowFileName(config.filePath)} v${version}`,\n { branch: config.branch, sha },\n );\n\n await this.updateConfigAfterSync(db, flowId, result.commitSha, version);\n await this.recordHistory(db, flowId, 'push', {\n commitSha: result.commitSha,\n version,\n message: 'Force push (local wins)',\n createdBy: identity,\n });\n\n return { success: true, commitSha: result.commitSha, action: 'push' };\n }\n\n // =========================================================================\n // Pull (Remote → DB)\n // =========================================================================\n\n async pullFlow(db: PluginDatabaseApi, flowId: string, identity?: string): Promise<VcSyncResult> {\n const config = await this.requireConfig(db, flowId);\n\n if (config.syncDirection === 'push') {\n return {\n success: false,\n error: 'Pull is not allowed — this flow is configured for push-only sync.',\n action: 'pull',\n };\n }\n\n const remote = await this.provider.getFileContent(config.repo, config.filePath, config.branch);\n\n if (!remote) {\n return { success: false, error: 'Remote file not found', action: 'pull' };\n }\n\n // Check if we're already in sync\n if (config.lastCommitSha && remote.sha === config.lastCommitSha) {\n return { success: true, action: 'pull' }; // Already up to date\n }\n\n await this.importFlowContent(db, flowId, remote.content, identity);\n await this.updateConfigAfterSync(db, flowId, remote.sha, null);\n await this.recordHistory(db, flowId, 'pull', {\n commitSha: remote.sha,\n message: 'Pulled from remote',\n createdBy: identity,\n });\n\n return { success: true, commitSha: remote.sha, action: 'pull' };\n }\n\n async forcePullFlow(\n db: PluginDatabaseApi,\n flowId: string,\n identity?: string,\n ): Promise<VcSyncResult> {\n // Same as pull but ignores SHA check — always overwrites local\n const config = await this.requireConfig(db, flowId);\n const remote = await this.provider.getFileContent(config.repo, config.filePath, config.branch);\n\n if (!remote) {\n return { success: false, error: 'Remote file not found', action: 'pull' };\n }\n\n await this.importFlowContent(db, flowId, remote.content, identity);\n await this.updateConfigAfterSync(db, flowId, remote.sha, null);\n await this.recordHistory(db, flowId, 'pull', {\n commitSha: remote.sha,\n message: 'Force pull (remote wins)',\n createdBy: identity,\n });\n\n return { success: true, commitSha: remote.sha, action: 'pull' };\n }\n\n // =========================================================================\n // Publish (pr-per-publish mode — open PR from draft branch)\n // =========================================================================\n\n async publishFlow(\n db: PluginDatabaseApi,\n flowId: string,\n identity?: string,\n ): Promise<VcSyncResult> {\n const config = await this.requireConfig(db, flowId);\n\n if (config.mode !== 'pr-per-publish') {\n return {\n success: false,\n error: 'Publish is only available in pr-per-publish mode',\n action: 'pr-created',\n };\n }\n\n if (!config.draftBranch) {\n return {\n success: false,\n error: 'No draft branch found — push changes first',\n action: 'pr-created',\n };\n }\n\n // Check if there's already an active PR\n if (config.activePrNumber) {\n const pr = await this.provider.getPullRequest(config.repo, config.activePrNumber);\n if (pr.state === 'open') {\n return {\n success: true,\n prNumber: config.activePrNumber,\n prUrl: config.activePrUrl ?? undefined,\n action: 'pr-created',\n };\n }\n // PR was closed/merged — clear it and create a new one\n }\n\n const fileName = this.flowFileName(config.filePath);\n const pr = await this.provider.createPullRequest(config.repo, {\n title: `feat(flow): publish ${fileName}`,\n body: `Automated PR from Invect — publishing flow changes for \\`${fileName}\\`.`,\n head: config.draftBranch,\n base: config.branch,\n });\n\n await db.execute(\n 'UPDATE vc_sync_config SET active_pr_number = ?, active_pr_url = ?, updated_at = ? WHERE flow_id = ?',\n [pr.number, pr.url, new Date().toISOString(), flowId],\n );\n\n await this.recordHistory(db, flowId, 'pr-created', {\n prNumber: pr.number,\n message: `PR #${pr.number} created`,\n createdBy: identity,\n });\n\n return { success: true, prNumber: pr.number, prUrl: pr.url, action: 'pr-created' };\n }\n\n // =========================================================================\n // Status & History\n // =========================================================================\n\n async getFlowSyncStatus(\n db: PluginDatabaseApi,\n flowId: string,\n ): Promise<{\n status: VcSyncStatus;\n config: VcSyncConfig | null;\n lastSync: VcSyncHistoryRecord | null;\n }> {\n const config = await this.getSyncConfig(db, flowId);\n if (!config) {\n return { status: 'not-connected', config: null, lastSync: null };\n }\n\n const history = await db.query<VcSyncHistoryRow>(\n 'SELECT * FROM vc_sync_history WHERE flow_id = ? ORDER BY created_at DESC LIMIT 1',\n [flowId],\n );\n\n const lastSync = history.length > 0 ? mapHistoryRow(history[0]) : null;\n\n let status: VcSyncStatus = 'synced';\n if (!config.enabled) {\n status = 'not-connected';\n } else if (lastSync?.action === 'conflict') {\n status = 'conflict';\n } else if (!config.lastSyncedAt) {\n status = 'pending';\n } else {\n // Check if there are newer versions than what was synced\n const versions = await db.query<{ version: number }>(\n 'SELECT MAX(version) as version FROM flow_versions WHERE flow_id = ?',\n [flowId],\n );\n const latestVersion = versions[0]?.version;\n if (latestVersion && config.lastSyncedVersion && latestVersion > config.lastSyncedVersion) {\n status = 'pending';\n }\n }\n\n return { status, config, lastSync };\n }\n\n async getSyncHistory(\n db: PluginDatabaseApi,\n flowId: string,\n limit = 20,\n ): Promise<VcSyncHistoryRecord[]> {\n const rows = await db.query<VcSyncHistoryRow>(\n 'SELECT * FROM vc_sync_history WHERE flow_id = ? ORDER BY created_at DESC LIMIT ?',\n [flowId, limit],\n );\n return rows.map(mapHistoryRow);\n }\n\n async listSyncedFlows(\n db: PluginDatabaseApi,\n ): Promise<Array<VcSyncConfig & { flowName: string }>> {\n const rows = await db.query<VcSyncConfigRow & { flow_name: string }>(\n `SELECT vc_sync_config.*, flows.name as flow_name\n FROM vc_sync_config\n JOIN flows ON flows.id = vc_sync_config.flow_id\n ORDER BY vc_sync_config.updated_at DESC`,\n );\n return rows.map((r) => ({ ...mapSyncConfigRow(r), flowName: r.flow_name }));\n }\n\n // =========================================================================\n // Flow deletion hook\n // =========================================================================\n\n async onFlowDeleted(db: PluginDatabaseApi, flowId: string): Promise<void> {\n const config = await this.getSyncConfig(db, flowId);\n if (!config) {\n return;\n }\n\n // Delete the file from the remote\n try {\n const remote = await this.provider.getFileContent(\n config.repo,\n config.filePath,\n config.branch,\n );\n if (remote) {\n await this.provider.deleteFile(\n config.repo,\n config.filePath,\n `chore(flow): delete ${this.flowFileName(config.filePath)}`,\n { branch: config.branch, sha: remote.sha },\n );\n this.logger.info('Deleted flow file from remote', { flowId, filePath: config.filePath });\n }\n } catch (err) {\n this.logger.warn('Failed to delete flow file from remote', {\n flowId,\n error: (err as Error).message,\n });\n }\n\n // Close active PR if any\n if (config.activePrNumber) {\n try {\n await this.provider.closePullRequest(\n config.repo,\n config.activePrNumber,\n 'Flow deleted — closing PR.',\n );\n } catch {\n // Ignore\n }\n }\n\n // Clean up draft branch\n if (config.draftBranch) {\n try {\n await this.provider.deleteBranch(config.repo, config.draftBranch);\n } catch {\n // Ignore\n }\n }\n\n // DB records cascade-delete from the flows FK\n }\n\n // =========================================================================\n // Internal — commit strategies\n // =========================================================================\n\n private async directCommit(\n db: PluginDatabaseApi,\n config: VcSyncConfig,\n content: string,\n version: number,\n identity?: string,\n ): Promise<VcSyncResult> {\n const sha = config.lastCommitSha ?? undefined;\n\n // Try to get remote SHA if we don't have one (first push)\n let remoteSha = sha;\n if (!remoteSha) {\n const remote = await this.provider.getFileContent(\n config.repo,\n config.filePath,\n config.branch,\n );\n remoteSha = remote?.sha;\n }\n\n const result = await this.provider.createOrUpdateFile(\n config.repo,\n config.filePath,\n content,\n `chore(flow): update ${this.flowFileName(config.filePath)} v${version}`,\n { branch: config.branch, sha: remoteSha },\n );\n\n await this.updateConfigAfterSync(db, config.flowId, result.commitSha, version);\n await this.recordHistory(db, config.flowId, 'push', {\n commitSha: result.commitSha,\n version,\n message: `Direct commit v${version}`,\n createdBy: identity,\n });\n\n return { success: true, commitSha: result.commitSha, action: 'push' };\n }\n\n private async commitToPrBranch(\n db: PluginDatabaseApi,\n config: VcSyncConfig,\n content: string,\n version: number,\n identity?: string,\n openPr: boolean = true,\n ): Promise<VcSyncResult> {\n const branchName = config.draftBranch ?? `invect/flow/${this.flowSlug(config.filePath)}`;\n\n // Create branch if it doesn't exist\n const existing = await this.provider.getBranch(config.repo, branchName);\n if (!existing) {\n await this.provider.createBranch(config.repo, branchName, config.branch);\n }\n\n // Get current file SHA on the branch\n const remote = await this.provider.getFileContent(config.repo, config.filePath, branchName);\n\n const result = await this.provider.createOrUpdateFile(\n config.repo,\n config.filePath,\n content,\n `chore(flow): update ${this.flowFileName(config.filePath)} v${version}`,\n { branch: branchName, sha: remote?.sha },\n );\n\n // Save draft branch reference\n await db.execute(\n 'UPDATE vc_sync_config SET draft_branch = ?, updated_at = ? WHERE flow_id = ?',\n [branchName, new Date().toISOString(), config.flowId],\n );\n\n let prNumber = config.activePrNumber ?? undefined;\n let prUrl = config.activePrUrl ?? undefined;\n\n // Open PR if needed\n if (openPr && !prNumber) {\n const pr = await this.provider.createPullRequest(config.repo, {\n title: `feat(flow): update ${this.flowFileName(config.filePath)}`,\n body: `Automated PR from Invect — flow changes for \\`${this.flowFileName(config.filePath)}\\`.`,\n head: branchName,\n base: config.branch,\n });\n prNumber = pr.number;\n prUrl = pr.url;\n\n await db.execute(\n 'UPDATE vc_sync_config SET active_pr_number = ?, active_pr_url = ?, updated_at = ? WHERE flow_id = ?',\n [prNumber, prUrl, new Date().toISOString(), config.flowId],\n );\n\n await this.recordHistory(db, config.flowId, 'pr-created', {\n commitSha: result.commitSha,\n prNumber,\n version,\n message: `PR #${prNumber} created`,\n createdBy: identity,\n });\n } else {\n await this.recordHistory(db, config.flowId, 'push', {\n commitSha: result.commitSha,\n version,\n message: `Updated PR branch v${version}`,\n createdBy: identity,\n });\n }\n\n await this.updateConfigAfterSync(db, config.flowId, result.commitSha, version);\n\n return {\n success: true,\n commitSha: result.commitSha,\n prNumber,\n prUrl,\n action: prNumber ? 'pr-created' : 'push',\n };\n }\n\n private async commitToDraftBranch(\n db: PluginDatabaseApi,\n config: VcSyncConfig,\n content: string,\n version: number,\n identity?: string,\n ): Promise<VcSyncResult> {\n // Same as PR branch commit but without opening a PR\n return this.commitToPrBranch(db, config, content, version, identity, false);\n }\n\n // =========================================================================\n // Internal — flow export / import\n // =========================================================================\n\n private async exportFlow(\n db: PluginDatabaseApi,\n flowId: string,\n ): Promise<{ content: string; version: number }> {\n const flows = await db.query<FlowRow>(\n 'SELECT id, name, description, tags FROM flows WHERE id = ?',\n [flowId],\n );\n if (flows.length === 0) {\n throw new Error(`Flow not found: ${flowId}`);\n }\n const flow = flows[0];\n\n const versions = await db.query<FlowVersionRow>(\n 'SELECT flow_id, version, invect_definition FROM flow_versions WHERE flow_id = ? ORDER BY version DESC LIMIT 1',\n [flowId],\n );\n if (versions.length === 0) {\n throw new Error(`No versions found for flow: ${flowId}`);\n }\n const fv = versions[0];\n\n const definition =\n typeof fv.invect_definition === 'string'\n ? JSON.parse(fv.invect_definition)\n : fv.invect_definition;\n\n let tags: string[] | undefined;\n if (flow.tags) {\n try {\n tags = typeof flow.tags === 'string' ? JSON.parse(flow.tags) : flow.tags;\n } catch {\n tags = undefined;\n }\n }\n\n const content = serializeFlowToTs(definition, {\n name: flow.name,\n description: flow.description ?? undefined,\n tags,\n });\n\n return { content, version: fv.version };\n }\n\n private async importFlowContent(\n db: PluginDatabaseApi,\n flowId: string,\n content: string,\n identity?: string,\n ): Promise<void> {\n // Parse the .flow.ts content statically — no eval/jiti to avoid\n // arbitrary code execution from untrusted remote files.\n const definition = parseFlowTsContent(content);\n\n if (\n !definition ||\n typeof definition !== 'object' ||\n !Array.isArray(definition.nodes) ||\n !Array.isArray(definition.edges)\n ) {\n throw new Error(\n 'Imported .flow.ts file did not produce a valid InvectDefinition. ' +\n 'Expected an object with \"nodes\" and \"edges\" arrays.',\n );\n }\n\n // Get current latest version number\n const versions = await db.query<{ version: number }>(\n 'SELECT MAX(version) as version FROM flow_versions WHERE flow_id = ?',\n [flowId],\n );\n const nextVersion = (versions[0]?.version ?? 0) + 1;\n\n // Insert new flow version\n const defJson = JSON.stringify(definition);\n\n await db.execute(\n `INSERT INTO flow_versions (flow_id, version, invect_definition, created_at, created_by)\n VALUES (?, ?, ?, ?, ?)`,\n [flowId, nextVersion, defJson, new Date().toISOString(), identity ?? null],\n );\n\n // Update flow's live version\n await db.execute('UPDATE flows SET live_version_number = ?, updated_at = ? WHERE id = ?', [\n nextVersion,\n new Date().toISOString(),\n flowId,\n ]);\n\n this.logger.info('Flow imported from remote', {\n flowId,\n version: nextVersion,\n });\n }\n\n // =========================================================================\n // Internal — helpers\n // =========================================================================\n\n private buildFilePath(flowName: string): string {\n const basePath = this.options.path ?? 'workflows/';\n const slug = flowName\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, '-')\n .replace(/^-|-$/g, '');\n return `${basePath}${slug}.flow.ts`;\n }\n\n private flowFileName(filePath: string): string {\n return filePath.split('/').pop() ?? filePath;\n }\n\n private flowSlug(filePath: string): string {\n const name = this.flowFileName(filePath);\n return name.replace(/\\.flow\\.ts$/, '');\n }\n\n private async requireConfig(db: PluginDatabaseApi, flowId: string): Promise<VcSyncConfig> {\n const config = await this.getSyncConfig(db, flowId);\n if (!config) {\n throw new Error(`Flow ${flowId} is not connected to version control`);\n }\n if (!config.enabled) {\n throw new Error(`Version control sync is disabled for flow ${flowId}`);\n }\n return config;\n }\n\n private async updateConfigAfterSync(\n db: PluginDatabaseApi,\n flowId: string,\n commitSha: string,\n version: number | null,\n ): Promise<void> {\n const now = new Date().toISOString();\n if (version !== null) {\n await db.execute(\n 'UPDATE vc_sync_config SET last_synced_at = ?, last_commit_sha = ?, last_synced_version = ?, updated_at = ? WHERE flow_id = ?',\n [now, commitSha, version, now, flowId],\n );\n } else {\n await db.execute(\n 'UPDATE vc_sync_config SET last_synced_at = ?, last_commit_sha = ?, updated_at = ? WHERE flow_id = ?',\n [now, commitSha, now, flowId],\n );\n }\n }\n\n private async recordHistory(\n db: PluginDatabaseApi,\n flowId: string,\n action: import('../shared/types').VcSyncAction,\n opts: {\n commitSha?: string;\n prNumber?: number;\n version?: number;\n message?: string;\n createdBy?: string;\n },\n ): Promise<void> {\n await db.execute(\n `INSERT INTO vc_sync_history (id, flow_id, action, commit_sha, pr_number, version, message, created_at, created_by)\n VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,\n [\n randomUUID(),\n flowId,\n action,\n opts.commitSha ?? null,\n opts.prNumber ?? null,\n opts.version ?? null,\n opts.message ?? null,\n new Date().toISOString(),\n opts.createdBy ?? null,\n ],\n );\n }\n}\n\n// =============================================================================\n// Row mappers (snake_case DB rows → camelCase types)\n// =============================================================================\n\ninterface VcSyncConfigRow {\n id: string;\n flow_id: string;\n provider: string;\n repo: string;\n branch: string;\n file_path: string;\n mode: string;\n sync_direction: string;\n last_synced_at: string | null;\n last_commit_sha: string | null;\n last_synced_version: number | null;\n draft_branch: string | null;\n active_pr_number: number | null;\n active_pr_url: string | null;\n enabled: boolean | number;\n created_at: string;\n updated_at: string;\n}\n\ninterface VcSyncHistoryRow {\n id: string;\n flow_id: string;\n action: string;\n commit_sha: string | null;\n pr_number: number | null;\n version: number | null;\n message: string | null;\n created_at: string;\n created_by: string | null;\n}\n\nfunction mapSyncConfigRow(r: VcSyncConfigRow): VcSyncConfig {\n return {\n id: r.id,\n flowId: r.flow_id,\n provider: r.provider,\n repo: r.repo,\n branch: r.branch,\n filePath: r.file_path,\n mode: r.mode as VcSyncConfig['mode'],\n syncDirection: r.sync_direction as VcSyncConfig['syncDirection'],\n lastSyncedAt: r.last_synced_at,\n lastCommitSha: r.last_commit_sha,\n lastSyncedVersion: r.last_synced_version,\n draftBranch: r.draft_branch,\n activePrNumber: r.active_pr_number,\n activePrUrl: r.active_pr_url,\n enabled: r.enabled === true || r.enabled === 1,\n };\n}\n\nfunction mapHistoryRow(r: VcSyncHistoryRow): VcSyncHistoryRecord {\n return {\n id: r.id,\n flowId: r.flow_id,\n action: r.action as VcSyncHistoryRecord['action'],\n commitSha: r.commit_sha,\n prNumber: r.pr_number,\n version: r.version,\n message: r.message,\n createdAt: r.created_at,\n createdBy: r.created_by,\n };\n}\n\n// =============================================================================\n// Static .flow.ts parser — extracts definition without eval\n// =============================================================================\n\n/**\n * Parse a .flow.ts file content to extract the InvectDefinition.\n *\n * This is a static parser that does NOT evaluate the TypeScript file.\n * It works by extracting the `defineFlow({ ... })` call's argument as a\n * JS object literal string and parsing it with a safe JSON5-like approach.\n *\n * Falls back to extracting raw `nodes` and `edges` arrays if defineFlow\n * wrapper is not found.\n */\nfunction parseFlowTsContent(content: string): { nodes: unknown[]; edges: unknown[] } | null {\n // Strategy 1 (preferred): Look for the embedded JSON block comment.\n // The serializer embeds `/* @invect-definition {...} */` for reliable round-tripping.\n const jsonCommentMatch = content.match(/\\/\\*\\s*@invect-definition\\s+([\\s\\S]*?)\\s*\\*\\//);\n if (jsonCommentMatch) {\n try {\n return JSON.parse(jsonCommentMatch[1]);\n } catch {\n // Fall through to strategy 2\n }\n }\n\n // Strategy 2 (fallback): Extract the defineFlow({ ... }) argument.\n // Used for hand-written or older .flow.ts files without the JSON comment.\n const defineFlowMatch = content.match(/defineFlow\\s*\\(\\s*\\{/);\n if (defineFlowMatch && defineFlowMatch.index !== undefined) {\n const startIdx = defineFlowMatch.index + defineFlowMatch[0].length - 1; // { position\n const objStr = extractBalancedBraces(content, startIdx);\n if (objStr) {\n try {\n const parsed = parseObjectLiteral(objStr);\n if (parsed && Array.isArray(parsed.nodes) && Array.isArray(parsed.edges)) {\n return { nodes: parsed.nodes, edges: parsed.edges };\n }\n } catch {\n // Fall through\n }\n }\n }\n\n return null;\n}\n\n/** Extract a balanced {} block from a string starting at the given { index */\nfunction extractBalancedBraces(str: string, startIdx: number): string | null {\n let depth = 0;\n let inString: string | false = false;\n let escaped = false;\n\n for (let i = startIdx; i < str.length; i++) {\n const ch = str[i];\n\n if (escaped) {\n escaped = false;\n continue;\n }\n\n if (ch === '\\\\') {\n escaped = true;\n continue;\n }\n\n if (inString) {\n if (ch === inString) {\n inString = false;\n }\n continue;\n }\n\n if (ch === '\"' || ch === \"'\" || ch === '`') {\n inString = ch;\n continue;\n }\n\n if (ch === '{' || ch === '[') {\n depth++;\n } else if (ch === '}' || ch === ']') {\n depth--;\n if (depth === 0) {\n return str.slice(startIdx, i + 1);\n }\n }\n }\n\n return null;\n}\n\n/**\n * Parse a JS object literal string into a JSON-compatible value.\n *\n * Handles: unquoted keys, single-quoted strings, trailing commas,\n * template literals (simplified), and function calls by converting\n * them to strings.\n */\nfunction parseObjectLiteral(objStr: string): Record<string, unknown> | null {\n try {\n // Normalize JS object literal to JSON:\n // 1. Strip single-line comments\n let normalized = objStr.replace(/\\/\\/.*$/gm, '');\n // 2. Strip multi-line comments\n normalized = normalized.replace(/\\/\\*[\\s\\S]*?\\*\\//g, '');\n // 3. Replace single quotes with double quotes (outside existing double quotes)\n normalized = replaceQuotes(normalized);\n // 4. Quote unquoted keys\n normalized = normalized.replace(/(?<=[{,]\\s*)([a-zA-Z_$][a-zA-Z0-9_$]*)(?=\\s*:)/g, '\"$1\"');\n // 5. Remove trailing commas before } or ]\n normalized = normalized.replace(/,\\s*([}\\]])/g, '$1');\n // 6. Replace function calls like input(\"ref\", {...}) with a placeholder string\n // This handles the helper calls in the nodes array\n normalized = replaceFunctionCalls(normalized);\n\n return JSON.parse(normalized);\n } catch {\n return null;\n }\n}\n\n/** Replace single-quoted strings with double-quoted */\nfunction replaceQuotes(str: string): string {\n let result = '';\n let inDouble = false;\n let inSingle = false;\n let escaped = false;\n\n for (let i = 0; i < str.length; i++) {\n const ch = str[i];\n\n if (escaped) {\n result += ch;\n escaped = false;\n continue;\n }\n\n if (ch === '\\\\') {\n result += ch;\n escaped = true;\n continue;\n }\n\n if (!inSingle && ch === '\"') {\n inDouble = !inDouble;\n result += ch;\n } else if (!inDouble && ch === \"'\") {\n inSingle = !inSingle;\n result += '\"';\n } else {\n result += ch;\n }\n }\n\n return result;\n}\n\n/**\n * Replace function calls like `input(\"ref\", { ... })` with a JSON object\n * that captures the node structure. This handles the SDK helper calls\n * in the serialized .flow.ts nodes array.\n *\n * Pattern: `helperName(\"refId\", { params })` → `{ \"type\": \"helperName\", \"referenceId\": \"refId\", \"params\": { ... } }`\n * Also handles namespaced: `ns.helperName(\"refId\", { ... })`\n */\nfunction replaceFunctionCalls(str: string): string {\n // Match function calls: word.word( or word( at the start of array items\n const callPattern =\n /([a-zA-Z_$][\\w$]*\\.[a-zA-Z_$][\\w$]*|[a-zA-Z_$][\\w$]*)\\s*\\(\\s*\"([^\"]*)\"\\s*,\\s*(\\{)/g;\n\n let result = str;\n let match: RegExpExecArray | null;\n let offset = 0;\n\n // Reset lastIndex\n callPattern.lastIndex = 0;\n\n while ((match = callPattern.exec(str)) !== null) {\n const fnName = match[1];\n const refId = match[2];\n const braceStart = match.index + match[0].length - 1;\n\n const paramsBlock = extractBalancedBraces(str, braceStart);\n if (!paramsBlock) {\n continue;\n }\n\n // Find the closing ) after the params block\n const afterParams = braceStart + paramsBlock.length;\n let closeParen = afterParams;\n while (closeParen < str.length && str[closeParen] !== ')') {\n closeParen++;\n }\n\n const fullCall = str.slice(match.index, closeParen + 1);\n const replacement = `{ \"__type\": \"${fnName}\", \"referenceId\": \"${refId}\", \"params\": ${paramsBlock} }`;\n\n result =\n result.slice(0, match.index + offset) +\n replacement +\n result.slice(match.index + offset + fullCall.length);\n\n offset += replacement.length - fullCall.length;\n }\n\n return result;\n}\n","// =============================================================================\n// Version Control Plugin — Input Validation Schemas\n// =============================================================================\n\nimport { z } from 'zod/v4';\nimport { VC_SYNC_MODES, VC_SYNC_DIRECTIONS } from '../shared/types';\n\nexport const configureSyncInputSchema = z.object({\n repo: z\n .string()\n .regex(/^[a-zA-Z0-9._-]+\\/[a-zA-Z0-9._-]+$/, 'Invalid repo format. Expected \"owner/name\".')\n .optional(),\n branch: z\n .string()\n .max(256)\n .regex(/^[a-zA-Z0-9._/-]+$/, 'Invalid branch name.')\n .optional(),\n filePath: z\n .string()\n .max(1024)\n .regex(/^[a-zA-Z0-9._/-]+\\.flow\\.ts$/, 'File path must end with .flow.ts')\n .optional(),\n mode: z.enum(VC_SYNC_MODES).optional(),\n syncDirection: z.enum(VC_SYNC_DIRECTIONS).optional(),\n enabled: z.boolean().optional(),\n});\n\nexport const historyLimitSchema = z.coerce.number().int().min(1).max(100).default(20);\n","// =============================================================================\n// Version Control Plugin — Main Entry Point\n// =============================================================================\n\nimport type { InvectPlugin, InvectPluginDefinition, PluginEndpointContext } from '@invect/core';\nimport { randomUUID } from 'node:crypto';\n\nimport type { VersionControlPluginOptions } from './types';\nimport { VC_SCHEMA } from './schema';\nimport { VcSyncService } from './sync-service';\nimport { configureSyncInputSchema, historyLimitSchema } from './validation';\n\n/**\n * Create the Version Control plugin.\n *\n * Syncs Invect flows to a Git remote as readable `.flow.ts` files.\n *\n * ```ts\n * import { versionControl } from '@invect/version-control';\n * import { githubProvider } from '@invect/version-control/providers/github';\n *\n * new Invect({\n * plugins: [\n * versionControl({\n * provider: githubProvider({ auth: { type: 'token', token: process.env.GITHUB_TOKEN! } }),\n * repo: 'acme/workflows',\n * mode: 'pr-per-publish',\n * }),\n * ],\n * });\n * ```\n */\nexport function versionControl(options: VersionControlPluginOptions): InvectPluginDefinition {\n const { frontend, ...backendOptions } = options;\n return {\n id: 'version-control',\n name: 'Version Control',\n backend: _vcBackendPlugin(backendOptions),\n frontend,\n };\n}\n\nfunction _vcBackendPlugin(options: Omit<VersionControlPluginOptions, 'frontend'>): InvectPlugin {\n let syncService: VcSyncService;\n let pluginLogger: { debug: Function; info: Function; warn: Function; error: Function } = console;\n\n return {\n id: 'version-control',\n name: 'Version Control',\n\n schema: VC_SCHEMA,\n\n setupInstructions:\n 'Run `npx invect-cli generate` then `npx invect-cli migrate` to create the vc_sync_config and vc_sync_history tables.',\n\n // =======================================================================\n // Initialization\n // =======================================================================\n\n init: async (ctx) => {\n pluginLogger = ctx.logger;\n syncService = new VcSyncService(options.provider, options, ctx.logger);\n ctx.logger.info(\n `Version control plugin initialized (provider: ${options.provider.id}, repo: ${options.repo})`,\n );\n },\n\n // =======================================================================\n // Endpoints\n // =======================================================================\n\n endpoints: [\n // -- Configure sync for a flow --\n {\n method: 'POST',\n path: '/vc/flows/:flowId/configure',\n handler: async (ctx: PluginEndpointContext) => {\n const { flowId } = ctx.params;\n const parsed = configureSyncInputSchema.safeParse(ctx.body);\n if (!parsed.success) {\n return { status: 400, body: { error: 'Invalid input', details: parsed.error.issues } };\n }\n const config = await syncService.configureSyncForFlow(ctx.database, flowId, parsed.data);\n return { status: 200, body: config };\n },\n },\n\n // -- Get sync status for a flow --\n {\n method: 'GET',\n path: '/vc/flows/:flowId/status',\n handler: async (ctx: PluginEndpointContext) => {\n const { flowId } = ctx.params;\n const status = await syncService.getFlowSyncStatus(ctx.database, flowId);\n return { status: 200, body: { flowId, ...status } };\n },\n },\n\n // -- Disconnect sync for a flow --\n {\n method: 'DELETE',\n path: '/vc/flows/:flowId/disconnect',\n handler: async (ctx: PluginEndpointContext) => {\n const { flowId } = ctx.params;\n await syncService.disconnectFlow(ctx.database, flowId);\n return { status: 200, body: { success: true } };\n },\n },\n\n // -- Push (DB → remote) --\n {\n method: 'POST',\n path: '/vc/flows/:flowId/push',\n handler: async (ctx: PluginEndpointContext) => {\n const { flowId } = ctx.params;\n const identity = ctx.identity?.id;\n const result = await syncService.pushFlow(ctx.database, flowId, identity);\n return { status: result.success ? 200 : 409, body: result };\n },\n },\n\n // -- Pull (remote → DB) --\n {\n method: 'POST',\n path: '/vc/flows/:flowId/pull',\n handler: async (ctx: PluginEndpointContext) => {\n const { flowId } = ctx.params;\n const identity = ctx.identity?.id;\n const result = await syncService.pullFlow(ctx.database, flowId, identity);\n return { status: result.success ? 200 : 404, body: result };\n },\n },\n\n // -- Publish (pr-per-publish mode) --\n {\n method: 'POST',\n path: '/vc/flows/:flowId/publish',\n handler: async (ctx: PluginEndpointContext) => {\n const { flowId } = ctx.params;\n const identity = ctx.identity?.id;\n const result = await syncService.publishFlow(ctx.database, flowId, identity);\n return { status: result.success ? 200 : 400, body: result };\n },\n },\n\n // -- Force push (conflict resolution — DB wins) --\n {\n method: 'POST',\n path: '/vc/flows/:flowId/force-push',\n handler: async (ctx: PluginEndpointContext) => {\n const { flowId } = ctx.params;\n const identity = ctx.identity?.id;\n const result = await syncService.forcePushFlow(ctx.database, flowId, identity);\n return { status: 200, body: result };\n },\n },\n\n // -- Force pull (conflict resolution — remote wins) --\n {\n method: 'POST',\n path: '/vc/flows/:flowId/force-pull',\n handler: async (ctx: PluginEndpointContext) => {\n const { flowId } = ctx.params;\n const identity = ctx.identity?.id;\n const result = await syncService.forcePullFlow(ctx.database, flowId, identity);\n return { status: result.success ? 200 : 404, body: result };\n },\n },\n\n // -- Bulk push all synced flows --\n {\n method: 'POST',\n path: '/vc/push-all',\n handler: async (ctx: PluginEndpointContext) => {\n const configs = await syncService.listSyncedFlows(ctx.database);\n const identity = ctx.identity?.id;\n const results = [];\n for (const config of configs) {\n if (!config.enabled) {\n continue;\n }\n try {\n const result = await syncService.pushFlow(ctx.database, config.flowId, identity);\n results.push({ flowId: config.flowId, flowName: config.flowName, ...result });\n } catch (err) {\n results.push({\n flowId: config.flowId,\n flowName: config.flowName,\n success: false,\n error: (err as Error).message,\n action: 'push' as const,\n });\n }\n }\n return { status: 200, body: { results } };\n },\n },\n\n // -- Bulk pull all synced flows --\n {\n method: 'POST',\n path: '/vc/pull-all',\n handler: async (ctx: PluginEndpointContext) => {\n const configs = await syncService.listSyncedFlows(ctx.database);\n const identity = ctx.identity?.id;\n const results = [];\n for (const config of configs) {\n if (!config.enabled) {\n continue;\n }\n try {\n const result = await syncService.pullFlow(ctx.database, config.flowId, identity);\n results.push({ flowId: config.flowId, flowName: config.flowName, ...result });\n } catch (err) {\n results.push({\n flowId: config.flowId,\n flowName: config.flowName,\n success: false,\n error: (err as Error).message,\n action: 'pull' as const,\n });\n }\n }\n return { status: 200, body: { results } };\n },\n },\n\n // -- Webhook receiver --\n {\n method: 'POST',\n path: '/vc/webhook',\n isPublic: true,\n handler: async (ctx: PluginEndpointContext) => {\n if (!options.webhookSecret) {\n return { status: 400, body: { error: 'Webhook secret not configured' } };\n }\n\n // Verify signature\n const signature = ctx.headers['x-hub-signature-256'] ?? '';\n const body = JSON.stringify(ctx.body);\n if (!options.provider.verifyWebhookSignature(body, signature, options.webhookSecret)) {\n return { status: 401, body: { error: 'Invalid webhook signature' } };\n }\n\n // Handle PR merge events\n const action = (ctx.body as Record<string, unknown>).action;\n const pullRequest = (ctx.body as Record<string, unknown>).pull_request as\n | { merged: boolean; number: number }\n | undefined;\n\n if (action === 'closed' && pullRequest?.merged) {\n await handlePrMerged(ctx.database, pullRequest.number);\n }\n\n return { status: 200, body: { received: true } };\n },\n },\n\n // -- List all synced flows --\n {\n method: 'GET',\n path: '/vc/flows',\n handler: async (ctx: PluginEndpointContext) => {\n const flows = await syncService.listSyncedFlows(ctx.database);\n return { status: 200, body: { flows } };\n },\n },\n\n // -- Get sync history for a flow --\n {\n method: 'GET',\n path: '/vc/flows/:flowId/history',\n handler: async (ctx: PluginEndpointContext) => {\n const { flowId } = ctx.params;\n const limit = historyLimitSchema.parse(ctx.query.limit);\n const history = await syncService.getSyncHistory(ctx.database, flowId, limit);\n return { status: 200, body: { flowId, history } };\n },\n },\n ],\n\n // =======================================================================\n // Hooks\n // =======================================================================\n\n // NOTE: Remote cleanup on flow deletion (deleting the file from GitHub,\n // closing active PRs, removing draft branches) requires calling\n // DELETE /vc/flows/:flowId/disconnect before deleting the flow.\n // DB records (vc_sync_config, vc_sync_history) cascade-delete via FK.\n // The plugin hook system does not provide database access in onRequest,\n // so automatic remote cleanup on flow delete is not possible via hooks.\n hooks: {},\n };\n\n // =========================================================================\n // Webhook handlers (internal)\n // =========================================================================\n\n async function handlePrMerged(\n db: import('@invect/core').PluginDatabaseApi,\n prNumber: number,\n ): Promise<void> {\n // Find the sync config with this active PR — read draft_branch BEFORE clearing it\n const rows = await db.query<{\n flow_id: string;\n draft_branch: string | null;\n repo: string;\n }>('SELECT flow_id, draft_branch, repo FROM vc_sync_config WHERE active_pr_number = ?', [\n prNumber,\n ]);\n\n for (const row of rows) {\n // Try to clean up the draft branch before clearing the reference\n if (row.draft_branch) {\n try {\n await options.provider.deleteBranch(row.repo, row.draft_branch);\n } catch {\n // Branch may already be deleted by the merge\n }\n }\n\n // Clear PR state, update sync status\n await db.execute(\n `UPDATE vc_sync_config\n SET active_pr_number = NULL, active_pr_url = NULL, draft_branch = NULL, updated_at = ?\n WHERE flow_id = ?`,\n [new Date().toISOString(), row.flow_id],\n );\n\n // Record history\n await db.execute(\n `INSERT INTO vc_sync_history (id, flow_id, action, pr_number, message, created_at)\n VALUES (?, ?, ?, ?, ?, ?)`,\n [\n randomUUID(),\n row.flow_id,\n 'pr-merged',\n prNumber,\n `PR #${prNumber} merged`,\n new Date().toISOString(),\n ],\n );\n\n pluginLogger.info('PR merged — sync updated', { flowId: row.flow_id, prNumber });\n }\n }\n}\n"],"mappings":"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"}
|
package/dist/backend/plugin.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { InvectPluginDefinition } from '@invect/core';
|
|
2
2
|
import type { VersionControlPluginOptions } from './types';
|
|
3
3
|
/**
|
|
4
4
|
* Create the Version Control plugin.
|
|
@@ -20,5 +20,5 @@ import type { VersionControlPluginOptions } from './types';
|
|
|
20
20
|
* });
|
|
21
21
|
* ```
|
|
22
22
|
*/
|
|
23
|
-
export declare function versionControl(options: VersionControlPluginOptions):
|
|
23
|
+
export declare function versionControl(options: VersionControlPluginOptions): InvectPluginDefinition;
|
|
24
24
|
//# sourceMappingURL=plugin.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../src/backend/plugin.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../src/backend/plugin.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAgB,sBAAsB,EAAyB,MAAM,cAAc,CAAC;AAGhG,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,SAAS,CAAC;AAK3D;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,2BAA2B,GAAG,sBAAsB,CAQ3F"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sync-service.d.ts","sourceRoot":"","sources":["../../src/backend/sync-service.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AACtD,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,SAAS,CAAC;AAC3D,OAAO,KAAK,EACV,YAAY,EACZ,mBAAmB,EACnB,YAAY,EACZ,YAAY,EACZ,kBAAkB,EACnB,MAAM,iBAAiB,CAAC;AAgBzB,KAAK,MAAM,GAAG;IACZ,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IACzD,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IACxD,IAAI,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IACxD,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;CAC1D,CAAC;AAEF,qBAAa,aAAa;IAEtB,OAAO,CAAC,QAAQ;IAChB,OAAO,CAAC,OAAO;IACf,OAAO,CAAC,MAAM;gBAFN,QAAQ,EAAE,WAAW,EACrB,OAAO,EAAE,2BAA2B,EACpC,MAAM,EAAE,MAAM;IAOlB,oBAAoB,CACxB,EAAE,EAAE,iBAAiB,EACrB,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,kBAAkB,GACxB,OAAO,CAAC,YAAY,CAAC;IA6BlB,aAAa,CAAC,EAAE,EAAE,iBAAiB,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC;IAUlF,cAAc,CAAC,EAAE,EAAE,iBAAiB,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAmCpE,QAAQ,CAAC,EAAE,EAAE,iBAAiB,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"sync-service.d.ts","sourceRoot":"","sources":["../../src/backend/sync-service.ts"],"names":[],"mappings":"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"}
|
package/dist/backend/types.d.ts
CHANGED
|
@@ -16,5 +16,10 @@ export interface VersionControlPluginOptions {
|
|
|
16
16
|
syncDirection?: VcSyncDirection;
|
|
17
17
|
/** Webhook secret for verifying incoming webhooks */
|
|
18
18
|
webhookSecret?: string;
|
|
19
|
+
/**
|
|
20
|
+
* Frontend plugin for the version control UI.
|
|
21
|
+
* Omit for backend-only setups.
|
|
22
|
+
*/
|
|
23
|
+
frontend?: unknown;
|
|
19
24
|
}
|
|
20
25
|
//# sourceMappingURL=types.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/backend/types.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,KAAK,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAEnE,sDAAsD;AACtD,MAAM,WAAW,2BAA2B;IAC1C,gEAAgE;IAChE,QAAQ,EAAE,WAAW,CAAC;IAEtB,sCAAsC;IACtC,IAAI,EAAE,MAAM,CAAC;IAEb,4BAA4B;IAC5B,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB,4DAA4D;IAC5D,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd,wBAAwB;IACxB,IAAI,CAAC,EAAE,UAAU,CAAC;IAElB,6BAA6B;IAC7B,aAAa,CAAC,EAAE,eAAe,CAAC;IAEhC,qDAAqD;IACrD,aAAa,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/backend/types.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,KAAK,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAEnE,sDAAsD;AACtD,MAAM,WAAW,2BAA2B;IAC1C,gEAAgE;IAChE,QAAQ,EAAE,WAAW,CAAC;IAEtB,sCAAsC;IACtC,IAAI,EAAE,MAAM,CAAC;IAEb,4BAA4B;IAC5B,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB,4DAA4D;IAC5D,IAAI,CAAC,EAAE,MAAM,CAAC;IAEd,wBAAwB;IACxB,IAAI,CAAC,EAAE,UAAU,CAAC;IAElB,6BAA6B;IAC7B,aAAa,CAAC,EAAE,eAAe,CAAC;IAEhC,qDAAqD;IACrD,aAAa,CAAC,EAAE,MAAM,CAAC;IAEvB;;;OAGG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { z } from 'zod/v4';
|
|
2
|
+
export declare const configureSyncInputSchema: z.ZodObject<{
|
|
3
|
+
repo: z.ZodOptional<z.ZodString>;
|
|
4
|
+
branch: z.ZodOptional<z.ZodString>;
|
|
5
|
+
filePath: z.ZodOptional<z.ZodString>;
|
|
6
|
+
mode: z.ZodOptional<z.ZodEnum<{
|
|
7
|
+
"direct-commit": "direct-commit";
|
|
8
|
+
"pr-per-save": "pr-per-save";
|
|
9
|
+
"pr-per-publish": "pr-per-publish";
|
|
10
|
+
}>>;
|
|
11
|
+
syncDirection: z.ZodOptional<z.ZodEnum<{
|
|
12
|
+
push: "push";
|
|
13
|
+
pull: "pull";
|
|
14
|
+
bidirectional: "bidirectional";
|
|
15
|
+
}>>;
|
|
16
|
+
enabled: z.ZodOptional<z.ZodBoolean>;
|
|
17
|
+
}, z.core.$strip>;
|
|
18
|
+
export declare const historyLimitSchema: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
|
|
19
|
+
//# sourceMappingURL=validation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../../src/backend/validation.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,CAAC,EAAE,MAAM,QAAQ,CAAC;AAG3B,eAAO,MAAM,wBAAwB;;;;;;;;;;;;;;;iBAkBnC,CAAC;AAEH,eAAO,MAAM,kBAAkB,2CAAsD,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ConnectFlowForm — Form to configure version control sync for a flow.
|
|
3
|
+
*/
|
|
4
|
+
interface ConnectFlowFormProps {
|
|
5
|
+
flowId: string;
|
|
6
|
+
onCancel: () => void;
|
|
7
|
+
}
|
|
8
|
+
export declare function ConnectFlowForm({ flowId, onCancel }: ConnectFlowFormProps): import("react/jsx-runtime").JSX.Element;
|
|
9
|
+
export {};
|
|
10
|
+
//# sourceMappingURL=ConnectFlowForm.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ConnectFlowForm.d.ts","sourceRoot":"","sources":["../../../src/frontend/components/ConnectFlowForm.tsx"],"names":[],"mappings":"AAAA;;GAEG;AAOH,UAAU,oBAAoB;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,IAAI,CAAC;CACtB;AAED,wBAAgB,eAAe,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,EAAE,oBAAoB,2CA6HzE"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* VcHeaderButton — Header action for quick push/pull from the flow editor header.
|
|
3
|
+
*
|
|
4
|
+
* Shows sync status as an icon and provides one-click push/pull.
|
|
5
|
+
*/
|
|
6
|
+
import type { HeaderActionProps } from '@invect/ui';
|
|
7
|
+
export declare function VcHeaderButton({ flowId }: HeaderActionProps): import("react/jsx-runtime").JSX.Element | null;
|
|
8
|
+
//# sourceMappingURL=VcHeaderButton.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"VcHeaderButton.d.ts","sourceRoot":"","sources":["../../../src/frontend/components/VcHeaderButton.tsx"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAEpD,wBAAgB,cAAc,CAAC,EAAE,MAAM,EAAE,EAAE,iBAAiB,kDAM3D"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* VcSyncPanel — Panel tab for the flow editor.
|
|
3
|
+
*
|
|
4
|
+
* Shows sync status, push/pull controls, and recent sync history
|
|
5
|
+
* for the current flow. Registered as a panelTab contribution
|
|
6
|
+
* for the 'flowEditor' context.
|
|
7
|
+
*/
|
|
8
|
+
import type { PanelTabProps } from '@invect/ui';
|
|
9
|
+
export declare function VcSyncPanel({ flowId }: PanelTabProps): import("react/jsx-runtime").JSX.Element;
|
|
10
|
+
//# sourceMappingURL=VcSyncPanel.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"VcSyncPanel.d.ts","sourceRoot":"","sources":["../../../src/frontend/components/VcSyncPanel.tsx"],"names":[],"mappings":"AAAA;;;;;;GAMG;AA0BH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAGhD,wBAAgB,WAAW,CAAC,EAAE,MAAM,EAAE,EAAE,aAAa,2CA6QpD"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* useFlowSync — React Query hooks for version control sync operations
|
|
3
|
+
*/
|
|
4
|
+
import type { VcFlowSyncStatus, VcSyncHistoryRecord, VcSyncResult, VcSyncConfig, ConfigureSyncInput } from '../../shared/types';
|
|
5
|
+
export declare const vcQueryKeys: {
|
|
6
|
+
syncStatus: (flowId: string) => readonly ["vc", "sync-status", string];
|
|
7
|
+
syncHistory: (flowId: string) => readonly ["vc", "sync-history", string];
|
|
8
|
+
syncedFlows: () => readonly ["vc", "synced-flows"];
|
|
9
|
+
};
|
|
10
|
+
/** Fetch sync status for a flow */
|
|
11
|
+
export declare function useFlowSyncStatus(flowId: string | undefined): import("@tanstack/react-query").UseQueryResult<VcFlowSyncStatus, Error>;
|
|
12
|
+
/** Fetch sync history for a flow */
|
|
13
|
+
export declare function useFlowSyncHistory(flowId: string | undefined): import("@tanstack/react-query").UseQueryResult<{
|
|
14
|
+
flowId: string;
|
|
15
|
+
history: VcSyncHistoryRecord[];
|
|
16
|
+
}, Error>;
|
|
17
|
+
/** List all synced flows */
|
|
18
|
+
export declare function useSyncedFlows(): import("@tanstack/react-query").UseQueryResult<{
|
|
19
|
+
flows: Array<VcSyncConfig & {
|
|
20
|
+
flowName: string;
|
|
21
|
+
}>;
|
|
22
|
+
}, Error>;
|
|
23
|
+
/** Push a flow to remote */
|
|
24
|
+
export declare function usePushFlow(flowId: string): import("@tanstack/react-query").UseMutationResult<VcSyncResult, Error, void, unknown>;
|
|
25
|
+
/** Pull a flow from remote */
|
|
26
|
+
export declare function usePullFlow(flowId: string): import("@tanstack/react-query").UseMutationResult<VcSyncResult, Error, void, unknown>;
|
|
27
|
+
/** Force push (local wins) */
|
|
28
|
+
export declare function useForcePushFlow(flowId: string): import("@tanstack/react-query").UseMutationResult<VcSyncResult, Error, void, unknown>;
|
|
29
|
+
/** Force pull (remote wins) */
|
|
30
|
+
export declare function useForcePullFlow(flowId: string): import("@tanstack/react-query").UseMutationResult<VcSyncResult, Error, void, unknown>;
|
|
31
|
+
/** Publish flow (pr-per-publish mode) */
|
|
32
|
+
export declare function usePublishFlow(flowId: string): import("@tanstack/react-query").UseMutationResult<VcSyncResult, Error, void, unknown>;
|
|
33
|
+
/** Configure sync for a flow */
|
|
34
|
+
export declare function useConfigureSync(flowId: string): import("@tanstack/react-query").UseMutationResult<VcSyncConfig, Error, ConfigureSyncInput, unknown>;
|
|
35
|
+
/** Disconnect sync for a flow */
|
|
36
|
+
export declare function useDisconnectSync(flowId: string): import("@tanstack/react-query").UseMutationResult<any, Error, void, unknown>;
|
|
37
|
+
//# sourceMappingURL=useFlowSync.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useFlowSync.d.ts","sourceRoot":"","sources":["../../../src/frontend/hooks/useFlowSync.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,OAAO,KAAK,EACV,gBAAgB,EAChB,mBAAmB,EACnB,YAAY,EACZ,YAAY,EACZ,kBAAkB,EACnB,MAAM,oBAAoB,CAAC;AAE5B,eAAO,MAAM,WAAW;yBACD,MAAM;0BACL,MAAM;;CAE7B,CAAC;AAEF,mCAAmC;AACnC,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,2EAiB3D;AAED,oCAAoC;AACpC,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS;YAGjC,MAAM;aAAW,mBAAmB,EAAE;UAcjE;AAED,4BAA4B;AAC5B,wBAAgB,cAAc;WAGH,KAAK,CAAC,YAAY,GAAG;QAAE,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC;UAYpE;AAED,4BAA4B;AAC5B,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,yFAoBzC;AAED,8BAA8B;AAC9B,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,yFAoBzC;AAED,8BAA8B;AAC9B,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,yFAoB9C;AAED,+BAA+B;AAC/B,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,yFAoB9C;AAED,yCAAyC;AACzC,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,yFAoB5C;AAED,gCAAgC;AAChC,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,uGA0B9C;AAED,iCAAiC;AACjC,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,gFAoB/C"}
|