effect 4.0.2 → 4.0.3

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 (243) hide show
  1. package/dist/Context.js +3 -3
  2. package/dist/Context.js.map +1 -1
  3. package/dist/Crypto.d.ts +33 -5
  4. package/dist/Crypto.d.ts.map +1 -1
  5. package/dist/Crypto.js +8 -2
  6. package/dist/Crypto.js.map +1 -1
  7. package/dist/DateTime.d.ts +9 -6
  8. package/dist/DateTime.d.ts.map +1 -1
  9. package/dist/DateTime.js +9 -6
  10. package/dist/DateTime.js.map +1 -1
  11. package/dist/Duration.d.ts +6 -6
  12. package/dist/Duration.d.ts.map +1 -1
  13. package/dist/Duration.js +27 -8
  14. package/dist/Duration.js.map +1 -1
  15. package/dist/FileSystem.d.ts +44 -2
  16. package/dist/FileSystem.d.ts.map +1 -1
  17. package/dist/FileSystem.js.map +1 -1
  18. package/dist/Schema.d.ts +473 -1
  19. package/dist/Schema.d.ts.map +1 -1
  20. package/dist/Schema.js +355 -4
  21. package/dist/Schema.js.map +1 -1
  22. package/dist/Stdio.d.ts +7 -1
  23. package/dist/Stdio.d.ts.map +1 -1
  24. package/dist/Stdio.js +1 -0
  25. package/dist/Stdio.js.map +1 -1
  26. package/dist/Stream.d.ts +11 -11
  27. package/dist/Stream.js +5 -5
  28. package/dist/ai/McpServer.d.ts +6 -0
  29. package/dist/ai/McpServer.d.ts.map +1 -1
  30. package/dist/ai/McpServer.js +65 -23
  31. package/dist/ai/McpServer.js.map +1 -1
  32. package/dist/ai/internal/mcpProtocol/v2026_07_28.js +5 -0
  33. package/dist/ai/internal/mcpProtocol/v2026_07_28.js.map +1 -1
  34. package/dist/ai/internal/mcpProtocol.js.map +1 -1
  35. package/dist/ai/internal/mcpRuntime.js +1 -0
  36. package/dist/ai/internal/mcpRuntime.js.map +1 -1
  37. package/dist/cli/CliOutput.d.ts +27 -0
  38. package/dist/cli/CliOutput.d.ts.map +1 -1
  39. package/dist/cli/CliOutput.js +33 -10
  40. package/dist/cli/CliOutput.js.map +1 -1
  41. package/dist/cli/Command.d.ts.map +1 -1
  42. package/dist/cli/Command.js +6 -3
  43. package/dist/cli/Command.js.map +1 -1
  44. package/dist/cli/Completions.d.ts +3 -3
  45. package/dist/cli/Completions.d.ts.map +1 -1
  46. package/dist/cli/Completions.js +8 -5
  47. package/dist/cli/Completions.js.map +1 -1
  48. package/dist/cli/GlobalFlag.d.ts +5 -4
  49. package/dist/cli/GlobalFlag.d.ts.map +1 -1
  50. package/dist/cli/GlobalFlag.js +3 -3
  51. package/dist/cli/GlobalFlag.js.map +1 -1
  52. package/dist/cli/Prompt.d.ts +17 -1
  53. package/dist/cli/Prompt.d.ts.map +1 -1
  54. package/dist/cli/Prompt.js +119 -114
  55. package/dist/cli/Prompt.js.map +1 -1
  56. package/dist/cli/internal/ansi.d.ts +0 -1
  57. package/dist/cli/internal/ansi.d.ts.map +1 -1
  58. package/dist/cli/internal/ansi.js +20 -3
  59. package/dist/cli/internal/ansi.js.map +1 -1
  60. package/dist/cli/internal/completions/powershell.d.ts +2 -0
  61. package/dist/cli/internal/completions/powershell.d.ts.map +1 -0
  62. package/dist/cli/internal/completions/powershell.js +273 -0
  63. package/dist/cli/internal/completions/powershell.js.map +1 -0
  64. package/dist/cli/internal/completions/zsh.js +3 -2
  65. package/dist/cli/internal/completions/zsh.js.map +1 -1
  66. package/dist/cli/internal/wizard.d.ts +3 -3
  67. package/dist/cli/internal/wizard.d.ts.map +1 -1
  68. package/dist/cli/internal/wizard.js +21 -18
  69. package/dist/cli/internal/wizard.js.map +1 -1
  70. package/dist/cluster/ClusterSchema.d.ts +28 -0
  71. package/dist/cluster/ClusterSchema.d.ts.map +1 -1
  72. package/dist/cluster/ClusterSchema.js +30 -0
  73. package/dist/cluster/ClusterSchema.js.map +1 -1
  74. package/dist/cluster/ClusterWorkflowEngine.d.ts.map +1 -1
  75. package/dist/cluster/ClusterWorkflowEngine.js +10 -18
  76. package/dist/cluster/ClusterWorkflowEngine.js.map +1 -1
  77. package/dist/cluster/MessageStorage.d.ts +18 -3
  78. package/dist/cluster/MessageStorage.d.ts.map +1 -1
  79. package/dist/cluster/MessageStorage.js +31 -11
  80. package/dist/cluster/MessageStorage.js.map +1 -1
  81. package/dist/cluster/Runners.d.ts.map +1 -1
  82. package/dist/cluster/Runners.js +8 -2
  83. package/dist/cluster/Runners.js.map +1 -1
  84. package/dist/cluster/Sharding.d.ts.map +1 -1
  85. package/dist/cluster/Sharding.js +181 -62
  86. package/dist/cluster/Sharding.js.map +1 -1
  87. package/dist/cluster/SqlMessageStorage.d.ts +59 -19
  88. package/dist/cluster/SqlMessageStorage.d.ts.map +1 -1
  89. package/dist/cluster/SqlMessageStorage.js +84 -55
  90. package/dist/cluster/SqlMessageStorage.js.map +1 -1
  91. package/dist/cluster/SqlRunnerStorage.d.ts +68 -19
  92. package/dist/cluster/SqlRunnerStorage.d.ts.map +1 -1
  93. package/dist/cluster/SqlRunnerStorage.js +191 -113
  94. package/dist/cluster/SqlRunnerStorage.js.map +1 -1
  95. package/dist/cluster/internal/entityManager.js +104 -37
  96. package/dist/cluster/internal/entityManager.js.map +1 -1
  97. package/dist/http/FindMyWay/internal/router.js +3 -6
  98. package/dist/http/FindMyWay/internal/router.js.map +1 -1
  99. package/dist/http/HttpClient.d.ts +6 -0
  100. package/dist/http/HttpClient.d.ts.map +1 -1
  101. package/dist/http/HttpClient.js +39 -2
  102. package/dist/http/HttpClient.js.map +1 -1
  103. package/dist/http/HttpTraceContext.d.ts.map +1 -1
  104. package/dist/http/HttpTraceContext.js +4 -2
  105. package/dist/http/HttpTraceContext.js.map +1 -1
  106. package/dist/internal/effect.js +12 -7
  107. package/dist/internal/effect.js.map +1 -1
  108. package/dist/internal/version.js +1 -1
  109. package/dist/net/AddressResolver.d.ts +145 -0
  110. package/dist/net/AddressResolver.d.ts.map +1 -0
  111. package/dist/net/AddressResolver.js +114 -0
  112. package/dist/net/AddressResolver.js.map +1 -0
  113. package/dist/net/Dns.d.ts +596 -0
  114. package/dist/net/Dns.d.ts.map +1 -0
  115. package/dist/net/Dns.js +549 -0
  116. package/dist/net/Dns.js.map +1 -0
  117. package/dist/net/Host.d.ts +320 -0
  118. package/dist/net/Host.d.ts.map +1 -0
  119. package/dist/net/Host.js +342 -0
  120. package/dist/net/Host.js.map +1 -0
  121. package/dist/net/IpInterface.d.ts +76 -3
  122. package/dist/net/IpInterface.d.ts.map +1 -1
  123. package/dist/net/IpInterface.js +53 -4
  124. package/dist/net/IpInterface.js.map +1 -1
  125. package/dist/net/IpNetwork.d.ts +82 -8
  126. package/dist/net/IpNetwork.d.ts.map +1 -1
  127. package/dist/net/IpNetwork.js +60 -4
  128. package/dist/net/IpNetwork.js.map +1 -1
  129. package/dist/net/NetAddress.d.ts +388 -35
  130. package/dist/net/NetAddress.d.ts.map +1 -1
  131. package/dist/net/NetAddress.js +350 -34
  132. package/dist/net/NetAddress.js.map +1 -1
  133. package/dist/net/index.d.ts +15 -0
  134. package/dist/net/index.d.ts.map +1 -1
  135. package/dist/net/index.js +15 -0
  136. package/dist/net/index.js.map +1 -1
  137. package/dist/observability/OtlpMetrics.d.ts.map +1 -1
  138. package/dist/observability/OtlpMetrics.js +15 -21
  139. package/dist/observability/OtlpMetrics.js.map +1 -1
  140. package/dist/observability/PrometheusMetrics.js +1 -0
  141. package/dist/observability/PrometheusMetrics.js.map +1 -1
  142. package/dist/persistence/PersistedQueue.d.ts +32 -3
  143. package/dist/persistence/PersistedQueue.d.ts.map +1 -1
  144. package/dist/persistence/PersistedQueue.js +75 -16
  145. package/dist/persistence/PersistedQueue.js.map +1 -1
  146. package/dist/rpc/RpcClient.d.ts +22 -6
  147. package/dist/rpc/RpcClient.d.ts.map +1 -1
  148. package/dist/rpc/RpcClient.js +12 -5
  149. package/dist/rpc/RpcClient.js.map +1 -1
  150. package/dist/rpc/RpcServer.d.ts +6 -0
  151. package/dist/rpc/RpcServer.d.ts.map +1 -1
  152. package/dist/rpc/RpcServer.js +42 -20
  153. package/dist/rpc/RpcServer.js.map +1 -1
  154. package/dist/socket/DatagramSocket.d.ts +456 -0
  155. package/dist/socket/DatagramSocket.d.ts.map +1 -0
  156. package/dist/socket/DatagramSocket.js +535 -0
  157. package/dist/socket/DatagramSocket.js.map +1 -0
  158. package/dist/socket/Socket.d.ts +14 -1
  159. package/dist/socket/Socket.d.ts.map +1 -1
  160. package/dist/socket/Socket.js +9 -1
  161. package/dist/socket/Socket.js.map +1 -1
  162. package/dist/socket/index.d.ts +5 -0
  163. package/dist/socket/index.d.ts.map +1 -1
  164. package/dist/socket/index.js +5 -0
  165. package/dist/socket/index.js.map +1 -1
  166. package/dist/sql/Migrator.d.ts.map +1 -1
  167. package/dist/sql/Migrator.js +7 -2
  168. package/dist/sql/Migrator.js.map +1 -1
  169. package/dist/sql/SqlClient.d.ts +7 -1
  170. package/dist/sql/SqlClient.d.ts.map +1 -1
  171. package/dist/sql/SqlClient.js +31 -6
  172. package/dist/sql/SqlClient.js.map +1 -1
  173. package/dist/testing/TestCrypto.d.ts +73 -0
  174. package/dist/testing/TestCrypto.d.ts.map +1 -0
  175. package/dist/testing/TestCrypto.js +87 -0
  176. package/dist/testing/TestCrypto.js.map +1 -0
  177. package/dist/testing/index.d.ts +5 -0
  178. package/dist/testing/index.d.ts.map +1 -1
  179. package/dist/testing/index.js +5 -0
  180. package/dist/testing/index.js.map +1 -1
  181. package/dist/workflow/DurableDeferred.d.ts +54 -0
  182. package/dist/workflow/DurableDeferred.d.ts.map +1 -1
  183. package/dist/workflow/DurableDeferred.js +78 -49
  184. package/dist/workflow/DurableDeferred.js.map +1 -1
  185. package/dist/workflow/WorkflowEngine.d.ts.map +1 -1
  186. package/dist/workflow/WorkflowEngine.js +2 -1
  187. package/dist/workflow/WorkflowEngine.js.map +1 -1
  188. package/package.json +1 -1
  189. package/src/Context.ts +3 -3
  190. package/src/Crypto.ts +43 -9
  191. package/src/DateTime.ts +9 -6
  192. package/src/Duration.ts +36 -16
  193. package/src/FileSystem.ts +48 -2
  194. package/src/Schema.ts +669 -5
  195. package/src/Stdio.ts +9 -2
  196. package/src/Stream.ts +11 -11
  197. package/src/ai/McpServer.ts +63 -23
  198. package/src/ai/internal/mcpProtocol/v2026_07_28.ts +5 -0
  199. package/src/ai/internal/mcpProtocol.ts +3 -0
  200. package/src/ai/internal/mcpRuntime.ts +2 -0
  201. package/src/cli/CliOutput.ts +33 -16
  202. package/src/cli/Command.ts +4 -3
  203. package/src/cli/Completions.ts +9 -6
  204. package/src/cli/GlobalFlag.ts +19 -11
  205. package/src/cli/Prompt.ts +159 -149
  206. package/src/cli/internal/ansi.ts +26 -3
  207. package/src/cli/internal/completions/powershell.ts +322 -0
  208. package/src/cli/internal/completions/zsh.ts +4 -2
  209. package/src/cli/internal/wizard.ts +34 -26
  210. package/src/cluster/ClusterSchema.ts +32 -0
  211. package/src/cluster/ClusterWorkflowEngine.ts +13 -24
  212. package/src/cluster/MessageStorage.ts +45 -14
  213. package/src/cluster/Runners.ts +8 -2
  214. package/src/cluster/Sharding.ts +207 -49
  215. package/src/cluster/SqlMessageStorage.ts +118 -84
  216. package/src/cluster/SqlRunnerStorage.ts +224 -128
  217. package/src/cluster/internal/entityManager.ts +151 -55
  218. package/src/http/FindMyWay/internal/router.ts +4 -7
  219. package/src/http/HttpClient.ts +56 -2
  220. package/src/http/HttpTraceContext.ts +8 -2
  221. package/src/internal/effect.ts +15 -13
  222. package/src/internal/version.ts +1 -1
  223. package/src/net/AddressResolver.ts +222 -0
  224. package/src/net/Dns.ts +1035 -0
  225. package/src/net/Host.ts +472 -0
  226. package/src/net/IpInterface.ts +117 -7
  227. package/src/net/IpNetwork.ts +116 -17
  228. package/src/net/NetAddress.ts +611 -76
  229. package/src/net/index.ts +18 -0
  230. package/src/observability/OtlpMetrics.ts +12 -23
  231. package/src/observability/PrometheusMetrics.ts +1 -0
  232. package/src/persistence/PersistedQueue.ts +146 -53
  233. package/src/rpc/RpcClient.ts +41 -19
  234. package/src/rpc/RpcServer.ts +60 -25
  235. package/src/socket/DatagramSocket.ts +919 -0
  236. package/src/socket/Socket.ts +22 -2
  237. package/src/socket/index.ts +6 -0
  238. package/src/sql/Migrator.ts +21 -5
  239. package/src/sql/SqlClient.ts +39 -15
  240. package/src/testing/TestCrypto.ts +94 -0
  241. package/src/testing/index.ts +6 -0
  242. package/src/workflow/DurableDeferred.ts +123 -48
  243. package/src/workflow/WorkflowEngine.ts +2 -1
