@livestore/cli 0.5.0-dev.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/__tests__/fixtures/mock-config.d.ts +3 -2
  3. package/dist/__tests__/fixtures/mock-config.d.ts.map +1 -1
  4. package/dist/__tests__/fixtures/mock-config.js +14 -4
  5. package/dist/__tests__/fixtures/mock-config.js.map +1 -1
  6. package/dist/__tests__/sync-operations.test.js +13 -3
  7. package/dist/__tests__/sync-operations.test.js.map +1 -1
  8. package/dist/bin.js +1 -1
  9. package/dist/cli.d.ts +2 -2
  10. package/dist/cli.d.ts.map +1 -1
  11. package/dist/cli.js +4 -4
  12. package/dist/commands/__fixtures__/mcp-protocol-server.js +2 -2
  13. package/dist/commands/__fixtures__/mcp-protocol-server.js.map +1 -1
  14. package/dist/commands/import-export.d.ts +3 -3
  15. package/dist/commands/import-export.js +11 -11
  16. package/dist/commands/mcp-coach.d.ts +2 -2
  17. package/dist/commands/mcp-coach.d.ts.map +1 -1
  18. package/dist/commands/mcp-coach.js +3 -3
  19. package/dist/commands/mcp-coach.js.map +1 -1
  20. package/dist/commands/mcp-protocols.d.ts +2 -2
  21. package/dist/commands/mcp-protocols.d.ts.map +1 -1
  22. package/dist/commands/mcp-protocols.js +1 -1
  23. package/dist/commands/mcp-protocols.js.map +1 -1
  24. package/dist/commands/mcp-protocols.test.js +1 -1
  25. package/dist/commands/mcp-protocols.test.js.map +1 -1
  26. package/dist/commands/mcp-stdio.test.js.map +1 -1
  27. package/dist/commands/mcp-tool-handlers.d.ts +1 -1
  28. package/dist/commands/mcp-tool-handlers.d.ts.map +1 -1
  29. package/dist/commands/mcp-tool-handlers.js +9 -9
  30. package/dist/commands/mcp-tool-handlers.js.map +1 -1
  31. package/dist/commands/mcp-tools-defs.d.ts +26 -26
  32. package/dist/commands/mcp-tools-defs.d.ts.map +1 -1
  33. package/dist/commands/mcp-tools-defs.js +7 -7
  34. package/dist/commands/mcp-tools-defs.js.map +1 -1
  35. package/dist/commands/mcp-tools-defs.test.js +2 -2
  36. package/dist/commands/mcp-tools-defs.test.js.map +1 -1
  37. package/dist/commands/mcp.d.ts.map +1 -1
  38. package/dist/commands/mcp.js +13 -13
  39. package/dist/commands/mcp.js.map +1 -1
  40. package/dist/commands/new-project.d.ts +1 -1
  41. package/dist/commands/new-project.d.ts.map +1 -1
  42. package/dist/commands/new-project.js +9 -9
  43. package/dist/commands/new-project.js.map +1 -1
  44. package/dist/mcp-runtime/runtime.d.ts +3 -3
  45. package/dist/mcp-runtime/runtime.d.ts.map +1 -1
  46. package/dist/mcp-runtime/runtime.js +1 -1
  47. package/dist/mod.js +1 -1
  48. package/dist/module-loader.d.ts.map +1 -1
  49. package/dist/module-loader.js +3 -3
  50. package/dist/module-loader.js.map +1 -1
  51. package/dist/package-manager.d.ts +3 -3
  52. package/dist/package-manager.d.ts.map +1 -1
  53. package/dist/package-manager.test.js +1 -1
  54. package/dist/sync-operations.d.ts +1 -1
  55. package/dist/sync-operations.d.ts.map +1 -1
  56. package/dist/sync-operations.js +6 -4
  57. package/dist/sync-operations.js.map +1 -1
  58. package/package.json +43 -43
  59. package/src/__tests__/fixtures/.gitignore +1 -0
  60. package/src/__tests__/fixtures/mock-config.ts +16 -5
  61. package/src/__tests__/sync-operations.test.ts +16 -1
  62. package/src/bin.ts +0 -0
  63. package/src/cli.ts +1 -1
  64. package/src/commands/__fixtures__/mcp-protocol-server.ts +1 -1
  65. package/src/commands/import-export.ts +10 -10
  66. package/src/commands/mcp-coach.ts +3 -3
  67. package/src/commands/mcp-protocols.ts +1 -1
  68. package/src/commands/mcp-tool-handlers.ts +1 -1
  69. package/src/commands/mcp-tools-defs.test.ts +1 -1
  70. package/src/commands/mcp-tools-defs.ts +6 -6
  71. package/src/commands/mcp.ts +4 -2
  72. package/src/commands/new-project.ts +8 -8
  73. package/src/module-loader.ts +3 -4
  74. package/src/sync-operations.ts +11 -4
