@sanity/cli 3.88.0 → 3.88.1-typegen-experimental.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 (37) hide show
  1. package/lib/_chunks-cjs/cli.js +58748 -56759
  2. package/lib/_chunks-cjs/cli.js.map +1 -1
  3. package/lib/_chunks-cjs/generateAction.js +113 -111
  4. package/lib/_chunks-cjs/generateAction.js.map +1 -1
  5. package/lib/_chunks-cjs/loadEnv.js +3 -3
  6. package/lib/_chunks-cjs/loadEnv.js.map +1 -1
  7. package/lib/_chunks-cjs/workerChannel.js +84 -0
  8. package/lib/_chunks-cjs/workerChannel.js.map +1 -0
  9. package/lib/workers/typegenGenerate.d.ts +144 -33
  10. package/lib/workers/typegenGenerate.js +83 -112
  11. package/lib/workers/typegenGenerate.js.map +1 -1
  12. package/package.json +19 -21
  13. package/src/actions/typegen/generate.telemetry.ts +9 -3
  14. package/src/actions/typegen/generateAction.ts +159 -152
  15. package/src/cli.ts +0 -0
  16. package/src/commands/blueprints/addBlueprintsCommand.ts +52 -56
  17. package/src/commands/blueprints/blueprintsGroup.ts +0 -1
  18. package/src/commands/blueprints/configBlueprintsCommand.ts +50 -74
  19. package/src/commands/blueprints/deployBlueprintsCommand.ts +41 -133
  20. package/src/commands/blueprints/destroyBlueprintsCommand.ts +76 -0
  21. package/src/commands/blueprints/infoBlueprintsCommand.ts +29 -51
  22. package/src/commands/blueprints/initBlueprintsCommand.ts +55 -73
  23. package/src/commands/blueprints/logsBlueprintsCommand.ts +43 -81
  24. package/src/commands/blueprints/planBlueprintsCommand.ts +26 -36
  25. package/src/commands/blueprints/stacksBlueprintsCommand.ts +43 -51
  26. package/src/commands/functions/devFunctionsCommand.ts +1 -2
  27. package/src/commands/functions/envFunctionsCommand.ts +55 -46
  28. package/src/commands/functions/functionsGroup.ts +1 -2
  29. package/src/commands/functions/logsFunctionsCommand.ts +101 -58
  30. package/src/commands/functions/testFunctionsCommand.ts +56 -36
  31. package/src/commands/index.ts +6 -4
  32. package/src/commands/projects/listProjectsCommand.ts +0 -0
  33. package/src/commands/projects/projectsGroup.ts +0 -0
  34. package/src/util/__tests__/workerChannel.test.ts +222 -0
  35. package/src/util/workerChannel.ts +312 -0
  36. package/src/workers/typegenGenerate.ts +181 -183
  37. package/templates/app-sanity-ui/src/ExampleComponent.tsx +1 -1
@@ -1,44 +1,49 @@
1
- import {type types} from '@sanity/runtime-cli/utils'
1
+ import chalk from 'chalk'
2
+ import inquirer from 'inquirer'
2
3
 
3
4
  import {type CliCommandDefinition} from '../../types'
4
5
 
5
- type StackFunctionResource = types.StackFunctionResource
6
-
7
6
  const helpText = `
7
+ Arguments
8
+ <name> The name of the Function to retrieve logs for
9
+
8
10
  Options
9
- --name <name> The name of the function to retrieve logs for
10
11
  --limit <limit> The number of log entries to retrieve [default 50]
11
12
  --json If set return json
12
13
  --utc Use UTC dates in logs
13
14
 
14
15
  Examples
15
- # Retrieve logs for Sanity Function abcd1234
16
- sanity functions logs --name echo
16
+ # Retrieve logs for Sanity Function
17
+ sanity functions logs echo
17
18
 
18
- # Retrieve the last two log entries for Sanity Function abcd1234
19
- sanity functions logs --name echo --limit 2
19
+ # Retrieve the last two log entries for Sanity Function
20
+ sanity functions logs echo --limit 2
20
21
 
21
- # Retrieve logs for Sanity Function abcd1234 in json format
22
+ # Retrieve logs for Sanity Function in json format
22
23
  sanity functions logs --name echo --json
24
+
25
+ # Delete all logs for Sanity Function
26
+ sanity functions logs --name echo --delete
23
27
  `
24
28
 
25
29
  const defaultFlags = {
26
- name: '',
27
30
  limit: 50,
28
31
  json: false,
29
32
  utc: false,
33
+ delete: false,
34
+ force: false,
30
35
  }