@@ -105,9 +105,32 @@ export const blackBright = `${ESC}90m`
105
105
  */
106
106
  export const cyanBright = `${ESC}96m`
107
107
 
108
- export const annotate = (text: string, ...styles: Array<string | Array<string>>) => {
109
- const flat = styles.flat()
110
- return `${flat.join("")}${text}${reset}`
108
+ /**
109
+ * Wraps `text` in the given styles followed by a reset. When `colors` is
110
+ * `false` the text is returned unchanged, so callers gate color and style
111
+ * escapes in one place while cursor and erase controls stay untouched.
112
+ *
113
+ * @internal
114
+ */
115
+ export const annotate = (colors: boolean, text: string, ...styles: Array<string | Array<string>>): string => {
116
+ if (!colors) return text
117
+ return `${styles.flat().join("")}${text}${reset}`
118
+ }
119
+
120
+ /**
121
+ * Detects whether CLI output should use color and style escapes: stdout must
122
+ * be a TTY and `NO_COLOR` must be unset or empty.
123
+ *
124
+ * @internal
125
+ */
126
+ export const detectColors = (): boolean => {
127
+ const globalProcess = (globalThis as any).process
128
+ return typeof globalProcess === "object" &&
129
+ globalProcess !== null &&
130
+ typeof globalProcess.stdout === "object" &&
131
+ globalProcess.stdout !== null &&
132
+ globalProcess.stdout.isTTY === true &&
133
+ !globalProcess.env?.NO_COLOR
111
134
  }
112
135
 
113
136
  export const combine = (...styles: Array<string>): Array<string> => styles
@@ -0,0 +1,322 @@
1
+ /**
2
+ * Static PowerShell completion script generator.
3
+ *
4
+ * Produces a self-contained completion script from a `CommandDescriptor`,
5
+ * without invoking the CLI at runtime. The script sticks to Windows
6
+ * PowerShell 5.1 syntax and APIs.
7
+ *
8
+ * @internal
9
+ */
10
+ import type * as Completions from "../../Completions.ts"
11
+
12
+ /**
13
+ * Encode non-ASCII and control characters as `[char]` code units. Windows
14
+ * PowerShell 5.1 can misread BOM-less UTF-8 as ANSI, turning some bytes into
15
+ * PowerShell quote delimiters.
16
+ */
17
+ const quotePs = (s: string): string => {
18
+ const parts: Array<string> = []
19
+ let literal = ""
20
+ for (let i = 0; i < s.length; i++) {
21
+ const code = s.charCodeAt(i)
22
+ if (code >= 0x20 && code < 0x80) {
23
+ literal += s[i] === "'" ? "''" : s[i]
24
+ continue
25
+ }
26
+ if (literal !== "" || parts.length === 0) parts.push(`'${literal}'`)
27
+ literal = ""
28
+ parts.push(`[char]0x${code.toString(16).toUpperCase().padStart(4, "0")}`)
29
+ }
30
+ if (literal !== "" || parts.length === 0) parts.push(`'${literal}'`)
31
+ return parts.length === 1 ? parts[0] : `(${parts.join(" + ")})`
32
+ }
33
+
34
+ const psArray = (items: ReadonlyArray<string>): string => `@(${items.map(quotePs).join(", ")})`
35
+
36
+ const psOptional = (s: string | undefined): string => s ? quotePs(s) : "$null"
37
+
38
+ const sanitize = (s: string): string => s.replace(/[^a-zA-Z0-9_]/g, "_")
39
+
40
+ const flagForms = (flag: Completions.FlagDescriptor): Array<string> => [
41
+ `--${flag.name}`,
42
+ ...flag.aliases.map((alias) => alias.length === 1 ? `-${alias}` : `--${alias}`)
43
+ ]
44
+
45
+ const valueFields = (type: Completions.FlagType | Completions.ArgumentType): string =>
46
+ `values = ${psArray(type._tag === "Choice" ? type.values : [])}; ` +
47
+ `pathType = ${type._tag === "Path" ? quotePs(type.pathType) : "$null"}`
48
+
49
+ const flagEntry = (flag: Completions.FlagDescriptor): string =>
50
+ `@{ name = ${quotePs(flag.name)}; forms = ${psArray(flagForms(flag))}; ` +
51
+ `takesValue = $${flag.type._tag !== "Boolean"}; ${valueFields(flag.type)}; ` +
52
+ `description = ${psOptional(flag.description)} }`
53
+
54
+ const argumentEntry = (argument: Completions.ArgumentDescriptor): string =>
55
+ `@{ name = ${quotePs(argument.name)}; variadic = $${argument.variadic}; ${valueFields(argument.type)}; ` +
56
+ `description = ${psOptional(argument.description)} }`
57
+
58
+ const pushList = (lines: Array<string>, field: string, entries: ReadonlyArray<string>): void => {
59
+ if (entries.length === 0) {
60
+ lines.push(` ${field} = @()`)
61
+ return
62
+ }
63
+ lines.push(` ${field} = @(`)
64
+ for (const entry of entries) {
65
+ lines.push(` ${entry}`)
66
+ }
67
+ lines.push(` )`)
68
+ }
69
+
70
+ /**
71
+ * Emit one context per subcommand path. Subcommands are case-sensitive, so
72
+ * contexts go in an ordinal hashtable instead of a hash literal, whose keys
73
+ * ignore case.
74
+ */
75
+ const generateContexts = (
76
+ dataName: string,
77
+ descriptor: Completions.CommandDescriptor,
78
+ path: ReadonlyArray<string>,
79
+ lines: Array<string>,
80
+ seen: Set<string>
81
+ ): void => {
82
+ const key = path.join(" ")
83
+ if (seen.has(key)) return
84
+ seen.add(key)
85
+ lines.push(`${dataName}[${quotePs(key)}] = @{`)
86
+ pushList(
87
+ lines,
88
+ "subcommands",
89
+ descriptor.subcommands.map((sub) =>
90
+ `@{ name = ${quotePs(sub.name)}; description = ${psOptional(sub.description)} }`
91
+ )
92
+ )
93
+ pushList(lines, "flags", descriptor.flags.map(flagEntry))
94
+ pushList(lines, "arguments", descriptor.arguments.map(argumentEntry))
95
+ lines.push(`}`)
96
+ for (const sub of descriptor.subcommands) {
97
+ generateContexts(dataName, sub, [...path, sub.name], lines, seen)
98
+ }
99
+ }
100
+
101
+ /**
102
+ * Replay the committed words with the CLI lexer and parser rules: `--`, short
103
+ * flag clusters, inline values, boolean literals, negative numbers, and
104
+ * subcommand selection by the first value only.
105
+ */
106
+ const completer = (dataName: string): string =>
107
+ String.raw` param($wordToComplete, $commandAst, $cursorPosition)
108
+
109
+ $spec = ${dataName}
110
+ $results = [System.Collections.Generic.List[System.Management.Automation.CompletionResult]]::new()
111
+ $booleanLiterals = @('true', 'yes', 'on', '1', 'y', 'false', 'no', 'off', '0', 'n')
112
+
113
+ $index = {
114
+ param($context)
115
+ $lookup = [hashtable]::new([System.StringComparer]::Ordinal)
116
+ foreach ($flag in $context.flags) {
117
+ foreach ($form in $flag.forms) { $lookup[$form] = $flag }
118
+ }
119
+ $lookup
120
+ }
121
+
122
+ $add = {
123
+ param([string]$value, [string]$type, $tooltip, [string]$typed, [string]$textPrefix)
124
+ if (-not $value.StartsWith($typed, [System.StringComparison]::OrdinalIgnoreCase)) { return }
125
+ $text = $value
126
+ if ($value -notmatch '^[\w./:%+=\\-]+$') { $text = "'" + ($value -replace "['\u2018-\u201b]", '$0$0') + "'" }
127
+ $text = $textPrefix + $text
128
+ $label = $value
129
+ if (-not $label) { $label = $text }
130
+ if (-not $tooltip) { $tooltip = $label }
131
+ $results.Add([System.Management.Automation.CompletionResult]::new($text, $label, $type, $tooltip))
132
+ }
133
+
134
+ $addValues = {
135
+ param($entry, [string]$typed, [string]$textPrefix)
136
+ foreach ($value in $entry.values) {
137
+ if ($textPrefix -eq '' -and (& $optionLike $value)) { continue }
138
+ & $add $value 'ParameterValue' $entry.description $typed $textPrefix
139
+ }
140
+ if ($entry.pathType) {
141
+ foreach ($file in [System.Management.Automation.CompletionCompleters]::CompleteFilename($typed)) {
142
+ if ($entry.pathType -eq 'directory' -and $file.ResultType -ne 'ProviderContainer') { continue }
143
+ $results.Add([System.Management.Automation.CompletionResult]::new($textPrefix + $file.CompletionText, $file.ListItemText, $file.ResultType, $file.ToolTip))
144
+ }
145
+ }
146
+ }
147
+
148
+ $addFlags = {
149
+ param([string]$typed)
150
+ foreach ($flag in $context.flags) {
151
+ if ($used.ContainsKey($flag.name)) { continue }
152
+ foreach ($form in $flag.forms) {
153
+ & $add $form 'ParameterName' $flag.description $typed ''
154
+ }
155
+ if (-not $flag.takesValue) {
156
+ $tooltip = $null
157
+ if ($flag.description) { $tooltip = 'Disable ' + $flag.name }
158
+ & $add ('--no-' + $flag.name) 'ParameterName' $tooltip $typed ''
159
+ }
160
+ }
161
+ }
162
+
163
+ # As in the CLI lexer, nothing after '--' is an option, and negative numbers
164
+ # and a lone '-' are values.
165
+ $optionLike = {
166
+ param([string]$word)
167
+ -not $endOfOptions -and $word -match '^-.' -and $word -notmatch '^-(\d+\.?\d*|\.\d+)([eE][+-]?\d+)?$'
168
+ }
169
+
170
+ # A typed value that has no candidates completes to itself, so the engine
171
+ # does not fall back to listing files.
172
+ $keepTyped = {
173
+ if ($results.Count -eq 0 -and $current) {
174
+ $results.Add([System.Management.Automation.CompletionResult]::new($current, $current, 'ParameterValue', $current))
175
+ }
176
+ }
177
+
178
+ $path = ''
179
+ $context = $spec[$path]
180
+ $lookup = & $index $context
181
+ $used = [hashtable]::new([System.StringComparer]::Ordinal)
182
+ $pending = $null
183
+ $endOfOptions = $false
184
+ $position = 0
185
+
186
+ # The engine strips the quotes from a quoted word.
187
+ $typed = [string]$wordToComplete
188
+ if ($typed -match '^([''"])(.*?)\1?$') { $typed = $Matches[2] }
189
+ $current = $null
190
+
191
+ $elements = $commandAst.CommandElements
192
+ for ($i = 1; $i -lt $elements.Count; $i++) {
193
+ $element = $elements[$i]
194
+ $word = $element.Extent.Text
195
+ if ($element -is [System.Management.Automation.Language.StringConstantExpressionAst]) { $word = $element.Value }
196
+ if ($element.Extent.EndOffset -ge $cursorPosition) {
197
+ if ($element -is [System.Management.Automation.Language.ConstantExpressionAst] -and $element.Extent.StartOffset -le $cursorPosition) {
198
+ $current = $element.Extent.Text
199
+ if ($element.Extent.EndOffset -eq $cursorPosition) { $typed = $word }
200
+ }
201
+ break
202
+ }
203
+
204
+ # A flag takes the next word as its value unless it is an option or '--';
205
+ # a boolean flag only takes a boolean literal.
206
+ $isOption = & $optionLike $word
207
+ if ($null -ne $pending) {
208
+ $flag = $pending
209
+ $pending = $null
210
+ if ($flag.takesValue) {
211
+ if (-not $isOption) { continue }
212
+ } elseif ($booleanLiterals -ccontains $word) { continue }
213
+ }
214
+ if ($endOfOptions) { $position++; continue }
215
+ if ($word -ceq '--') { $endOfOptions = $true; continue }
216
+
217
+ if ($isOption) {
218
+ $hasValue = $word.Contains('=')
219
+ if ($hasValue) {
220
+ $forms = @($word.Substring(0, $word.IndexOf('=')))
221
+ } elseif ($word.StartsWith('--')) {
222
+ $forms = @($word)
223
+ } else {
224
+ $forms = @(foreach ($char in $word.Substring(1).ToCharArray()) { '-' + $char })
225
+ }
226
+ foreach ($form in $forms) {
227
+ $pending = $null
228
+ $flag = $lookup[$form]
229
+ if ($null -eq $flag) {
230
+ if ($form.StartsWith('--no-')) {
231
+ $name = $form.Substring(5)
232
+ $flag = $lookup['--' + $name]
233
+ if ($null -ne $flag -and -not $flag.takesValue -and $flag.name -ceq $name) { $used[$name] = $true }
234
+ }
235
+ continue
236
+ }
237
+ $used[$flag.name] = $true
238
+ if (-not $hasValue) { $pending = $flag }
239
+ }
240
+ continue
241
+ }
242
+
243
+ if ($position -eq 0 -and $context.subcommands.name -ccontains $word) {
244
+ if ($path) { $path = $path + ' ' + $word } else { $path = $word }
245
+ $context = $spec[$path]
246
+ $lookup = & $index $context
247
+ $used = [hashtable]::new([System.StringComparer]::Ordinal)
248
+ continue
249
+ }
250
+ $position++
251
+ }
252
+
253
+ $typedIsOption = & $optionLike $typed
254
+ if ($null -ne $pending -and $pending.takesValue -and -not $typedIsOption) {
255
+ & $addValues $pending $typed ''
256
+ & $keepTyped
257
+ return $results
258
+ }
259
+
260
+ if ($typedIsOption) {
261
+ if ($typed -match '^(-[^=]+)=(.*)$') {
262
+ $form = $Matches[1]
263
+ $flag = $lookup[$form]
264
+ if ($null -ne $flag -and $flag.takesValue) {
265
+ & $addValues $flag $Matches[2] ($form + '=')
266
+ & $keepTyped
267
+ }
268
+ } else {
269
+ & $addFlags $typed
270
+ }
271
+ return $results
272
+ }
273
+
274
+ if (-not $endOfOptions -and $position -eq 0) {
275
+ foreach ($sub in $context.subcommands) {
276
+ & $add $sub.name 'Command' $sub.description $typed ''
277
+ }
278
+ }
279
+
280
+ $arguments = $context.arguments
281
+ if ($position -lt $arguments.Count) {
282
+ & $addValues $arguments[$position] $typed ''
283
+ } elseif ($arguments.Count -gt 0 -and $arguments[-1].variadic) {
284
+ & $addValues $arguments[-1] $typed ''
285
+ }
286
+
287
+ if (-not $endOfOptions) {
288
+ & $addFlags $typed
289
+ }
290
+ return $results`
291
+
292
+ /** @internal */
293
+ export const generate = (
294
+ executableName: string,
295
+ descriptor: Completions.CommandDescriptor
296
+ ): string => {
297
+ const dataName = `$global:_${sanitize(executableName)}_Completions`
298
+ const lines: Array<string> = []
299
+
300
+ lines.push(`###-begin-${executableName}-completions-###`)
301
+ lines.push(`#`)
302
+ lines.push(`# Static completion script for PowerShell`)
303
+ lines.push(`#`)
304
+ lines.push(`# Installation:`)
305
+ // `>>` on Windows PowerShell 5.1 appends UTF-16LE to the profile, so the
306
+ // script goes in its own file.
307
+ lines.push(`# ${executableName} --completions powershell > ${executableName}-completion.ps1`)
308
+ lines.push(`# then add this line to your $PROFILE (use the full path to the file):`)
309
+ lines.push(`# . <PATH>\\${executableName}-completion.ps1`)
310
+ lines.push(`#`)
311
+ lines.push(``)
312
+ lines.push(`${dataName} = [hashtable]::new([System.StringComparer]::Ordinal)`)
313
+ generateContexts(dataName, descriptor, [], lines, new Set())
314
+ lines.push(``)
315
+ lines.push(`Register-ArgumentCompleter -Native -CommandName ${quotePs(executableName)} -ScriptBlock {`)
316
+ lines.push(completer(dataName))
317
+ lines.push(`}`)
318
+ lines.push(``)
319
+ lines.push(`###-end-${executableName}-completions-###`)
320
+
321
+ return lines.join("\n")
322
+ }
@@ -14,6 +14,8 @@ import type * as Completions from "../../Completions.ts"
14
14
 
15
15
  const escapeZsh = (s: string): string => s.replace(/\\/g, "\\\\").replace(/'/g, "'\\''").replace(/:/g, "\\:")
16
16
 
17
+ const escapeZshExplanation = (s: string): string => escapeZsh(s).replace(/]/g, "\\]")
18
+
17
19
  /** Escape choices for the second parse of a Zsh `_arguments` action. */
18
20
  const escapeZshChoice = (s: string): string => s.replace(/[^A-Za-z0-9_.,/@%+-]/gu, "\\$&").replace(/'/g, "'\\''")
19
21
 
@@ -69,7 +71,7 @@ const argAction = (type: Completions.ArgumentType): string => {
69
71
  */
70
72
  const flagSpecs = (flag: Completions.FlagDescriptor): Array<string> => {
71
73
  const specs: Array<string> = []
72
- const desc = flag.description ? `[${escapeZsh(flag.description)}]` : ""
74
+ const desc = flag.description ? `[${escapeZshExplanation(flag.description)}]` : ""
73
75
  const action = valueAction(flag.type)
74
76
  const excl = `(${allForms(flag).join(" ")})`
75
77
 
@@ -84,7 +86,7 @@ const flagSpecs = (flag: Completions.FlagDescriptor): Array<string> => {
84
86
 
85
87
  // Boolean negation
86
88
  if (flag.type._tag === "Boolean") {
87
- const negDesc = flag.description ? `[${escapeZsh(`Disable ${flag.name}`)}]` : ""
89
+ const negDesc = flag.description ? `[${escapeZshExplanation(`Disable ${flag.name}`)}]` : ""
88
90
  specs.push(`'${excl}--no-${flag.name}${negDesc}'`)
89
91
  }
90
92
 
@@ -34,8 +34,9 @@ export const run: (
34
34
  const commandPath = options?.commandPath ?? [command.name]
35
35
  const selected = getCommandAtPath(command, commandPath)
36
36
  const commandLine = (options?.prefix ?? commandPath).map((value) => commandLineArg(value))
37
- yield* logCurrentCommand(commandLine)
38
- yield* promptCommand(selected, commandLine, selected === command ? "ROOT" : selected.name)
37
+ const { colors } = yield* Prompt.Theme
38
+ yield* logCurrentCommand(commandLine, colors)
39
+ yield* promptCommand(selected, commandLine, selected === command ? "ROOT" : selected.name, colors)
39
40
  return {
40
41
  args: commandLine.map((arg) => arg.value),
41
42
  displayArgs: commandLine.map((arg) => arg.displayValue)
@@ -63,30 +64,31 @@ const getCommandAtPath = (
63
64
  const promptCommand: (
64
65
  command: Command.Command.Any,
65
66
  commandLine: Array<CommandLineArg>,
66
- sectionName: string
67
+ sectionName: string,
68
+ colors: boolean
67
69
  ) => Effect.Effect<void, CliError.CliError | Terminal.QuitError, Command.Environment> = Effect.fnUntraced(
68
- function*(command, commandLine, sectionName) {
70
+ function*(command, commandLine, sectionName, colors) {
69
71
  const impl = toImpl(command)
70
72
  const visibleSubcommands = command.subcommands.flatMap((group) => group.commands.filter((child) => !child.unlisted))
71
73
  const config = visibleSubcommands.length === 0 ? impl.config : impl.contextConfig
72
74
 
73
75
  if (config.flags.length > 0) {
74
- yield* Console.log(renderSection(sectionName, "FLAGS"))
76
+ yield* Console.log(renderSection(sectionName, "FLAGS", colors))
75
77
  for (const param of config.flags) {
76
78
  commandLine.push(...yield* promptParam(param))
77
79
  }
78
80
  if (config.arguments.length > 0 || visibleSubcommands.length > 0) {
79
- yield* logCurrentCommand(commandLine)
81
+ yield* logCurrentCommand(commandLine, colors)
80
82
  }
81
83
  }
82
84
 
83
85
  if (config.arguments.length > 0) {
84
- yield* Console.log(renderSection(sectionName, "ARGUMENTS"))
86
+ yield* Console.log(renderSection(sectionName, "ARGUMENTS", colors))
85
87
  for (const param of config.arguments) {
86
88
  commandLine.push(...yield* promptParam(param))
87
89
  }
88
90
  if (visibleSubcommands.length > 0) {
89
- yield* logCurrentCommand(commandLine)
91
+ yield* logCurrentCommand(commandLine, colors)
90
92
  }
91
93
  }
92
94
 
@@ -109,9 +111,9 @@ const promptCommand: (
109
111
  yield* Console.log()
110
112
  commandLine.push(commandLineArg(child.name))
111
113
  if (hasWizardSteps(child)) {
112
- yield* logCurrentCommand(commandLine)
114
+ yield* logCurrentCommand(commandLine, colors)
113
115
  }
114
- yield* promptCommand(child, commandLine, child.name)
116
+ yield* promptCommand(child, commandLine, child.name, colors)
115
117
  }
116
118
  )
117
119
 
@@ -244,41 +246,47 @@ const humanize = (name: string): string => {
244
246
  return [words[0][0].toUpperCase() + words[0].slice(1), ...words.slice(1)].join(" ")
245
247
  }
246
248
 
247
- const logCurrentCommand = (commandLine: ReadonlyArray<CommandLineArg>): Effect.Effect<void> =>
248
- Console.log(renderCommandBlock("Current command", commandLine.map((arg) => arg.displayValue), Ansi.magenta))
249
+ const logCurrentCommand = (commandLine: ReadonlyArray<CommandLineArg>, colors: boolean): Effect.Effect<void> =>
250
+ Console.log(renderCommandBlock("Current command", commandLine.map((arg) => arg.displayValue), colors, Ansi.magenta))
249
251
 
250
- const renderSection = (commandName: string, section: string): string =>
251
- `${Ansi.annotate(commandName.toUpperCase(), Ansi.bold, Ansi.cyanBright)} ${Ansi.annotate("·", Ansi.blackBright)} ${
252
- Ansi.annotate(section, Ansi.bold, Ansi.white)
253
- }`
252
+ const renderSection = (commandName: string, section: string, colors: boolean): string =>
253
+ `${Ansi.annotate(colors, commandName.toUpperCase(), Ansi.bold, Ansi.cyanBright)} ${
254
+ Ansi.annotate(colors, "·", Ansi.blackBright)
255
+ } ${Ansi.annotate(colors, section, Ansi.bold, Ansi.white)}`
254
256
 
255
- export const renderIntroduction = (name: string, version: string, summary: string | undefined): string => {
256
- const title = `${Ansi.annotate(name, Ansi.bold, Ansi.cyanBright)} ${Ansi.annotate(`v${version}`, Ansi.white)} ${
257
- Ansi.annotate("· Command wizard", Ansi.bold, Ansi.white)
258
- }`
257
+ export const renderIntroduction = (
258
+ name: string,
259
+ version: string,
260
+ summary: string | undefined,
261
+ colors: boolean
262
+ ): string => {
263
+ const title = `${Ansi.annotate(colors, name, Ansi.bold, Ansi.cyanBright)} ${
264
+ Ansi.annotate(colors, `v${version}`, Ansi.white)
265
+ } ${Ansi.annotate(colors, "· Command wizard", Ansi.bold, Ansi.white)}`
259
266
  return [
260
267
  title,
261
268
  ...(summary === undefined || summary.length === 0 ? [] : [summary]),
262
- Ansi.annotate("Build a command interactively. Press Ctrl+C to cancel.", Ansi.blackBright),
269
+ Ansi.annotate(colors, "Build a command interactively. Press Ctrl+C to cancel.", Ansi.blackBright),
263
270
  ""
264
271
  ].join("\n")
265
272
  }
266
273
 
267
- export const renderCompletion = (commandLine: ReadonlyArray<string>): string =>
268
- renderCommandBlock("Command ready", commandLine, Ansi.cyanBright, Ansi.green)
274
+ export const renderCompletion = (commandLine: ReadonlyArray<string>, colors: boolean): string =>
275
+ renderCommandBlock("Command ready", commandLine, colors, Ansi.cyanBright, Ansi.green)
269
276
 
270
- export const renderQuit = (): string => `\n${Ansi.annotate("Wizard cancelled.", Ansi.red)}`
277
+ export const renderQuit = (colors: boolean): string => `\n${Ansi.annotate(colors, "Wizard cancelled.", Ansi.red)}`
271
278
 
272
279
  const renderCommandBlock = (
273
280
  label: string,
274
281
  commandLine: ReadonlyArray<string>,
282
+ colors: boolean,
275
283
  commandColor: string,
276
284
  labelColor: string = Ansi.white
277
285
  ): string => {
278
286
  const lines = wrapCommand(commandLine)
279
287
  return [
280
- Ansi.annotate(label, Ansi.bold, labelColor),
281
- ...lines.map((line) => Ansi.annotate(line, commandColor)),
288
+ Ansi.annotate(colors, label, Ansi.bold, labelColor),
289
+ ...lines.map((line) => Ansi.annotate(colors, line, commandColor)),
282
290
  ""
283
291
  ].join("\n")
284
292
  }
@@ -62,11 +62,22 @@ export const Persisted = Context.Reference<boolean>("effect/cluster/ClusterSchem
62
62
  * The default value is `false`. When `true`, entity handling wraps server
63
63
  * writes with the configured storage transaction.
64
64
  *
65
+ * Persisted requests deliver terminal replies and mark requests processed only
66
+ * after commit or clean rollback. Success replies are saved in the transaction;
67
+ * failures are saved after rollback. If commit reports failure, a stored success
68
+ * reply is recovered. Otherwise, commit or rollback failures retry the request
69
+ * without delivering a reply. Handlers must tolerate retries.
70
+ *
65
71
  * **Gotchas**
66
72
  *
67
73
  * This annotation has transactional behavior only when the configured
68
74
  * `MessageStorage` implements it.
69
75
  *
76
+ * Stream chunks are saved and delivered immediately, even if the transaction
77
+ * later rolls back. A handler returning a `Deferred` commits before its reply
78
+ * is saved. Non-persisted requests save no reply; successes are delivered when
79
+ * the handler exits, and failures after the transaction exits.
80
+ *
70
81
  * @stability unstable
71
82
  * @category services
72
83
  * @since 4.0.0
@@ -95,6 +106,27 @@ export const Uninterruptible = Context.Reference<boolean | "client" | "server">(
95
106
  { defaultValue: constFalse }
96
107
  )
97
108
 
109
+ /**
110
+ * Annotation that interrupts a cluster request when entity termination starts,
111
+ * rather than waiting for the termination grace period to expire.
112
+ *
113
+ * **Details**
114
+ *
115
+ * The default value is `false`. Use for long-lived requests that cannot finish
116
+ * on their own. Persisted requests resume under the next owner without saving
117
+ * the interruption as a reply. Non-persisted requests receive an interrupt exit.
118
+ *
119
+ * Server-side `Uninterruptible` takes precedence over this annotation.
120
+ *
121
+ * @stability unstable
122
+ * @category services
123
+ * @since 4.0.0
124
+ */
125
+ export const InterruptOnTermination = Context.Reference<boolean>(
126
+ "effect/cluster/ClusterSchema/InterruptOnTermination",
127
+ { defaultValue: constFalse }
128
+ )
129
+
98
130
  /**
99
131
  * Returns whether the `Uninterruptible` annotation applies to server-side
100
132
  * request handling for the provided context.
@@ -15,7 +15,6 @@ import * as Effect from "../Effect.ts"
15
15
  import * as Exit from "../Exit.ts"
16
16
  import * as Fiber from "../Fiber.ts"
17
17
  import * as Headers from "../http/Headers.ts"
18
- import * as Latch from "../Latch.ts"
19
18
  import * as Layer from "../Layer.ts"
20
19
  import * as Option from "../Option.ts"
21
20
  import * as PrimaryKey from "../PrimaryKey.ts"
@@ -132,7 +131,6 @@ export const make = Effect.gen(function*() {
132
131
  readonly context: Context.Context<any>
133
132
  }>()
134
133
  const interruptedActivities = new Set<string>()
135
- const activityLatches = new Map<string, Latch.Latch>()
136
134
  const deferredState = WorkflowEngine.makeDeferredState()
137
135
  // Rebuilds share an activation; overlapping activations have separate results.
138
136
  // Weak keys do not retain scopes, but contexts captured by clients, activities,
@@ -476,23 +474,19 @@ export const make = Effect.gen(function*() {
476
474
  const activityId = `${executionId}/${payload.name}`
477
475
  const instance = WorkflowEngine.WorkflowInstance.initial(workflow, executionId)
478
476
  interruptedActivities.delete(activityId)
479
- return Effect.gen(function*() {
480
- let entry = activities.get(activityId)
481
- while (!entry) {
482
- const latch = Latch.makeUnsafe()
483
- activityLatches.set(activityId, latch)
484
- yield* latch.await
485
- entry = activities.get(activityId)
486
- }
487
- const context = entry.context.pipe(
488
- Context.add(WorkflowEngine.WorkflowInstance, instance),
489
- Context.add(CurrentActivationScope, activation),
490
- Context.add(Activity.CurrentAttempt, payload.attempt)
491
- )
492
- return yield* entry.activity.executeEncoded.pipe(
493
- Effect.provideContext(context)
494
- )
495
- }).pipe(
477
+ const entry = activities.get(activityId)
478
+ if (!entry) {
479
+ // Replay may never register a losing race activity. Answer it
480
+ // without waiting; a later registration can reset this reply.
481
+ return Effect.succeed(new Workflow.Suspended({})).pipe(Rpc.wrap({ fork: true }))
482
+ }
483
+ const context = entry.context.pipe(
484
+ Context.add(WorkflowEngine.WorkflowInstance, instance),
485
+ Context.add(CurrentActivationScope, activation),
486
+ Context.add(Activity.CurrentAttempt, payload.attempt)
487
+ )
488
+ return entry.activity.executeEncoded.pipe(
489
+ Effect.provideContext(context),
496
490
  Workflow.intoResult,
497
491
  Effect.catchCause((cause) => {
498
492
  // we only want to store interrupts as suspends when the
@@ -633,11 +627,6 @@ export const make = Effect.gen(function*() {
633
627
  while (true) {
634
628
  if (!activities.has(activityId)) {
635
629
  activities.set(activityId, { activity, context: services })
636
- const latch = activityLatches.get(activityId)
637
- if (latch) {
638
- yield* latch.open
639
- activityLatches.delete(activityId)
640
- }
641
630
  }
642
631
  const result = yield* Effect.orDie(
643
632
  client.activity({