package/package.json CHANGED
@@ -1,11 +1,34 @@
1
1
  {
2
+ "$genie": {
3
+ "source": "package.json.genie.ts",
4
+ "warning": "DO NOT EDIT - changes will be overwritten",
5
+ "workspaceClosureDirs": [
6
+ "packages/@livestore/adapter-node",
7
+ "packages/@livestore/cli",
8
+ "repos/livestore/packages/@livestore/adapter-web",
9
+ "repos/livestore/packages/@livestore/common",
10
+ "repos/livestore/packages/@livestore/common-cf",
11
+ "repos/livestore/packages/@livestore/livestore",
12
+ "repos/livestore/packages/@livestore/peer-deps",
13
+ "repos/livestore/packages/@livestore/sqlite-wasm",
14
+ "repos/livestore/packages/@livestore/utils",
15
+ "repos/livestore/packages/@livestore/utils-dev",
16
+ "repos/livestore/packages/@livestore/wa-sqlite",
17
+ "repos/livestore/packages/@livestore/webmesh"
18
+ ]
19
+ },
2
20
  "name": "@livestore/cli",
3
- "version": "0.5.0-dev.0",
21
+ "version": "0.5.0",
22
+ "type": "module",
23
+ "sideEffects": false,
4
24
  "license": "Apache-2.0",
5
25
  "repository": {
6
26
  "type": "git",
7
27
  "url": "git+https://github.com/livestorejs/livestore-contrib.git"
8
28
  },
29
+ "exports": {
30
+ ".": "./dist/mod.js"
31
+ },
9
32
  "bin": {
10
33
  "livestore": "./dist/bin.js"
11
34
  },
@@ -14,59 +37,36 @@
14
37
  "package.json",
15
38
  "src"
16
39
  ],
17
- "type": "module",
18
- "sideEffects": false,
19
- "exports": {
20
- ".": "./dist/mod.js"
21
- },
22
- "publishConfig": {
23
- "access": "public"
24
- },
25
40
  "dependencies": {
26
- "@effect/ai-openai": "4.0.0-rc.111",
27
- "@effect/opentelemetry": "4.0.0-rc.111",
28
- "@livestore/adapter-node": "0.5.0-dev.0",
29
- "@livestore/common": "0.5.0-dev.0",
30
- "@livestore/livestore": "0.5.0-dev.0",
31
- "@livestore/peer-deps": "0.5.0-dev.0",
32
- "@livestore/utils": "0.5.0-dev.0",
33
- "effect": "4.0.0-rc.111"
41
+ "@effect/ai-openai": "4.0.3",
42
+ "@effect/opentelemetry": "4.0.3",
43
+ "@livestore/adapter-node": "0.5.0",
44
+ "@livestore/common": "0.5.0",
45
+ "@livestore/livestore": "0.5.0",
46
+ "@livestore/peer-deps": "0.5.0",
47
+ "@livestore/utils": "0.5.0",
48
+ "effect": "4.0.3"
34
49
  },
