@livestore/cli 0.4.0-dev.9 → 0.5.0-dev.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/.tsbuildinfo +1 -0
- package/dist/__tests__/fixtures/mock-config.d.ts +63 -0
- package/dist/__tests__/fixtures/mock-config.d.ts.map +1 -0
- package/dist/__tests__/fixtures/mock-config.js +94 -0
- package/dist/__tests__/fixtures/mock-config.js.map +1 -0
- package/dist/__tests__/sync-operations.test.d.ts +2 -0
- package/dist/__tests__/sync-operations.test.d.ts.map +1 -0
- package/dist/__tests__/sync-operations.test.js +167 -0
- package/dist/__tests__/sync-operations.test.js.map +1 -0
- package/dist/bin.js +6 -5
- package/dist/bin.js.map +1 -1
- package/dist/cli.d.ts +2 -9
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +4 -5
- package/dist/cli.js.map +1 -1
- package/dist/commands/__fixtures__/mcp-protocol-server.d.ts +2 -0
- package/dist/commands/__fixtures__/mcp-protocol-server.d.ts.map +1 -0
- package/dist/commands/__fixtures__/mcp-protocol-server.js +10 -0
- package/dist/commands/__fixtures__/mcp-protocol-server.js.map +1 -0
- package/dist/commands/import-export.d.ts +20 -0
- package/dist/commands/import-export.d.ts.map +1 -0
- package/dist/commands/import-export.js +135 -0
- package/dist/commands/import-export.js.map +1 -0
- package/dist/commands/mcp-coach.d.ts +21 -12
- package/dist/commands/mcp-coach.d.ts.map +1 -1
- package/dist/commands/mcp-coach.js +49 -53
- package/dist/commands/mcp-coach.js.map +1 -1
- package/dist/commands/mcp-protocols.d.ts +7 -0
- package/dist/commands/mcp-protocols.d.ts.map +1 -0
- package/dist/commands/mcp-protocols.js +12 -0
- package/dist/commands/mcp-protocols.js.map +1 -0
- package/dist/commands/mcp-protocols.test.d.ts +2 -0
- package/dist/commands/mcp-protocols.test.d.ts.map +1 -0
- package/dist/commands/mcp-protocols.test.js +60 -0
- package/dist/commands/mcp-protocols.test.js.map +1 -0
- package/dist/commands/mcp-stdio.test.d.ts +2 -0
- package/dist/commands/mcp-stdio.test.d.ts.map +1 -0
- package/dist/commands/mcp-stdio.test.js +79 -0
- package/dist/commands/mcp-stdio.test.js.map +1 -0
- package/dist/commands/mcp-tool-handlers.d.ts +6 -0
- package/dist/commands/mcp-tool-handlers.d.ts.map +1 -0
- package/dist/commands/{mcp-tools.js → mcp-tool-handlers.js} +80 -45
- package/dist/commands/mcp-tool-handlers.js.map +1 -0
- package/dist/commands/mcp-tools-defs.d.ts +134 -0
- package/dist/commands/mcp-tools-defs.d.ts.map +1 -0
- package/dist/commands/mcp-tools-defs.js +285 -0
- package/dist/commands/mcp-tools-defs.js.map +1 -0
- package/dist/commands/mcp-tools-defs.test.d.ts +2 -0
- package/dist/commands/mcp-tools-defs.test.d.ts.map +1 -0
- package/dist/commands/mcp-tools-defs.test.js +11 -0
- package/dist/commands/mcp-tools-defs.test.js.map +1 -0
- package/dist/commands/mcp.d.ts +1 -3
- package/dist/commands/mcp.d.ts.map +1 -1
- package/dist/commands/mcp.js +7 -5
- package/dist/commands/mcp.js.map +1 -1
- package/dist/commands/new-project.d.ts +22 -23
- package/dist/commands/new-project.d.ts.map +1 -1
- package/dist/commands/new-project.js +85 -44
- package/dist/commands/new-project.js.map +1 -1
- package/dist/mcp-content/architecture.d.ts +1 -1
- package/dist/mcp-content/architecture.d.ts.map +1 -1
- package/dist/mcp-content/architecture.js +3 -3
- package/dist/mcp-content/features.d.ts +1 -1
- package/dist/mcp-content/features.d.ts.map +1 -1
- package/dist/mcp-content/features.js +1 -1
- package/dist/mcp-content/getting-started.d.ts +1 -1
- package/dist/mcp-content/getting-started.d.ts.map +1 -1
- package/dist/mcp-content/getting-started.js +37 -37
- package/dist/mcp-content/overview.d.ts +1 -1
- package/dist/mcp-content/overview.d.ts.map +1 -1
- package/dist/mcp-content/overview.js +6 -6
- package/dist/mcp-content/schemas/blog.d.ts +1 -1
- package/dist/mcp-content/schemas/blog.d.ts.map +1 -1
- package/dist/mcp-content/schemas/blog.js +25 -25
- package/dist/mcp-content/schemas/ecommerce.d.ts +1 -1
- package/dist/mcp-content/schemas/ecommerce.d.ts.map +1 -1
- package/dist/mcp-content/schemas/ecommerce.js +67 -67
- package/dist/mcp-content/schemas/social.d.ts +1 -1
- package/dist/mcp-content/schemas/social.d.ts.map +1 -1
- package/dist/mcp-content/schemas/social.js +48 -48
- package/dist/mcp-content/schemas/todo.d.ts +1 -1
- package/dist/mcp-content/schemas/todo.d.ts.map +1 -1
- package/dist/mcp-content/schemas/todo.js +35 -35
- package/dist/mcp-runtime/runtime.d.ts +48 -0
- package/dist/mcp-runtime/runtime.d.ts.map +1 -0
- package/dist/mcp-runtime/runtime.js +107 -0
- package/dist/mcp-runtime/runtime.js.map +1 -0
- package/dist/module-loader.d.ts +22 -0
- package/dist/module-loader.d.ts.map +1 -0
- package/dist/module-loader.js +75 -0
- package/dist/module-loader.js.map +1 -0
- package/dist/package-manager.d.ts +38 -0
- package/dist/package-manager.d.ts.map +1 -0
- package/dist/package-manager.js +33 -0
- package/dist/package-manager.js.map +1 -0
- package/dist/package-manager.test.d.ts +2 -0
- package/dist/package-manager.test.d.ts.map +1 -0
- package/dist/package-manager.test.js +57 -0
- package/dist/package-manager.test.js.map +1 -0
- package/dist/sync-operations.d.ts +115 -0
- package/dist/sync-operations.d.ts.map +1 -0
- package/dist/sync-operations.js +178 -0
- package/dist/sync-operations.js.map +1 -0
- package/package.json +57 -15
- package/src/__tests__/fixtures/mock-config.ts +112 -0
- package/src/__tests__/sync-operations.test.ts +218 -0
- package/src/bin.ts +8 -6
- package/src/cli.ts +5 -6
- package/src/commands/__fixtures__/mcp-protocol-server.ts +12 -0
- package/src/commands/import-export.ts +283 -0
- package/src/commands/mcp-coach.ts +76 -75
- package/src/commands/mcp-protocols.test.ts +76 -0
- package/src/commands/mcp-protocols.ts +12 -0
- package/src/commands/mcp-stdio.test.ts +93 -0
- package/src/commands/{mcp-tools.ts → mcp-tool-handlers.ts} +100 -61
- package/src/commands/mcp-tools-defs.test.ts +13 -0
- package/src/commands/mcp-tools-defs.ts +320 -0
- package/src/commands/mcp.ts +10 -6
- package/src/commands/new-project.ts +124 -57
- package/src/mcp-content/architecture.ts +3 -3
- package/src/mcp-content/features.ts +1 -1
- package/src/mcp-content/getting-started.ts +37 -37
- package/src/mcp-content/overview.ts +6 -6
- package/src/mcp-content/schemas/blog.ts +25 -25
- package/src/mcp-content/schemas/ecommerce.ts +67 -67
- package/src/mcp-content/schemas/social.ts +48 -48
- package/src/mcp-content/schemas/todo.ts +35 -35
- package/src/mcp-runtime/runtime.ts +147 -0
- package/src/module-loader.ts +94 -0
- package/src/package-manager.test.ts +66 -0
- package/src/package-manager.ts +44 -0
- package/src/sync-operations.ts +360 -0
- package/dist/commands/mcp-tools.d.ts +0 -58
- package/dist/commands/mcp-tools.d.ts.map +0 -1
- package/dist/commands/mcp-tools.js.map +0 -1
- package/dist/tsconfig.tsbuildinfo +0 -1
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { Vitest } from '@livestore/utils-dev/node-vitest'
|
|
2
|
+
|
|
3
|
+
import { detectPackageManager, pmCommands } from './package-manager.ts'
|
|
4
|
+
|
|
5
|
+
Vitest.describe('detectPackageManager', () => {
|
|
6
|
+
Vitest.it('detects npm from user agent', () => {
|
|
7
|
+
const result = detectPackageManager('npm/10.2.4 node/v20.11.0 darwin arm64 workspaces/false')
|
|
8
|
+
Vitest.expect(result).toEqual({ _tag: 'supported', pm: 'npm' })
|
|
9
|
+
})
|
|
10
|
+
|
|
11
|
+
Vitest.it('detects pnpm from user agent', () => {
|
|
12
|
+
const result = detectPackageManager('pnpm/9.0.6 npm/? node/v22.0.0 darwin arm64')
|
|
13
|
+
Vitest.expect(result).toEqual({ _tag: 'supported', pm: 'pnpm' })
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
Vitest.it('detects yarn as unsupported', () => {
|
|
17
|
+
const result = detectPackageManager('yarn/1.22.19 npm/? node/v20.11.0 darwin arm64')
|
|
18
|
+
Vitest.expect(result).toEqual({ _tag: 'unsupported', pm: 'yarn' })
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
Vitest.it('detects bun from user agent', () => {
|
|
22
|
+
const result = detectPackageManager('bun/1.1.7')
|
|
23
|
+
Vitest.expect(result).toEqual({ _tag: 'supported', pm: 'bun' })
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
Vitest.it('falls back to bun for empty user agent', () => {
|
|
27
|
+
const result = detectPackageManager('')
|
|
28
|
+
Vitest.expect(result).toEqual({ _tag: 'supported', pm: 'bun' })
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
Vitest.it('uses process.env.npm_config_user_agent when no argument provided', () => {
|
|
32
|
+
const originalEnv = process.env.npm_config_user_agent
|
|
33
|
+
try {
|
|
34
|
+
process.env.npm_config_user_agent = 'pnpm/9.0.6'
|
|
35
|
+
const result = detectPackageManager()
|
|
36
|
+
Vitest.expect(result).toEqual({ _tag: 'supported', pm: 'pnpm' })
|
|
37
|
+
} finally {
|
|
38
|
+
process.env.npm_config_user_agent = originalEnv
|
|
39
|
+
}
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
Vitest.it('falls back to bun for unknown user agent', () => {
|
|
43
|
+
const result = detectPackageManager('some-unknown-tool/1.0.0')
|
|
44
|
+
Vitest.expect(result).toEqual({ _tag: 'supported', pm: 'bun' })
|
|
45
|
+
})
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
Vitest.describe('pmCommands', () => {
|
|
49
|
+
Vitest.it('provides correct install commands for each package manager', () => {
|
|
50
|
+
Vitest.expect(pmCommands.install.npm).toBe('npm install')
|
|
51
|
+
Vitest.expect(pmCommands.install.pnpm).toBe('pnpm install')
|
|
52
|
+
Vitest.expect(pmCommands.install.bun).toBe('bun install')
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
Vitest.it('provides correct run commands for dev script', () => {
|
|
56
|
+
Vitest.expect(pmCommands.run.npm('dev')).toBe('npm run dev')
|
|
57
|
+
Vitest.expect(pmCommands.run.pnpm('dev')).toBe('pnpm dev')
|
|
58
|
+
Vitest.expect(pmCommands.run.bun('dev')).toBe('bun dev')
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
Vitest.it('provides correct run commands for start script', () => {
|
|
62
|
+
Vitest.expect(pmCommands.run.npm('start')).toBe('npm run start')
|
|
63
|
+
Vitest.expect(pmCommands.run.pnpm('start')).toBe('pnpm start')
|
|
64
|
+
Vitest.expect(pmCommands.run.bun('start')).toBe('bun start')
|
|
65
|
+
})
|
|
66
|
+
})
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Supported package managers for LiveStore projects.
|
|
3
|
+
* Yarn is explicitly not supported due to compatibility issues.
|
|
4
|
+
*/
|
|
5
|
+
export type PackageManager = 'npm' | 'pnpm' | 'bun'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Result of package manager detection.
|
|
9
|
+
* Returns 'unsupported' for yarn to allow the CLI to show a warning message.
|
|
10
|
+
*/
|
|
11
|
+
export type DetectPackageManagerResult = { _tag: 'supported'; pm: PackageManager } | { _tag: 'unsupported'; pm: 'yarn' }
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Detects the package manager used to invoke the CLI based on the `npm_config_user_agent`
|
|
15
|
+
* environment variable. This env var is set by npm, pnpm, yarn, and bun when running scripts.
|
|
16
|
+
*
|
|
17
|
+
* - Returns 'unsupported' for yarn so the CLI can show a recommendation to use bun instead
|
|
18
|
+
* - Falls back to 'bun' when detection fails (e.g., when run directly without a package manager)
|
|
19
|
+
*/
|
|
20
|
+
export const detectPackageManager = (
|
|
21
|
+
userAgent = process.env.npm_config_user_agent ?? '',
|
|
22
|
+
): DetectPackageManagerResult => {
|
|
23
|
+
if (userAgent.startsWith('bun/') === true) return { _tag: 'supported', pm: 'bun' }
|
|
24
|
+
if (userAgent.startsWith('pnpm/') === true) return { _tag: 'supported', pm: 'pnpm' }
|
|
25
|
+
if (userAgent.startsWith('npm/') === true) return { _tag: 'supported', pm: 'npm' }
|
|
26
|
+
if (userAgent.startsWith('yarn/') === true) return { _tag: 'unsupported', pm: 'yarn' }
|
|
27
|
+
|
|
28
|
+
// Default to bun when the package manager can't be detected
|
|
29
|
+
return { _tag: 'supported', pm: 'bun' }
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** Package manager command templates */
|
|
33
|
+
export const pmCommands = {
|
|
34
|
+
install: {
|
|
35
|
+
npm: 'npm install',
|
|
36
|
+
pnpm: 'pnpm install',
|
|
37
|
+
bun: 'bun install',
|
|
38
|
+
},
|
|
39
|
+
run: {
|
|
40
|
+
npm: (script: string) => `npm run ${script}`,
|
|
41
|
+
pnpm: (script: string) => `pnpm ${script}`,
|
|
42
|
+
bun: (script: string) => `bun ${script}`,
|
|
43
|
+
},
|
|
44
|
+
} as const
|
|
@@ -0,0 +1,360 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared sync operations for CLI and MCP.
|
|
3
|
+
* Contains the core logic for exporting and importing events from sync backends.
|
|
4
|
+
*/
|
|
5
|
+
import type { SyncBackend } from '@livestore/common'
|
|
6
|
+
import { UnknownError } from '@livestore/common'
|
|
7
|
+
import { LiveStoreEvent } from '@livestore/common/schema'
|
|
8
|
+
import {
|
|
9
|
+
Cause,
|
|
10
|
+
Effect,
|
|
11
|
+
type FileSystem,
|
|
12
|
+
type HttpClient,
|
|
13
|
+
KeyValueStore,
|
|
14
|
+
Option,
|
|
15
|
+
Schema,
|
|
16
|
+
type Scope,
|
|
17
|
+
Stream,
|
|
18
|
+
SubscriptionRef,
|
|
19
|
+
} from '@livestore/utils/effect'
|
|
20
|
+
|
|
21
|
+
import { loadModuleConfig } from './module-loader.ts'
|
|
22
|
+
|
|
23
|
+
/** Connection timeout for sync backend ping (5 seconds) */
|
|
24
|
+
const CONNECTION_TIMEOUT_MS = 5000
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Schema for the export file format.
|
|
28
|
+
* Contains metadata about the export and an array of events in global encoded format.
|
|
29
|
+
*/
|
|
30
|
+
export const ExportFileSchema = Schema.Struct({
|
|
31
|
+
/** Format version for future compatibility */
|
|
32
|
+
version: Schema.Literal(1),
|
|
33
|
+
/** Store identifier */
|
|
34
|
+
storeId: Schema.String,
|
|
35
|
+
/** ISO timestamp of when the export was created */
|
|
36
|
+
exportedAt: Schema.String,
|
|
37
|
+
/** Total number of events in the export */
|
|
38
|
+
eventCount: Schema.Number,
|
|
39
|
+
/** Array of events in global encoded format */
|
|
40
|
+
events: Schema.Array(LiveStoreEvent.Global.Encoded),
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
export type ExportFile = typeof ExportFileSchema.Type
|
|
44
|
+
|
|
45
|
+
export class ConnectionError extends Schema.TaggedError<ConnectionError>('~@livestore/cli/ConnectionError')(
|
|
46
|
+
'ConnectionError',
|
|
47
|
+
{
|
|
48
|
+
cause: Schema.Defect(),
|
|
49
|
+
note: Schema.String,
|
|
50
|
+
},
|
|
51
|
+
) {}
|
|
52
|
+
|
|
53
|
+
export class ExportError extends Schema.TaggedError<ExportError>('~@livestore/cli/ExportError')('ExportError', {
|
|
54
|
+
cause: Schema.Defect(),
|
|
55
|
+
note: Schema.String,
|
|
56
|
+
}) {}
|
|
57
|
+
|
|
58
|
+
export class ImportError extends Schema.TaggedError<ImportError>('~@livestore/cli/ImportError')('ImportError', {
|
|
59
|
+
cause: Schema.Defect(),
|
|
60
|
+
note: Schema.String,
|
|
61
|
+
}) {}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Creates a sync backend connection from a user module and verifies connectivity.
|
|
65
|
+
* This is a simplified version of the MCP runtime that only creates the sync backend.
|
|
66
|
+
*/
|
|
67
|
+
export const makeSyncBackend = ({
|
|
68
|
+
configPath,
|
|
69
|
+
storeId,
|
|
70
|
+
clientId,
|
|
71
|
+
}: {
|
|
72
|
+
/** Absolute or cwd-relative path to a module exporting `schema` and `syncBackend`. */
|
|
73
|
+
configPath: string
|
|
74
|
+
/** Identifier to scope the backend connection. */
|
|
75
|
+
storeId: string
|
|
76
|
+
/** Client identifier used when establishing the sync connection. */
|
|
77
|
+
clientId: string
|
|
78
|
+
}): Effect.Effect<
|
|
79
|
+
SyncBackend.SyncBackend,
|
|
80
|
+
UnknownError | ConnectionError,
|
|
81
|
+
FileSystem.FileSystem | HttpClient.HttpClient | Scope.Scope
|
|
82
|
+
> =>
|
|
83
|
+
Effect.gen(function* () {
|
|
84
|
+
const { syncBackendConstructor, syncPayload } = yield* loadModuleConfig({ configPath })
|
|
85
|
+
|
|
86
|
+
const syncBackend = yield* syncBackendConstructor({
|
|
87
|
+
storeId,
|
|
88
|
+
clientId,
|
|
89
|
+
/** syncPayload is validated against syncPayloadSchema by loadModuleConfig */
|
|
90
|
+
payload: syncPayload as Schema.Json | undefined,
|
|
91
|
+
}).pipe(Effect.provide(KeyValueStore.layerMemory), UnknownError.mapToUnknownError)
|
|
92
|
+
|
|
93
|
+
/** Connect to the sync backend */
|
|
94
|
+
yield* syncBackend.connect.pipe(
|
|
95
|
+
Effect.mapError(
|
|
96
|
+
(cause) =>
|
|
97
|
+
new ConnectionError({
|
|
98
|
+
cause,
|
|
99
|
+
note: `Failed to connect to sync backend: ${cause._tag === 'IsOfflineError' ? 'Backend is offline or unreachable' : String(cause)}`,
|
|
100
|
+
}),
|
|
101
|
+
),
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
/** Verify connectivity with a ping (with timeout) */
|
|
105
|
+
yield* syncBackend.ping.pipe(
|
|
106
|
+
Effect.timeout(CONNECTION_TIMEOUT_MS),
|
|
107
|
+
Effect.catch((cause) => {
|
|
108
|
+
if (Cause.isTimeoutError(cause) === true) {
|
|
109
|
+
return Effect.fail(
|
|
110
|
+
new ConnectionError({
|
|
111
|
+
cause,
|
|
112
|
+
note: `Connection timeout: Sync backend did not respond within ${CONNECTION_TIMEOUT_MS}ms`,
|
|
113
|
+
}),
|
|
114
|
+
)
|
|
115
|
+
}
|
|
116
|
+
return Effect.fail(
|
|
117
|
+
new ConnectionError({
|
|
118
|
+
cause,
|
|
119
|
+
note: `Failed to ping sync backend: ${cause._tag === 'IsOfflineError' ? 'Backend is offline or unreachable' : String(cause)}`,
|
|
120
|
+
}),
|
|
121
|
+
)
|
|
122
|
+
}),
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
return syncBackend
|
|
126
|
+
})
|
|
127
|
+
|
|
128
|
+
const releaseSyncBackend = (syncBackend: SyncBackend.SyncBackend): Effect.Effect<void> => {
|
|
129
|
+
const maybeDisconnect = (syncBackend as { disconnect?: Effect.Effect<void> }).disconnect
|
|
130
|
+
const releaseEffect = maybeDisconnect ?? SubscriptionRef.set(syncBackend.isConnected, false)
|
|
131
|
+
return releaseEffect.pipe(Effect.catch(() => Effect.void))
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export interface ExportResult {
|
|
135
|
+
storeId: string
|
|
136
|
+
eventCount: number
|
|
137
|
+
exportedAt: string
|
|
138
|
+
backendName: string
|
|
139
|
+
/** The export data as JSON string (for MCP) or written to file (for CLI) */
|
|
140
|
+
data: ExportFile
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Core export operation - pulls all events from sync backend.
|
|
145
|
+
* Returns the export data structure without writing to file.
|
|
146
|
+
*/
|
|
147
|
+
export const pullEventsFromSyncBackend = ({
|
|
148
|
+
configPath,
|
|
149
|
+
storeId,
|
|
150
|
+
clientId,
|
|
151
|
+
}: {
|
|
152
|
+
configPath: string
|
|
153
|
+
storeId: string
|
|
154
|
+
clientId: string
|
|
155
|
+
}): Effect.Effect<
|
|
156
|
+
ExportResult,
|
|
157
|
+
ExportError | UnknownError | ConnectionError,
|
|
158
|
+
FileSystem.FileSystem | HttpClient.HttpClient | Scope.Scope
|
|
159
|
+
> =>
|
|
160
|
+
Effect.acquireUseRelease(
|
|
161
|
+
makeSyncBackend({ configPath, storeId, clientId }),
|
|
162
|
+
(syncBackend) =>
|
|
163
|
+
Effect.gen(function* () {
|
|
164
|
+
const backendName = syncBackend.metadata.name
|
|
165
|
+
|
|
166
|
+
const batchesChunk = yield* syncBackend.pull(Option.none(), { live: false }).pipe(
|
|
167
|
+
Stream.takeUntil((item) => item.pageInfo._tag === 'NoMore'),
|
|
168
|
+
Stream.runCollect,
|
|
169
|
+
Effect.mapError(
|
|
170
|
+
(cause) =>
|
|
171
|
+
new ExportError({
|
|
172
|
+
cause,
|
|
173
|
+
note: `Failed to pull events from sync backend: ${String(cause)}`,
|
|
174
|
+
}),
|
|
175
|
+
),
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
const events = batchesChunk.flatMap((item) => item.batch).map((item) => item.eventEncoded)
|
|
179
|
+
|
|
180
|
+
const exportedAt = new Date().toISOString()
|
|
181
|
+
const exportData: ExportFile = {
|
|
182
|
+
version: 1,
|
|
183
|
+
storeId,
|
|
184
|
+
exportedAt,
|
|
185
|
+
eventCount: events.length,
|
|
186
|
+
events,
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
return {
|
|
190
|
+
storeId,
|
|
191
|
+
eventCount: events.length,
|
|
192
|
+
exportedAt,
|
|
193
|
+
backendName,
|
|
194
|
+
data: exportData,
|
|
195
|
+
}
|
|
196
|
+
}),
|
|
197
|
+
releaseSyncBackend,
|
|
198
|
+
).pipe(Effect.withSpan('sync:pullEvents'))
|
|
199
|
+
|
|
200
|
+
export interface ImportResult {
|
|
201
|
+
storeId: string
|
|
202
|
+
eventCount: number
|
|
203
|
+
/** Whether this was a dry run */
|
|
204
|
+
dryRun: boolean
|
|
205
|
+
backendName?: string
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
export interface ImportValidationResult {
|
|
209
|
+
storeId: string
|
|
210
|
+
eventCount: number
|
|
211
|
+
sourceStoreId: string
|
|
212
|
+
storeIdMismatch: boolean
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Validates an export file for import.
|
|
217
|
+
* Returns validation info without actually importing.
|
|
218
|
+
*/
|
|
219
|
+
export const validateExportData = ({
|
|
220
|
+
data,
|
|
221
|
+
targetStoreId,
|
|
222
|
+
}: {
|
|
223
|
+
data: unknown
|
|
224
|
+
targetStoreId: string
|
|
225
|
+
}): Effect.Effect<ImportValidationResult, ImportError> =>
|
|
226
|
+
Effect.gen(function* () {
|
|
227
|
+
const exportData = yield* Schema.decodeUnknownEffect(ExportFileSchema)(data).pipe(
|
|
228
|
+
Effect.mapError(
|
|
229
|
+
(cause) =>
|
|
230
|
+
new ImportError({
|
|
231
|
+
cause: new Error(`Invalid export file format: ${String(cause)}`),
|
|
232
|
+
note: `Invalid export file format: ${String(cause)}`,
|
|
233
|
+
}),
|
|
234
|
+
),
|
|
235
|
+
)
|
|
236
|
+
|
|
237
|
+
return {
|
|
238
|
+
storeId: targetStoreId,
|
|
239
|
+
eventCount: exportData.events.length,
|
|
240
|
+
sourceStoreId: exportData.storeId,
|
|
241
|
+
storeIdMismatch: exportData.storeId !== targetStoreId,
|
|
242
|
+
}
|
|
243
|
+
})
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Core import operation - pushes events to sync backend.
|
|
247
|
+
* Validates that the backend is empty before importing.
|
|
248
|
+
*/
|
|
249
|
+
export const pushEventsToSyncBackend = ({
|
|
250
|
+
configPath,
|
|
251
|
+
storeId,
|
|
252
|
+
clientId,
|
|
253
|
+
data,
|
|
254
|
+
force,
|
|
255
|
+
dryRun,
|
|
256
|
+
onProgress,
|
|
257
|
+
}: {
|
|
258
|
+
configPath: string
|
|
259
|
+
storeId: string
|
|
260
|
+
clientId: string
|
|
261
|
+
/** The export data to import (already parsed) */
|
|
262
|
+
data: unknown
|
|
263
|
+
force: boolean
|
|
264
|
+
dryRun: boolean
|
|
265
|
+
onProgress?: (pushed: number, total: number) => Effect.Effect<void>
|
|
266
|
+
}): Effect.Effect<
|
|
267
|
+
ImportResult,
|
|
268
|
+
ImportError | UnknownError | ConnectionError,
|
|
269
|
+
FileSystem.FileSystem | HttpClient.HttpClient | Scope.Scope
|
|
270
|
+
> =>
|
|
271
|
+
Effect.acquireUseRelease(
|
|
272
|
+
makeSyncBackend({ configPath, storeId, clientId }),
|
|
273
|
+
(syncBackend) =>
|
|
274
|
+
Effect.gen(function* () {
|
|
275
|
+
const exportData = yield* Schema.decodeUnknownEffect(ExportFileSchema)(data).pipe(
|
|
276
|
+
Effect.mapError(
|
|
277
|
+
(cause) =>
|
|
278
|
+
new ImportError({
|
|
279
|
+
cause: new Error(`Invalid export file format: ${String(cause)}`),
|
|
280
|
+
note: `Invalid export file format: ${String(cause)}`,
|
|
281
|
+
}),
|
|
282
|
+
),
|
|
283
|
+
)
|
|
284
|
+
|
|
285
|
+
if (exportData.storeId !== storeId && force === false) {
|
|
286
|
+
return yield* new ImportError({
|
|
287
|
+
cause: new Error(`Store ID mismatch: file has '${exportData.storeId}', expected '${storeId}'`),
|
|
288
|
+
note: `The export file was created for a different store. Use force option to import anyway.`,
|
|
289
|
+
})
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
if (dryRun === true) {
|
|
293
|
+
return {
|
|
294
|
+
storeId,
|
|
295
|
+
eventCount: exportData.events.length,
|
|
296
|
+
dryRun: true,
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
const backendName = syncBackend.metadata.name
|
|
301
|
+
|
|
302
|
+
/** Check if events already exist by pulling from the backend first (short-circuit on first non-empty batch) */
|
|
303
|
+
const existingBatchOption = yield* syncBackend.pull(Option.none(), { live: false }).pipe(
|
|
304
|
+
Stream.filter((item) => item.batch.length > 0),
|
|
305
|
+
Stream.runHead,
|
|
306
|
+
Effect.mapError(
|
|
307
|
+
(cause) =>
|
|
308
|
+
new ImportError({
|
|
309
|
+
cause,
|
|
310
|
+
note: `Failed to check existing events: ${String(cause)}`,
|
|
311
|
+
}),
|
|
312
|
+
),
|
|
313
|
+
)
|
|
314
|
+
|
|
315
|
+
if (Option.isSome(existingBatchOption) === true) {
|
|
316
|
+
const existingBatch = existingBatchOption.value
|
|
317
|
+
const estimatedCount =
|
|
318
|
+
existingBatch.pageInfo._tag === 'MoreKnown'
|
|
319
|
+
? existingBatch.batch.length + existingBatch.pageInfo.remaining
|
|
320
|
+
: existingBatch.batch.length
|
|
321
|
+
|
|
322
|
+
return yield* new ImportError({
|
|
323
|
+
cause: new Error(`Sync backend already contains at least ${estimatedCount} events`),
|
|
324
|
+
note: `Cannot import into a non-empty sync backend. The sync backend must be empty.`,
|
|
325
|
+
})
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
/** Push events in batches of 100 (sync backend constraint) */
|
|
329
|
+
const batchSize = 100
|
|
330
|
+
const total = exportData.events.length
|
|
331
|
+
let pushed = 0
|
|
332
|
+
|
|
333
|
+
for (let i = 0; i < exportData.events.length; i += batchSize) {
|
|
334
|
+
const batch = exportData.events.slice(i, i + batchSize)
|
|
335
|
+
|
|
336
|
+
yield* syncBackend.push(batch).pipe(
|
|
337
|
+
Effect.mapError(
|
|
338
|
+
(cause) =>
|
|
339
|
+
new ImportError({
|
|
340
|
+
cause,
|
|
341
|
+
note: `Failed to push events at position ${i}: ${String(cause)}`,
|
|
342
|
+
}),
|
|
343
|
+
),
|
|
344
|
+
)
|
|
345
|
+
|
|
346
|
+
pushed += batch.length
|
|
347
|
+
if (onProgress !== undefined) {
|
|
348
|
+
yield* onProgress(pushed, total)
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
return {
|
|
353
|
+
storeId,
|
|
354
|
+
eventCount: exportData.events.length,
|
|
355
|
+
dryRun: false,
|
|
356
|
+
backendName,
|
|
357
|
+
}
|
|
358
|
+
}),
|
|
359
|
+
releaseSyncBackend,
|
|
360
|
+
).pipe(Effect.withSpan('sync:pushEvents'))
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
import { Effect, Schema, Tool, Toolkit } from '@livestore/utils/effect';
|
|
2
|
-
export declare const livestoreToolkit: Toolkit.Toolkit<{
|
|
3
|
-
readonly livestore_coach: Tool.Tool<"livestore_coach", {
|
|
4
|
-
readonly parameters: Schema.Struct<{
|
|
5
|
-
code: Schema.SchemaClass<string, string, never>;
|
|
6
|
-
codeType: Schema.optional<Schema.SchemaClass<string, string, never>>;
|
|
7
|
-
}>;
|
|
8
|
-
readonly success: Schema.Struct<{
|
|
9
|
-
feedback: Schema.SchemaClass<string, string, never>;
|
|
10
|
-
score: Schema.optional<Schema.SchemaClass<number, number, never>>;
|
|
11
|
-
suggestions: Schema.Array$<Schema.SchemaClass<string, string, never>>;
|
|
12
|
-
}>;
|
|
13
|
-
readonly failure: Schema.Union<[typeof import("@effect/ai/AiError").HttpRequestError, typeof import("@effect/ai/AiError").HttpResponseError, typeof import("@effect/ai/AiError").MalformedInput, typeof import("@effect/ai/AiError").MalformedOutput, typeof import("@effect/ai/AiError").UnknownError]>;
|
|
14
|
-
}, never>;
|
|
15
|
-
readonly livestore_generate_schema: Tool.Tool<"livestore_generate_schema", {
|
|
16
|
-
readonly parameters: Schema.Struct<{
|
|
17
|
-
schemaType: Schema.SchemaClass<string, string, never>;
|
|
18
|
-
customDescription: Schema.optional<Schema.SchemaClass<string, string, never>>;
|
|
19
|
-
}>;
|
|
20
|
-
readonly success: Schema.Struct<{
|
|
21
|
-
schemaCode: Schema.SchemaClass<string, string, never>;
|
|
22
|
-
explanation: Schema.SchemaClass<string, string, never>;
|
|
23
|
-
}>;
|
|
24
|
-
readonly failure: typeof Schema.Never;
|
|
25
|
-
}, never>;
|
|
26
|
-
readonly livestore_get_example_schema: Tool.Tool<"livestore_get_example_schema", {
|
|
27
|
-
readonly parameters: Schema.Struct<{
|
|
28
|
-
type: Schema.SchemaClass<string, string, never>;
|
|
29
|
-
}>;
|
|
30
|
-
readonly success: Schema.Struct<{
|
|
31
|
-
schemaCode: Schema.SchemaClass<string, string, never>;
|
|
32
|
-
description: Schema.SchemaClass<string, string, never>;
|
|
33
|
-
}>;
|
|
34
|
-
readonly failure: typeof Schema.Never;
|
|
35
|
-
}, never>;
|
|
36
|
-
}>;
|
|
37
|
-
export declare const toolHandlers: {
|
|
38
|
-
livestore_coach: (args_0: any) => Effect.Effect<{
|
|
39
|
-
feedback: string;
|
|
40
|
-
score: number | undefined;
|
|
41
|
-
suggestions: string[];
|
|
42
|
-
}, import("@effect/ai/AiError").AiError, never>;
|
|
43
|
-
livestore_generate_schema: (args_0: {
|
|
44
|
-
readonly schemaType: string;
|
|
45
|
-
} & {
|
|
46
|
-
readonly customDescription?: string | undefined;
|
|
47
|
-
}) => Effect.Effect<{
|
|
48
|
-
schemaCode: string;
|
|
49
|
-
explanation: string;
|
|
50
|
-
}, never, never>;
|
|
51
|
-
livestore_get_example_schema: (args_0: {
|
|
52
|
-
readonly type: string;
|
|
53
|
-
}) => Effect.Effect<{
|
|
54
|
-
schemaCode: string;
|
|
55
|
-
description: string;
|
|
56
|
-
}, never, never>;
|
|
57
|
-
};
|
|
58
|
-
//# sourceMappingURL=mcp-tools.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"mcp-tools.d.ts","sourceRoot":"","sources":["../../src/commands/mcp-tools.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,yBAAyB,CAAA;AAQvE,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA8C5B,CAAA;AAGD,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;CA+GvB,CAAA"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"mcp-tools.js","sourceRoot":"","sources":["../../src/commands/mcp-tools.ts"],"names":[],"mappings":"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"}
|