31
36
 
32
37
  const logsFunctionsCommand: CliCommandDefinition = {
33
38
  name: 'logs',
34
39
  group: 'functions',
35
40
  helpText,
36
- signature: '',
37
- description: 'Retrieve logs for a Sanity Function',
38
- hideFromHelp: true,
41
+ signature: '<name> [--limit <number>] [--json] [--utc] [--delete [--force]]',
42
+ description: 'Retrieve or delete logs for a Sanity Function',
39
43
  async action(args, context) {
40
44
  const {apiClient, output} = context
41
- const {print} = output
45
+ const {print, error: printError} = output
46
+ const [name] = args.argsWithoutOptions
42
47
  const flags = {...defaultFlags, ...args.extOptions}
43
48
 
44
49
  const client = apiClient({
@@ -46,69 +51,97 @@ const logsFunctionsCommand: CliCommandDefinition = {
46
51
  requireProject: false,
47
52
  })
48
53
 
49
- if (flags.name === '') {
50
- throw new Error('You must provide a function name via the --name flag')
54
+ if (!name) {
55
+ throw new Error('You must provide a function name as the first argument')
51
56
  }
52
57
 
53
58
  const token = client.config().token
54
- const {blueprint} = await import('@sanity/runtime-cli/actions/blueprints')
59
+ if (!token) throw new Error('No API token found. Please run `sanity login`.')
60
+
61
+ const {getBlueprintAndStack} = await import('@sanity/runtime-cli/actions/blueprints')
55
62
  const {findFunction} = await import('@sanity/runtime-cli/utils')
56
63
 
57
- const {deployedStack} = await blueprint.readBlueprintOnDisk({
58
- getStack: true,
59
- token,
60
- })
64
+ const {deployedStack} = await getBlueprintAndStack({token})
61
65
 
62
66
  if (!deployedStack) {
63
67
  throw new Error('Stack not found')
64
68
  }
65
69
 
66
- const blueprintConfig = blueprint.readConfigFile()
67
- const projectId = blueprintConfig?.projectId
70
+ const {projectId} = deployedStack
68
71
 
69
- const {externalId} = findFunction.findFunctionByName(
70
- deployedStack,
71
- flags.name,
72
- ) as StackFunctionResource
72
+ const {externalId} = findFunction.findFunctionByName(deployedStack, name)
73
73
 
74
74
  if (token && projectId) {
75
75
  const {logs: logsAction} = await import('@sanity/runtime-cli/actions/functions')
76
- const {ok, error, logs, total} = await logsAction.logs(
77
- externalId,
78
- {limit: flags.limit},
79
- {token, projectId},
80
- )
81
-
82
- if (!ok) {
83
- print(`Error: ${error || 'Unknown error'}`)
84
- return
85
- }
86
76
 
87
- const filteredLogs = logs.filter(
88
- (entry: {level: string; message: string}) => entry.level && entry.message,
89
- )
77
+ if (!externalId) throw new Error('Unable to delete logs. Unable to determine function ID.')
78
+
79
+ if (flags.delete) {
80
+ if (!flags.force) {
81
+ const {certain} = await inquirer.prompt({
82
+ type: 'confirm',
83
+ name: 'certain',
84
+ message: `Are you sure you want to delete ${chalk.bold('all')} logs for function ${chalk.yellow(name)}?`,
85
+ default: false,
86
+ })
87
+ if (!certain) return
88
+ }
90
89
 
91
- if (filteredLogs.length === 0) {
92
- print(`No logs found for function ${flags.name}`)
93
- return
94
- }
90
+ print(`Deleting logs for function ${chalk.yellow(name)}`)
91
+ const {ok, error} = await logsAction.deleteLogs(externalId, {token, projectId})
95
92
 
96
- if (flags.json) {
97
- print(JSON.stringify(filteredLogs, null, 2))
93
+ if (!ok) {
94
+ printError(`${chalk.red('Failed')} to retrieve logs`)
95
+ printError(`Error: ${error || 'Unknown error'}`)
96
+ return
97
+ }
98
+
99
+ print('Logs deleted')
98
100
  } else {
99
- print(`Found ${total} log entries for function ${flags.name}`)
100
- if (logs.length < total) {
101
- print(`Here are the last ${filteredLogs.length} entries`)
101
+ print(`Finding logs for function "${name}"`)
102
+
103
+ const {ok, error, logs, total} = await logsAction.logs(
104
+ externalId,
105
+ {limit: flags.limit},
106
+ {token, projectId},
107
+ )
108
+
109
+ if (!ok) {
110
+ printError(`${chalk.red('Failed')} to retrieve logs`)
111
+ printError(`Error: ${error || 'Unknown error'}`)
112
+ return
113
+ }
114
+
115
+ const filteredLogs = logs.filter(
116
+ (entry: {level: string; message: string}) => entry.level && entry.message,
117
+ )
118
+
119
+ if (filteredLogs.length === 0) {
120
+ print(`No logs found for function ${name}`)
121
+ return
102
122
  }
103
- print('\n')
104
-
105
- for (const log of filteredLogs) {
106
- const {time, level, message} = log
107
- const date = new Date(time)
108
- const dateString = flags.utc
109
- ? date.toISOString().slice(0, 19).split('T').join(' ')
110
- : `${date.toLocaleDateString()} ${date.toLocaleTimeString()}`
111
- print(`${dateString} ${level} ${message}`)
123
+
124
+ if (flags.json) {
125
+ print(JSON.stringify(filteredLogs, null, 2))
126
+ } else {
127
+ print(`Found ${total} log entries for function ${name}`)
128
+ if (logs.length < total) {
129
+ print(`Here are the last ${filteredLogs.length} entries`)
130
+ }
131
+ print('\n')
132
+
133
+ for (const log of filteredLogs) {
134
+ const {time, level, message} = log
135
+ const date = new Date(time)
136
+ const [dateString, timeString] = flags.utc
137
+ ? date.toISOString().slice(0, 19).split('T')
138
+ : [date.toLocaleDateString(), date.toLocaleTimeString()]
139
+ print(
140
+ [chalk.bold(dateString), chalk.bold.blue(timeString), logLevel(level), message].join(
141
+ ' ',
142
+ ),
143
+ )
144
+ }
112
145
  }
113
146
  }
114
147
  } else {
@@ -117,4 +150,14 @@ const logsFunctionsCommand: CliCommandDefinition = {
117
150
  },
118
151
  }
119
152
 
153
+ function logLevel(level: string) {
154
+ if (level === 'ERROR') {
155
+ return chalk.red(level)
156
+ }
157
+ if (level === 'WARN') {
158
+ return chalk.yellow(level)
159
+ }
160
+ return chalk.green(level)
161
+ }
162
+
120
163
  export default logsFunctionsCommand
@@ -1,76 +1,96 @@
1
1
  import {type CliCommandDefinition} from '../../types'
2
2
 
3
3
  const helpText = `
4
+ Arguments
5
+ <name> The name of the Sanity Function
6
+
4
7
  Options
5
8
  --data <data> Data to send to the function
6
9
  --file <file> Read data from file and send to the function
7
- --name <name> The name of your Sanity Function
8
10
  --timeout <timeout> Execution timeout value in seconds
11
+ --api <version> Sanity API Version to use
12
+ --dataset <dataset> The Sanity dataset to use
13
+ --project-id <id> Sanity Project ID to use
14
+
9
15
 
10
16
  Examples
11
17
  # Test function passing event data on command line
12
- sanity functions test --name echo --data '{ "id": 1 }'
18
+ sanity functions test echo --data '{ "id": 1 }'
13
19
 
14
20
  # Test function passing event data via a file
15
- sanity functions test -name echo --file 'payload.json'
21
+ sanity functions test echo --file 'payload.json'
16
22
 
17
23
  # Test function passing event data on command line and cap execution time to 60 seconds
18
- sanity functions test -name echo --data '{ "id": 1 }' --timeout 60
24
+ sanity functions test echo --data '{ "id": 1 }' --timeout 60
19
25
  `
20
26
 
21
27
  const defaultFlags = {
22
- data: undefined,
23
- file: undefined,
24
- name: '',
25
- timeout: 5, // seconds
28
+ 'data': undefined,
29
+ 'file': undefined,
30
+ 'timeout': 10, // seconds
31
+ 'api': undefined,
32
+ 'dataset': undefined,
33
+ 'project-id': undefined,
34
+ 'project': undefined,
35
+ 'projectId': undefined,
26
36
  }
27
37
 
28
38
  const testFunctionsCommand: CliCommandDefinition = {
29
39
  name: 'test',
30
40
  group: 'functions',
31
41
  helpText,
32
- signature: '',
42
+ signature:
43
+ '<name> [--data <json>] [--file <filename>] [--timeout <seconds>] [--api <version>] [--dataset <name>] [--project-id] <id>]',
33
44
  description: 'Invoke a local Sanity Function',
34
- hideFromHelp: true,
35
45
  async action(args, context) {
36
46
  const {output} = context
37
- const {print} = output
47
+ const {print, error: printError} = output
48
+ const [name] = args.argsWithoutOptions
38
49
  const flags = {...defaultFlags, ...args.extOptions}
39
50
 
40
- if (flags.name === '') {
41
- throw new Error('You must provide a function name via the --name flag')
51
+ if (!name) {
52
+ throw new Error('You must provide a function name as the first argument')
42
53
  }
43
54
 
44
55
  const {test} = await import('@sanity/runtime-cli/actions/functions')
45
56
  const {blueprint} = await import('@sanity/runtime-cli/actions/blueprints')
46
57
  const {findFunction} = await import('@sanity/runtime-cli/utils')
47
58
 
48
- const {parsedBlueprint} = await blueprint.readBlueprintOnDisk({
49
- getStack: false,
50
- })
59
+ const {parsedBlueprint} = await blueprint.readLocalBlueprint()
51
60
 
52
- const src = findFunction.getFunctionSource(parsedBlueprint, flags.name)
53
- if (!src) {
54
- throw new Error(`Error: Function ${flags.name} has no source code`)
55
- }
61
+ try {
62
+ const fn = findFunction.findFunctionByName(parsedBlueprint, name)
63
+ if (!fn) {
64
+ throw new Error(`Function ${name} has no source code`)
65
+ }
56
66
 
57
- const {json, logs, error} = await test.testAction(
58
- src,
59
- {
60
- data: flags.data,
61
- file: flags.file,
62
- timeout: flags.timeout,
63
- },
64
- {}, // @TODO: Add context
65
- )
67
+ const projectId = flags['project-id'] ?? flags.projectId ?? flags.project
68
+ const {json, logs, error} = await test.testAction(
69
+ fn,
70
+ {
71
+ data: flags.data,
72
+ file: flags.file,
73
+ timeout: flags.timeout,
74
+ },
75
+ {
76
+ clientOptions: {
77
+ apiVersion: flags.api,
78
+ dataset: flags.dataset,
79
+ projectId: projectId,
80
+ },
81
+ },
82
+ )
66
83
 
67
- if (error) {
68
- print(error.toString())
69
- } else {
70
- print('Logs:')
71
- print(logs)
72
- print('Response:')
73
- print(JSON.stringify(json, null, 2))
84
+ if (error) {
85
+ print(error.toString())
86
+ } else {
87
+ print('Logs:')
88
+ print(logs)
89
+ print('Response:')
90
+ print(JSON.stringify(json, null, 2))
91
+ }
92
+ } catch (error) {
93
+ printError(`Error: ${error || 'Unknown error'}`)
74
94
  }
75
95
  },
76
96
  }
@@ -3,6 +3,7 @@ import addBlueprintsCommand from './blueprints/addBlueprintsCommand'
3
3
  import blueprintsGroup from './blueprints/blueprintsGroup'
4
4
  import configBlueprintsCommand from './blueprints/configBlueprintsCommand'
5
5
  import deployBlueprintsCommand from './blueprints/deployBlueprintsCommand'
6
+ import destroyBlueprintsCommand from './blueprints/destroyBlueprintsCommand'
6
7
  import infoBlueprintsCommand from './blueprints/infoBlueprintsCommand'
7
8
  import initBlueprintsCommand from './blueprints/initBlueprintsCommand'
8
9
  import logsBlueprintsCommand from './blueprints/logsBlueprintsCommand'
@@ -59,12 +60,13 @@ export const baseCommands: (CliCommandDefinition | CliCommandGroupDefinition)[]
59
60
  testfunctionsCommand,
60
61
  envFunctionsCommand,
61
62
  blueprintsGroup,
63
+ addBlueprintsCommand,
64
+ configBlueprintsCommand,
65
+ deployBlueprintsCommand,
66
+ destroyBlueprintsCommand,
62
67
  infoBlueprintsCommand,
63
- listBlueprintsCommand,
64
68
  initBlueprintsCommand,
65
- deployBlueprintsCommand,
69
+ listBlueprintsCommand,
66
70
  logsBlueprintsCommand,
67
- addBlueprintsCommand,
68
- configBlueprintsCommand,
69
71
  planBlueprintsCommand,
70
72
  ]
File without changes
File without changes
@@ -0,0 +1,222 @@
1
+ import {EventEmitter} from 'node:events'
2
+ import {type MessagePort, type Worker} from 'node:worker_threads'
3
+
4
+ import {afterEach, beforeEach, describe, expect, it, vi} from 'vitest'
5
+
6
+ import {
7
+ createReceiver,
8
+ createReporter,
9
+ type WorkerChannel,
10
+ type WorkerChannelEvent,
11
+ type WorkerChannelStream,
12
+ } from '../workerChannel'
13
+
14
+ // Define a sample worker channel for testing
15
+ type TestWorkerChannel = WorkerChannel<{
16
+ simpleEvent: WorkerChannelEvent<string>
17
+ dataEvent: WorkerChannelEvent<{id: number; value: boolean}>
18
+ simpleStream: WorkerChannelStream<number>
19
+ endStream: WorkerChannelStream<void>
20
+ }>
21
+
22
+ // Mock Worker and MessagePort
23
+ class MockWorker extends EventEmitter {
24
+ terminated = false
25
+ terminate = vi.fn(async () => {
26
+ this.terminated = true
27
+ return 0
28
+ })
29
+ postMessage = vi.fn((message: unknown) => {
30
+ this.emit('message', message)
31
+ })
32
+
33
+ // Helper to simulate receiving a message from the parent (if needed)
34
+ receiveMessage(message: unknown) {
35
+ this.emit('message', message)
36
+ }
37
+
38
+ // Helper to simulate an error from the worker
39
+ emitError(error: unknown) {
40
+ this.emit('error', error)
41
+ }
42
+ }
43
+
44
+ class MockMessagePort extends EventEmitter {
45
+ postMessage = vi.fn((message: unknown) => {
46
+ // Simulate the message being sent back to the parent/receiver
47
+ // In a real scenario, this would go to the Worker's listener
48
+ mockWorkerInstance?.receiveMessage(message)
49
+ })
50
+
51
+ // Helper to simulate receiving a message (e.g., from the parent)
52
+ receiveMessage(message: unknown) {
53
+ this.emit('message', message)
54
+ }
55
+ }
56
+
57
+ let mockWorkerInstance: MockWorker
58
+ let mockParentPortInstance: MockMessagePort
59
+ let receiver: ReturnType<typeof createReceiver<TestWorkerChannel>>
60
+ let reporter: ReturnType<typeof createReporter<TestWorkerChannel>>
61
+
62
+ beforeEach(() => {
63
+ mockWorkerInstance = new MockWorker()
64
+ mockParentPortInstance = new MockMessagePort()
65
+ receiver = createReceiver<TestWorkerChannel>(mockWorkerInstance as unknown as Worker)
66
+ reporter = createReporter<TestWorkerChannel>(mockParentPortInstance as unknown as MessagePort)
67
+ })
68
+
69
+ afterEach(() => {
70
+ vi.clearAllMocks()
71
+ })
72
+
73
+ describe('workerChannel', () => {
74
+ it('should send and receive a simple event', async () => {
75
+ const receivedPromise = receiver.event.simpleEvent()
76
+ reporter.event.simpleEvent('hello')
77
+
78
+ await expect(receivedPromise).resolves.toBe('hello')
79
+ })
80
+
81
+ it('should send and receive an event with data object', async () => {
82
+ const payload = {id: 123, value: true}
83
+ const receivedPromise = receiver.event.dataEvent()
84
+ reporter.event.dataEvent(payload)
85
+
86
+ await expect(receivedPromise).resolves.toEqual(payload)
87
+ })
88
+
89
+ it('should send and receive a stream of data', async () => {
90
+ const receivedItems: number[] = []
91
+ const streamPromise = (async () => {
92
+ for await (const item of receiver.stream.simpleStream()) {
93
+ receivedItems.push(item)
94
+ }
95
+ })()
96
+
97
+ reporter.stream.simpleStream.emit(1)
98
+ reporter.stream.simpleStream.emit(2)
99
+ reporter.stream.simpleStream.emit(3)
100
+ reporter.stream.simpleStream.end()
101
+
102
+ await streamPromise // Wait for the stream processing to complete
103
+
104
+ expect(receivedItems).toEqual([1, 2, 3])
105
+ })
106
+
107
+ it('should handle an empty stream correctly', async () => {
108
+ let streamEntered = false
109
+ const streamPromise = (async () => {
110
+ for await (const _item of receiver.stream.endStream()) {
111
+ streamEntered = true // This should not happen
112
+ }
113
+ })()
114
+
115
+ reporter.stream.endStream.end() // End immediately
116
+
117
+ await streamPromise
118
+
119
+ expect(streamEntered).toBe(false)
120
+ })
121
+
122
+ it('should propagate errors from the worker via event receiver', async () => {
123
+ const error = new Error('Worker failed')
124
+
125
+ const receivedPromise = receiver.event.simpleEvent()
126
+ mockWorkerInstance?.emitError(error)
127
+
128
+ await expect(receivedPromise).rejects.toThrow(error)
129
+ })
130
+
131
+ it('should propagate errors from the worker via stream receiver', async () => {
132
+ const error = new Error('Worker failed during stream')
133
+
134
+ const streamPromise = (async () => {
135
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
136
+ for await (const _item of receiver.stream.simpleStream()) {
137
+ // no-op
138
+ }
139
+ })()
140
+
141
+ // Emit error before ending the stream
142
+ mockWorkerInstance?.emitError(error)
143
+
144
+ await expect(streamPromise).rejects.toThrow(error)
145
+ })
146
+
147
+ it('should handle messages arriving before receiver awaits', async () => {
148
+ // Reporter sends event *before* receiver awaits it
149
+ reporter.event.simpleEvent('early bird')
150
+
151
+ // Give a tick for the message to be processed internally by the mock
152
+ await new Promise((resolve) => setImmediate(resolve))
153
+
154
+ const receivedPromise = receiver.event.simpleEvent()
155
+
156
+ await expect(receivedPromise).resolves.toBe('early bird')
157
+ })
158
+
159
+ it('should handle stream emissions arriving before receiver iterates', async () => {
160
+ // Reporter sends stream data *before* receiver starts iterating
161
+ reporter.stream.simpleStream.emit(10)
162
+ reporter.stream.simpleStream.emit(20)
163
+
164
+ // Give a tick for messages to process
165
+ await new Promise((resolve) => setImmediate(resolve))
166
+
167
+ const receivedItems: number[] = []
168
+ const streamPromise = (async () => {
169
+ for await (const item of receiver.stream.simpleStream()) {
170
+ receivedItems.push(item)
171
+ }
172
+ })()
173
+
174
+ // Send remaining data and end
175
+ reporter.stream.simpleStream.emit(30)
176
+ reporter.stream.simpleStream.end()
177
+
178
+ await streamPromise
179
+
180
+ expect(receivedItems).toEqual([10, 20, 30])
181
+ })
182
+
183
+ it('dispose() should remove listeners and terminate worker', async () => {
184
+ expect(mockWorkerInstance?.listenerCount('message')).toBe(1)
185
+ expect(mockWorkerInstance?.listenerCount('error')).toBe(1)
186
+
187
+ const terminatePromise = receiver.dispose()
188
+
189
+ await expect(terminatePromise).resolves.toBe(0)
190
+ expect(mockWorkerInstance?.terminate).toHaveBeenCalledTimes(1)
191
+ expect(mockWorkerInstance?.listenerCount('message')).toBe(0)
192
+ expect(mockWorkerInstance?.listenerCount('error')).toBe(0)
193
+ expect(mockWorkerInstance?.terminated).toBe(true)
194
+ })
195
+
196
+ it('should throw error if parentPort is null for reporter', () => {
197
+ expect(() => createReporter<TestWorkerChannel>(null)).toThrow('parentPart was falsy')
198
+ })
199
+
200
+ it('should ignore non-worker channel messages', async () => {
201
+ const receivedPromise = receiver.event.simpleEvent()
202
+
203
+ // Send a valid message
204
+ reporter.event.simpleEvent('valid')
205
+ await expect(receivedPromise).resolves.toBe('valid')
206
+
207
+ const nextReceivedPromise = receiver.event.simpleEvent()
208
+
209
+ // Send an invalid message
210
+ mockWorkerInstance?.receiveMessage({foo: 'bar'}) // Not a valid WorkerChannelMessage
211
+ mockWorkerInstance?.receiveMessage('just a string')
212
+ mockWorkerInstance?.receiveMessage(null)
213
+ mockWorkerInstance?.receiveMessage(undefined)
214
+ mockWorkerInstance?.receiveMessage({type: 'unknown'})
215
+
216
+ // Send the actual message we are waiting for
217
+ reporter.event.simpleEvent('after invalid')
218
+
219
+ // It should eventually resolve with the correct message, ignoring the invalid ones
220
+ await expect(nextReceivedPromise).resolves.toBe('after invalid')
221
+ })
222
+ })