35
50
  "devDependencies": {
36
- "@livestore/utils-dev": "0.5.0-dev.0",
51
+ "@livestore/utils-dev": "0.5.0",
37
52
  "@types/node": "25.3.3",
38
- "typescript": "6.0.3",
53
+ "typescript": "7.0.2",
39
54
  "vitest": "4.1.9"
40
55
  },
41
56
  "peerDependencies": {
42
- "@effect/opentelemetry": "^4.0.0-rc.111",
43
- "@effect/platform-browser": "^4.0.0-rc.111",
44
- "@effect/platform-bun": "^4.0.0-rc.111",
45
- "@effect/platform-node": "^4.0.0-rc.111",
46
- "@effect/platform-node-shared": "^4.0.0-rc.111",
47
- "@effect/vitest": "^4.0.0-rc.111",
57
+ "@effect/opentelemetry": "^4.0.3",
58
+ "@effect/platform-browser": "^4.0.3",
59
+ "@effect/platform-bun": "^4.0.3",
60
+ "@effect/platform-node": "^4.0.3",
61
+ "@effect/platform-node-shared": "^4.0.3",
62
+ "@effect/vitest": "^4.0.3",
48
63
  "@opentelemetry/api": "^1.9.0",
49
64
  "@opentelemetry/resources": "^2.2.0",
50
65
  "@standard-schema/spec": "^1.1.0",
51
- "effect": "^4.0.0-rc.111"
66
+ "effect": "^4.0.3"
52
67
  },
53
- "$genie": {
54
- "source": "package.json.genie.ts",
55
- "warning": "DO NOT EDIT - changes will be overwritten",
56
- "workspaceClosureDirs": [
57
- "packages/@livestore/adapter-node",
58
- "packages/@livestore/cli",
59
- "repos/livestore/packages/@livestore/adapter-web",
60
- "repos/livestore/packages/@livestore/common",
61
- "repos/livestore/packages/@livestore/common-cf",
62
- "repos/livestore/packages/@livestore/livestore",
63
- "repos/livestore/packages/@livestore/peer-deps",
64
- "repos/livestore/packages/@livestore/sqlite-wasm",
65
- "repos/livestore/packages/@livestore/utils",
66
- "repos/livestore/packages/@livestore/utils-dev",
67
- "repos/livestore/packages/@livestore/wa-sqlite",
68
- "repos/livestore/packages/@livestore/webmesh"
69
- ]
68
+ "publishConfig": {
69
+ "access": "public"
70
70
  },
71
71
  "scripts": {
72
72
  "build": "tsc",
@@ -0,0 +1 @@
1
+ .tmp-test-configs/
@@ -4,7 +4,7 @@ import { fileURLToPath, pathToFileURL } from 'node:url'
4
4
  import { Events, makeSchema, State } from '@livestore/common/schema'
5
5
  import type { MockSyncBackend } from '@livestore/common/sync'
6
6
  import { EventFactory } from '@livestore/common/testing'
7
- import { Effect, FileSystem, type Queue, Schema } from '@livestore/utils/effect'
7
+ import { Effect, FileSystem, type Queue, Schema, type SubscriptionRef } from '@livestore/utils/effect'
8
8
 
9
9
  const __dirname = path.dirname(fileURLToPath(import.meta.url))
10
10
 
@@ -49,10 +49,11 @@ const makeTempConfig = () => {
49
49
  const moduleSource = `
50
50
  import { schema } from ${JSON.stringify(schemaModuleUrl)}
51
51
  import { makeMockSyncBackend } from '@livestore/common/sync'
52
- import { Effect, Queue } from '@livestore/utils/effect'
52
+ import { Effect, Queue, SubscriptionRef } from '@livestore/utils/effect'
53
53
 
54
54
  export const mockBackend = await Effect.runPromise(Effect.scoped(makeMockSyncBackend({ startConnected: true })))
55
55
  export const connectionEvents = await Effect.runPromise(Queue.make<'connect' | 'disconnect'>())
56
+ export const disconnectShouldFail = await Effect.runPromise(SubscriptionRef.make(false))
56
57
 
57
58
  export { schema }
58
59
 
@@ -60,7 +61,11 @@ export const syncBackend = (_args) =>
60
61
  mockBackend.makeSyncBackend.pipe(
61
62
  Effect.tap(() => Queue.offer(connectionEvents, 'connect')),
62
63
  Effect.map((backend) => {
63
- const disconnect = backend.disconnect ?? Effect.void
64
+ const disconnect = SubscriptionRef.get(disconnectShouldFail).pipe(
65
+ Effect.flatMap((shouldFail) =>
66
+ shouldFail ? Effect.fail(new Error('disconnect failed')) : (backend.disconnect ?? Effect.void),
67
+ ),
68
+ )
64
69
  return {
65
70
  ...backend,
66
71
  disconnect: disconnect.pipe(Effect.tap(() => Queue.offer(connectionEvents, 'disconnect'))),
@@ -93,14 +98,20 @@ export const useMockConfig = Effect.acquireRelease(
93
98
  })) as {
94
99
  mockBackend: MockSyncBackend
95
100
  connectionEvents: Queue.Queue<'connect' | 'disconnect'>
101
+ disconnectShouldFail: SubscriptionRef.SubscriptionRef<boolean>
96
102
  }
97
103
 
98
- return { configPath: tempPath, mockBackend: mod.mockBackend, connectionEvents: mod.connectionEvents }
104
+ return {
105
+ configPath: tempPath,
106
+ mockBackend: mod.mockBackend,
107
+ connectionEvents: mod.connectionEvents,
108
+ disconnectShouldFail: mod.disconnectShouldFail,
109
+ }
99
110
  }),
100
111
  ({ configPath }) =>
101
112
  Effect.gen(function* () {
102
113
  const fs = yield* FileSystem.FileSystem
103
- yield* fs.remove(configPath, { recursive: false }).pipe(Effect.catch(() => Effect.void))
114
+ yield* fs.remove(configPath, { recursive: false }).pipe(Effect.ignore)
104
115
  }),
105
116
  )
106
117
 
@@ -1,4 +1,4 @@
1
- import { Effect, FetchHttpClient, Layer, Queue, Stream } from '@livestore/utils/effect'
1
+ import { Effect, FetchHttpClient, Layer, type Queue, Stream, SubscriptionRef } from '@livestore/utils/effect'
2
2
  import { PlatformNode } from '@livestore/utils/node'
3
3
  import { Vitest } from '@livestore/utils-dev/node-vitest'
4
4
  import { expect } from 'vitest'
@@ -46,6 +46,21 @@ Vitest.describe('sync-operations', { timeout: 10_000 }, () => {
46
46
  }).pipe(withTestCtx(test)),
47
47
  )
48
48
 
49
+ Vitest.live('preserves a successful export when backend disconnect fails', (test: Vitest.TestContext) =>
50
+ Effect.gen(function* () {
51
+ const { configPath, disconnectShouldFail } = yield* useMockConfig
52
+ yield* SubscriptionRef.set(disconnectShouldFail, true)
53
+
54
+ const result = yield* pullEventsFromSyncBackend({
55
+ configPath,
56
+ storeId,
57
+ clientId,
58
+ })
59
+
60
+ expect(result.eventCount).toBe(0)
61
+ }).pipe(withTestCtx(test)),
62
+ )
63
+
49
64
  Vitest.live('fails import when backend is not empty', (test: Vitest.TestContext) =>
50
65
  Effect.gen(function* () {
51
66
  const { mockBackend, connectionEvents, configPath } = yield* useMockConfig
package/src/bin.ts CHANGED
File without changes
package/src/cli.ts CHANGED
@@ -5,5 +5,5 @@ import { mcpCommand } from './commands/mcp.ts'
5
5
  import { createCommand } from './commands/new-project.ts'
6
6
 
7
7
  export const command = Cli.Command.make('livestore', {
8
- verbose: Cli.Flag.boolean('verbose').pipe(Cli.Flag.withDefault(false)),
8
+ verbose: Cli.Flag.Boolean('verbose').pipe(Cli.Flag.withDefault(false)),
9
9
  }).pipe(Cli.Command.withSubcommands([mcpCommand, createCommand, syncCommand]))
@@ -1,4 +1,4 @@
1
- import { McpServer } from 'effect/unstable/ai'
1
+ import { McpServer } from 'effect/ai'
2
2
 
3
3
  import { Effect, Layer } from '@livestore/utils/effect'
4
4
  import { PlatformNode } from '@livestore/utils/node'
@@ -171,19 +171,19 @@ const importEvents = ({
171
171
  export const exportCommand = Cli.Command.make(
172
172
  'export',
173
173
  {
174
- config: Cli.Flag.string('config').pipe(
174
+ config: Cli.Flag.String('config').pipe(
175
175
  Cli.Flag.withAlias('c'),
176
176
  Cli.Flag.withDescription('Path to the config module that exports schema and syncBackend'),
177
177
  ),
178
- storeId: Cli.Flag.string('store-id').pipe(
178
+ storeId: Cli.Flag.String('store-id').pipe(
179
179
  Cli.Flag.withAlias('i'),
180
180
  Cli.Flag.withDescription('Store identifier'),
181
181
  ),
182
- clientId: Cli.Flag.string('client-id').pipe(
182
+ clientId: Cli.Flag.String('client-id').pipe(
183
183
  Cli.Flag.withDefault('cli-export'),
184
184
  Cli.Flag.withDescription('Client identifier for the sync connection'),
185
185
  ),
186
- output: Cli.Argument.string('file').pipe(Cli.Argument.withDescription('Output JSON file path')),
186
+ output: Cli.Argument.String('file').pipe(Cli.Argument.withDescription('Output JSON file path')),
187
187
  },
188
188
  Effect.fn(function* ({
189
189
  config,
@@ -218,28 +218,28 @@ export const exportCommand = Cli.Command.make(
218
218
  export const importCommand = Cli.Command.make(
219
219
  'import',
220
220
  {
221
- config: Cli.Flag.string('config').pipe(
221
+ config: Cli.Flag.String('config').pipe(
222
222
  Cli.Flag.withAlias('c'),
223
223
  Cli.Flag.withDescription('Path to the config module that exports schema and syncBackend'),
224
224
  ),
225
- storeId: Cli.Flag.string('store-id').pipe(
225
+ storeId: Cli.Flag.String('store-id').pipe(
226
226
  Cli.Flag.withAlias('i'),
227
227
  Cli.Flag.withDescription('Store identifier'),
228
228
  ),
229
- clientId: Cli.Flag.string('client-id').pipe(
229
+ clientId: Cli.Flag.String('client-id').pipe(
230
230
  Cli.Flag.withDefault('cli-import'),
231
231
  Cli.Flag.withDescription('Client identifier for the sync connection'),
232
232
  ),
233
- force: Cli.Flag.boolean('force').pipe(
233
+ force: Cli.Flag.Boolean('force').pipe(
234
234
  Cli.Flag.withAlias('f'),
235
235
  Cli.Flag.withDefault(false),
236
236
  Cli.Flag.withDescription('Force import even if store ID does not match'),
237
237
  ),
238
- dryRun: Cli.Flag.boolean('dry-run').pipe(
238
+ dryRun: Cli.Flag.Boolean('dry-run').pipe(
239
239
  Cli.Flag.withDefault(false),
240
240
  Cli.Flag.withDescription('Validate the import file without actually importing'),
241
241
  ),
242
- input: Cli.Argument.string('file').pipe(Cli.Argument.withDescription('Input JSON file to import')),
242
+ input: Cli.Argument.String('file').pipe(Cli.Argument.withDescription('Input JSON file to import')),
243
243
  },
244
244
  Effect.fn(function* ({
245
245
  config,
@@ -1,4 +1,4 @@
1
- import { AiError, LanguageModel, Prompt, Tool } from 'effect/unstable/ai'
1
+ import { AiError, LanguageModel, Prompt, Tool } from 'effect/ai'
2
2
  import { OpenAiClient, OpenAiLanguageModel } from '@effect/ai-openai'
3
3
 
4
4
  import { Config, Effect, FetchHttpClient, Layer, Schema } from '@livestore/utils/effect'
@@ -22,7 +22,7 @@ export const coachTool = Tool.make('livestore_coach', {
22
22
  description: 'AI-generated feedback and recommendations for the code',
23
23
  }),
24
24
  score: Schema.optional(
25
- Schema.Number.annotate({
25
+ Schema.Finite.annotate({
26
26
  description: 'Code quality score from 1-10 (optional)',
27
27
  }),
28
28
  ),
@@ -39,7 +39,7 @@ export const coachTool = Tool.make('livestore_coach', {
39
39
 
40
40
  /** Composed layer: HttpClient → OpenAiClient → LanguageModel */
41
41
  const LanguageModelLayer = OpenAiLanguageModel.layer({ model: 'gpt-4o-mini' }).pipe(
42
- Layer.provide(OpenAiClient.layerConfig({ apiKey: Config.redacted('OPENAI_API_KEY') })),
42
+ Layer.provide(OpenAiClient.layerConfig({ apiKey: Config.Redacted('OPENAI_API_KEY') })),
43
43
  Layer.provide(FetchHttpClient.layer),
44
44
  )
45
45
 
@@ -1,4 +1,4 @@
1
- import { McpProtocol } from 'effect/unstable/ai'
1
+ import { McpProtocol } from 'effect/ai'
2
2
 
3
3
  /**
4
4
  * MCP stdio selects an exact offered version when available and otherwise
@@ -1,4 +1,4 @@
1
- import { type Toolkit } from 'effect/unstable/ai'
1
+ import { type Toolkit } from 'effect/ai'
2
2
 
3
3
  import { Effect, FetchHttpClient, Layer } from '@livestore/utils/effect'
4
4
  import { PlatformNode } from '@livestore/utils/node'
@@ -1,4 +1,4 @@
1
- import { Tool } from 'effect/unstable/ai'
1
+ import { Tool } from 'effect/ai'
2
2
 
3
3
  import { Vitest } from '@livestore/utils-dev/node-vitest'
4
4
 
@@ -1,4 +1,4 @@
1
- import { Tool, Toolkit } from 'effect/unstable/ai'
1
+ import { Tool, Toolkit } from 'effect/ai'
2
2
 
3
3
  import { Schema } from '@livestore/utils/effect'
4
4
 
@@ -150,7 +150,7 @@ Returns on success:
150
150
  }),
151
151
  success: Schema.Struct({
152
152
  rows: Schema.Array(Schema.Record(Schema.String, Schema.Json)),
153
- rowCount: Schema.Number,
153
+ rowCount: Schema.Finite,
154
154
  }),
155
155
  }).annotate(Tool.Destructive, false),
156
156
 
@@ -188,7 +188,7 @@ Returns on success:
188
188
  }),
189
189
  ),
190
190
  }),
191
- success: Schema.Struct({ committed: Schema.Number }),
191
+ success: Schema.Struct({ committed: Schema.Finite }),
192
192
  }).annotate(Tool.Destructive, true),
193
193
 
194
194
  Tool.make('livestore_instance_status', {
@@ -213,7 +213,7 @@ Returns when not connected:
213
213
  storeId: Schema.String,
214
214
  clientId: Schema.String,
215
215
  sessionId: Schema.String,
216
- tableCounts: Schema.Record(Schema.String, Schema.Number).annotate({
216
+ tableCounts: Schema.Record(Schema.String, Schema.Finite).annotate({
217
217
  description: 'Tables in the LiveStore instance with their row count',
218
218
  }),
219
219
  }),
@@ -264,7 +264,7 @@ Returns on success:
264
264
  }),
265
265
  success: Schema.Struct({
266
266
  storeId: Schema.String,
267
- eventCount: Schema.Number,
267
+ eventCount: Schema.Finite,
268
268
  exportedAt: Schema.String,
269
269
  data: Schema.Json.annotate({ description: 'The export file data (can be saved or passed to import)' }),
270
270
  }),
@@ -313,7 +313,7 @@ Returns on success:
313
313
  }),
314
314
  success: Schema.Struct({
315
315
  storeId: Schema.String,
316
- eventCount: Schema.Number,
316
+ eventCount: Schema.Finite,
317
317
  dryRun: Schema.Boolean,
318
318
  }),
319
319
  }).annotate(Tool.Destructive, true),
@@ -1,4 +1,4 @@
1
- import { McpServer } from 'effect/unstable/ai'
1
+ import { McpProtocol, McpServer } from 'effect/ai'
2
2
 
3
3
  import { Effect, Layer, Logger } from '@livestore/utils/effect'
4
4
  import { Cli, PlatformNode } from '@livestore/utils/node'
@@ -91,7 +91,9 @@ const mcpServerCommand = Cli.Command.make(
91
91
  }).pipe(
92
92
  Layer.provide(LivestoreResources),
93
93
  Layer.provide(LivestoreTools),
94
- Layer.provide(Logger.layer([Logger.consolePretty({ stderr: true })])),
94
+ Layer.provide(
95
+ Layer.merge(Logger.layer([Logger.consolePretty()]), Layer.succeed(Logger.LogToStderr, true)),
96
+ ),
95
97
  Layer.provide(PlatformNode.NodeStdio.layer),
96
98
  Layer.launch,
97
99
  )
@@ -87,7 +87,7 @@ const fetchExamples = (ref: string) =>
87
87
 
88
88
  const responseText = yield* response.text
89
89
 
90
- const examples = yield* Schema.decodeUnknownEffect(Schema.fromJsonString(GitHubContentsResponseSchema))(
90
+ const examples = yield* Schema.decodeEffect(Schema.fromJsonString(GitHubContentsResponseSchema))(
91
91
  responseText,
92
92
  ).pipe(
93
93
  Effect.catch(
@@ -116,7 +116,7 @@ const selectExample = (examples: string[]) =>
116
116
  return yield* new NoExamplesError({ message: 'No examples available' })
117
117
  }
118
118
 
119
- const prompt = Cli.Prompt.select({
119
+ const prompt = Cli.Prompt.Select({
120
120
  message: '📦 Select a LiveStore example to create:',
121
121
  choices: examples.map((example) => ({
122
122
  title: example,
@@ -213,10 +213,10 @@ const downloadExample = (exampleName: string, ref: string, destinationPath: stri
213
213
  )
214
214
 
215
215
  // Clean up extract directory
216
- yield* fs.remove(extractDir, { recursive: true }).pipe(Effect.catch(() => Effect.void))
216
+ yield* fs.remove(extractDir, { recursive: true }).pipe(Effect.ignore)
217
217
 
218
218
  // Clean up tarball
219
- yield* fs.remove(tarballPath).pipe(Effect.catch(() => Effect.void))
219
+ yield* fs.remove(tarballPath).pipe(Effect.ignore)
220
220
 
221
221
  yield* Console.log(`✅ Example "${exampleName}" created successfully at: ${destinationPath}`)
222
222
  })
@@ -224,11 +224,11 @@ const downloadExample = (exampleName: string, ref: string, destinationPath: stri
224
224
  export const createCommand = Cli.Command.make(
225
225
  'create',
226
226
  {
227
- example: Cli.Flag.string('example').pipe(
227
+ example: Cli.Flag.String('example').pipe(
228
228
  Cli.Flag.optional,
229
229
  Cli.Flag.withDescription('Example name to create (bypasses interactive selection)'),
230
230
  ),
231
- ref: Cli.Flag.string('ref').pipe(
231
+ ref: Cli.Flag.String('ref').pipe(
232
232
  Cli.Flag.withAlias('commit'),
233
233
  Cli.Flag.withAlias('branch'),
234
234
  Cli.Flag.withAlias('tag'),
@@ -237,7 +237,7 @@ export const createCommand = Cli.Command.make(
237
237
  'The name of the commit/branch/tag to fetch examples from. Pull requests refs must be fully-formed (e.g., `refs/pull/123/merge`).',
238
238
  ),
239
239
  ),
240
- path: Cli.Argument.string('path').pipe(
240
+ path: Cli.Argument.String('path').pipe(
241
241
  Cli.Argument.optional,
242
242
  Cli.Argument.withDescription('Destination path for the new project'),
243
243
  ),
@@ -292,7 +292,7 @@ export const createCommand = Cli.Command.make(
292
292
  const fs = yield* FileSystem.FileSystem
293
293
  const packageJsonPath = nodePath.join(destinationPath, 'package.json')
294
294
  const packageJsonContent = yield* fs.readFileString(packageJsonPath)
295
- const runScript = yield* Schema.decodeUnknownEffect(Schema.fromJsonString(PackageJsonScriptsSchema))(
295
+ const runScript = yield* Schema.decodeEffect(Schema.fromJsonString(PackageJsonScriptsSchema))(
296
296
  packageJsonContent,
297
297
  ).pipe(
298
298
  Effect.map((pkg) => ('dev' in pkg.scripts ? ('dev' as const) : ('start' as const))),
@@ -79,11 +79,10 @@ export const loadModuleConfig = ({
79
79
  )
80
80
 
81
81
  const syncPayloadExport = mod?.syncPayload
82
- const syncPayload = yield* (
82
+ const syncPayload =
83
83
  syncPayloadExport === undefined
84
- ? Effect.succeed<Schema.Json | undefined>(undefined)
85
- : Schema.decodeUnknownEffect(syncPayloadSchema)(syncPayloadExport)
86
- ).pipe(UnknownError.mapToUnknownError)
84
+ ? undefined
85
+ : yield* Schema.decodeUnknownEffect(syncPayloadSchema)(syncPayloadExport).pipe(UnknownError.mapToUnknownError)
87
86
 
88
87
  return {
89
88
  schema,
@@ -35,7 +35,7 @@ export const ExportFileSchema = Schema.Struct({
35
35
  /** ISO timestamp of when the export was created */
36
36
  exportedAt: Schema.String,
37
37
  /** Total number of events in the export */
38
- eventCount: Schema.Number,
38
+ eventCount: Schema.Finite,
39
39
  /** Array of events in global encoded format */
40
40
  events: Schema.Array(LiveStoreEvent.Global.Encoded),
41
41
  })
@@ -125,10 +125,17 @@ export const makeSyncBackend = ({
125
125
  return syncBackend
126
126
  })
127
127
 
128
+ type SyncBackendWithOptionalDisconnect = SyncBackend.SyncBackend & {
129
+ readonly disconnect?: Effect.Effect<void, UnknownError>
130
+ }
131
+
128
132
  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))
133
+ const maybeDisconnect = (syncBackend as SyncBackendWithOptionalDisconnect).disconnect
134
+ const releaseEffect =
135
+ maybeDisconnect !== undefined && Effect.isEffect(maybeDisconnect) === true
136
+ ? maybeDisconnect
137
+ : SubscriptionRef.set(syncBackend.isConnected, false)
138
+ return releaseEffect.pipe(Effect.ignore)
132
139
  }
133
140
 
134
141
  export interface ExportResult {