effect-start 0.22.0 → 0.23.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 (313) hide show
  1. package/dist/BlobStore.d.ts +80 -0
  2. package/dist/BlobStore.js +19 -0
  3. package/dist/ChildProcess.d.ts +60 -0
  4. package/dist/ChildProcess.js +30 -0
  5. package/dist/Commander.d.ts +3 -6
  6. package/dist/Commander.js +6 -13
  7. package/dist/ContentNegotiation.d.ts +8 -9
  8. package/dist/ContentNegotiation.js +32 -37
  9. package/dist/Cookies.d.ts +47 -0
  10. package/dist/Cookies.js +273 -363
  11. package/dist/Development.d.ts +2 -2
  12. package/dist/Development.js +3 -4
  13. package/dist/Effectify.d.ts +1 -4
  14. package/dist/FilePathPattern.d.ts +3 -3
  15. package/dist/FileRouter.d.ts +5 -8
  16. package/dist/FileRouter.js +9 -10
  17. package/dist/FileRouterCodegen.d.ts +1 -1
  18. package/dist/FileRouterCodegen.js +33 -13
  19. package/dist/FileSystem.d.ts +158 -0
  20. package/dist/FileSystem.js +64 -125
  21. package/dist/Http.js +2 -6
  22. package/dist/PathPattern.d.ts +7 -7
  23. package/dist/PathPattern.js +1 -3
  24. package/dist/PlatformError.d.ts +24 -32
  25. package/dist/PlatformError.js +3 -21
  26. package/dist/PlatformRuntime.js +5 -10
  27. package/dist/Route.d.ts +14 -19
  28. package/dist/Route.js +8 -11
  29. package/dist/RouteBody.d.ts +6 -12
  30. package/dist/RouteBody.js +2 -2
  31. package/dist/RouteError.d.ts +98 -0
  32. package/dist/RouteError.js +55 -0
  33. package/dist/RouteHook.js +6 -11
  34. package/dist/RouteHttp.d.ts +3 -3
  35. package/dist/RouteHttp.js +27 -22
  36. package/dist/RouteMount.d.ts +16 -50
  37. package/dist/RouteMount.js +6 -20
  38. package/dist/RouteSchema.d.ts +22 -1
  39. package/dist/RouteSchema.js +33 -0
  40. package/dist/RouteSse.js +4 -10
  41. package/dist/RouteTree.d.ts +2 -1
  42. package/dist/RouteTree.js +17 -15
  43. package/dist/RouteTrie.d.ts +2 -2
  44. package/dist/RouteTrie.js +4 -9
  45. package/dist/SchemaExtra.d.ts +1 -1
  46. package/dist/Socket.d.ts +27 -0
  47. package/dist/Socket.js +20 -28
  48. package/dist/Sql.d.ts +34 -0
  49. package/dist/Sql.js +5 -0
  50. package/dist/SqlIntrospect.d.ts +91 -0
  51. package/dist/SqlIntrospect.js +466 -0
  52. package/dist/Start.d.ts +4 -6
  53. package/dist/Start.js +10 -2
  54. package/dist/StreamExtra.d.ts +1 -1
  55. package/dist/StreamExtra.js +9 -9
  56. package/dist/System.d.ts +7 -0
  57. package/dist/System.js +22 -0
  58. package/dist/TuplePathPattern.js +55 -50
  59. package/dist/Unique.js +7 -7
  60. package/dist/Values.d.ts +2 -1
  61. package/dist/Values.js +19 -13
  62. package/dist/bun/BunBlobStoreDisk.d.ts +6 -0
  63. package/dist/bun/BunBlobStoreDisk.js +116 -0
  64. package/dist/bun/BunBlobStoreS3.d.ts +11 -0
  65. package/dist/bun/BunBlobStoreS3.js +89 -0
  66. package/dist/bun/BunBlobWatcherDisk.d.ts +6 -0
  67. package/dist/bun/BunBlobWatcherDisk.js +60 -0
  68. package/dist/bun/BunBlobWatcherQueue.d.ts +6 -0
  69. package/dist/bun/BunBlobWatcherQueue.js +17 -0
  70. package/dist/bun/BunBundle.d.ts +5 -6
  71. package/dist/bun/BunBundle.js +7 -15
  72. package/dist/bun/BunChildProcessSpawner.d.ts +3 -0
  73. package/dist/bun/BunChildProcessSpawner.js +103 -0
  74. package/dist/bun/BunImportTrackerPlugin.d.ts +1 -1
  75. package/dist/bun/BunImportTrackerPlugin.js +3 -5
  76. package/dist/bun/BunRoute.d.ts +3 -2
  77. package/dist/bun/BunRoute.js +5 -7
  78. package/dist/bun/BunRuntime.js +1 -1
  79. package/dist/bun/BunServer.d.ts +11 -4
  80. package/dist/bun/BunServer.js +35 -11
  81. package/dist/bun/BunSql.d.ts +4 -0
  82. package/dist/bun/BunSql.js +81 -0
  83. package/dist/bun/_BunEnhancedResolve.d.ts +3 -3
  84. package/dist/bun/_BunEnhancedResolve.js +2 -4
  85. package/dist/bun/index.d.ts +1 -0
  86. package/dist/bun/index.js +1 -0
  87. package/dist/bundler/Bundle.d.ts +2 -1
  88. package/dist/bundler/Bundle.js +1 -1
  89. package/dist/bundler/BundleFiles.d.ts +5 -5
  90. package/dist/bundler/BundleFiles.js +10 -8
  91. package/dist/bundler/BundleRoute.d.ts +27 -0
  92. package/dist/bundler/BundleRoute.js +51 -0
  93. package/dist/client/ScrollState.js +2 -6
  94. package/dist/client/index.js +6 -8
  95. package/dist/console/Console.d.ts +6 -0
  96. package/dist/console/Console.js +26 -0
  97. package/dist/console/ConsoleErrors.d.ts +3 -0
  98. package/dist/console/ConsoleErrors.js +200 -0
  99. package/dist/console/ConsoleLogger.d.ts +3 -0
  100. package/dist/console/ConsoleLogger.js +47 -0
  101. package/dist/console/ConsoleMetrics.d.ts +3 -0
  102. package/dist/console/ConsoleMetrics.js +61 -0
  103. package/dist/console/ConsoleProcess.d.ts +3 -0
  104. package/dist/console/ConsoleProcess.js +49 -0
  105. package/dist/console/ConsoleStore.d.ts +144 -0
  106. package/dist/console/ConsoleStore.js +61 -0
  107. package/dist/console/ConsoleTracer.d.ts +3 -0
  108. package/dist/console/ConsoleTracer.js +94 -0
  109. package/dist/console/Simulation.d.ts +2 -0
  110. package/dist/console/Simulation.js +633 -0
  111. package/dist/console/index.d.ts +3 -0
  112. package/dist/console/index.js +3 -0
  113. package/dist/console/routes/errors/route.d.ts +10 -0
  114. package/dist/console/routes/errors/route.js +47 -0
  115. package/dist/console/routes/fiberDetail.d.ts +16 -0
  116. package/dist/console/routes/fiberDetail.js +38 -0
  117. package/dist/console/routes/fibers/route.d.ts +10 -0
  118. package/dist/console/routes/fibers/route.js +19 -0
  119. package/dist/console/routes/git/route.d.ts +11 -0
  120. package/dist/console/routes/git/route.js +33 -0
  121. package/dist/console/routes/layout.d.ts +9 -0
  122. package/dist/console/routes/layout.js +3 -0
  123. package/dist/console/routes/logs/route.d.ts +10 -0
  124. package/dist/console/routes/logs/route.js +32 -0
  125. package/dist/console/routes/metrics/route.d.ts +10 -0
  126. package/dist/console/routes/metrics/route.js +17 -0
  127. package/dist/console/routes/route.d.ts +6 -0
  128. package/dist/console/routes/route.js +5 -0
  129. package/dist/console/routes/routes/route.d.ts +6 -0
  130. package/dist/console/routes/routes/route.js +20 -0
  131. package/dist/console/routes/services/route.d.ts +6 -0
  132. package/dist/console/routes/services/route.js +12 -0
  133. package/dist/console/routes/system/route.d.ts +10 -0
  134. package/dist/console/routes/system/route.js +18 -0
  135. package/dist/console/routes/traceDetail.d.ts +16 -0
  136. package/dist/console/routes/traceDetail.js +14 -0
  137. package/dist/console/routes/traces/route.d.ts +10 -0
  138. package/dist/console/routes/traces/route.js +39 -0
  139. package/dist/console/routes/tree.d.ts +153 -0
  140. package/dist/console/routes/tree.js +29 -0
  141. package/dist/console/ui/Errors.d.ts +4 -0
  142. package/dist/console/ui/Errors.js +15 -0
  143. package/dist/console/ui/Fibers.d.ts +24 -0
  144. package/dist/console/ui/Fibers.js +121 -0
  145. package/dist/console/ui/Git.d.ts +20 -0
  146. package/dist/console/ui/Git.js +95 -0
  147. package/dist/console/ui/Logs.d.ts +4 -0
  148. package/dist/console/ui/Logs.js +25 -0
  149. package/dist/console/ui/Metrics.d.ts +4 -0
  150. package/dist/console/ui/Metrics.js +26 -0
  151. package/dist/console/ui/Routes.d.ts +8 -0
  152. package/dist/console/ui/Routes.js +70 -0
  153. package/dist/console/ui/Services.d.ts +10 -0
  154. package/dist/console/ui/Services.js +246 -0
  155. package/dist/console/ui/Shell.d.ts +10 -0
  156. package/dist/console/ui/Shell.js +7 -0
  157. package/dist/console/ui/System.d.ts +4 -0
  158. package/dist/console/ui/System.js +35 -0
  159. package/dist/console/ui/Traces.d.ts +12 -0
  160. package/dist/console/ui/Traces.js +179 -0
  161. package/dist/datastar/actions/fetch.d.ts +1 -1
  162. package/dist/datastar/actions/fetch.js +10 -18
  163. package/dist/datastar/actions/peek.js +1 -2
  164. package/dist/datastar/actions/setAll.js +1 -2
  165. package/dist/datastar/actions/toggleAll.js +1 -2
  166. package/dist/datastar/attributes/attr.js +1 -2
  167. package/dist/datastar/attributes/bind.js +10 -18
  168. package/dist/datastar/attributes/class.js +2 -5
  169. package/dist/datastar/attributes/computed.js +2 -3
  170. package/dist/datastar/attributes/effect.js +1 -2
  171. package/dist/datastar/attributes/indicator.js +2 -4
  172. package/dist/datastar/attributes/init.js +2 -3
  173. package/dist/datastar/attributes/jsonSignals.js +1 -2
  174. package/dist/datastar/attributes/on.js +41 -22
  175. package/dist/datastar/attributes/onIntersect.js +2 -3
  176. package/dist/datastar/attributes/onInterval.js +2 -3
  177. package/dist/datastar/attributes/onSignalPatch.js +2 -4
  178. package/dist/datastar/attributes/ref.js +1 -2
  179. package/dist/datastar/attributes/show.js +1 -2
  180. package/dist/datastar/attributes/signals.js +1 -2
  181. package/dist/datastar/attributes/style.js +6 -12
  182. package/dist/datastar/attributes/text.js +1 -2
  183. package/dist/datastar/engine.d.ts +13 -7
  184. package/dist/datastar/engine.js +76 -48
  185. package/dist/datastar/happydom.d.ts +1 -0
  186. package/dist/datastar/happydom.js +8 -0
  187. package/dist/datastar/index.d.ts +1 -1
  188. package/dist/datastar/index.js +1 -1
  189. package/dist/datastar/utils.js +4 -7
  190. package/dist/datastar/watchers/patchElements.js +24 -45
  191. package/dist/datastar/watchers/patchSignals.js +1 -2
  192. package/dist/experimental/EncryptedCookies.d.ts +2 -5
  193. package/dist/experimental/EncryptedCookies.js +17 -48
  194. package/dist/experimental/index.d.ts +0 -1
  195. package/dist/experimental/index.js +0 -1
  196. package/dist/hyper/Hyper.d.ts +2 -9
  197. package/dist/hyper/Hyper.js +1 -12
  198. package/dist/hyper/HyperHtml.d.ts +1 -1
  199. package/dist/hyper/HyperHtml.js +18 -12
  200. package/dist/hyper/HyperHtml.test.d.ts +1 -0
  201. package/dist/hyper/HyperHtml.test.js +197 -0
  202. package/dist/hyper/HyperRoute.test.js +14 -3
  203. package/dist/hyper/html.d.ts +11 -0
  204. package/dist/hyper/html.js +30 -0
  205. package/dist/hyper/index.d.ts +2 -0
  206. package/dist/hyper/index.js +1 -0
  207. package/dist/hyper/jsx-runtime.d.ts +1 -1
  208. package/dist/hyper/jsx-runtime.js +1 -1
  209. package/dist/index.d.ts +1 -0
  210. package/dist/index.js +1 -0
  211. package/dist/lint/plugin.d.ts +86 -0
  212. package/dist/lint/plugin.js +341 -0
  213. package/dist/node/NodeFileSystem.d.ts +2 -2
  214. package/dist/node/NodeFileSystem.js +4 -14
  215. package/dist/sql/bun/index.d.ts +3 -0
  216. package/dist/sql/bun/index.js +75 -0
  217. package/dist/sql/mssql/docker.d.ts +2 -0
  218. package/dist/sql/mssql/docker.js +67 -0
  219. package/dist/sql/mssql/index.d.ts +21 -0
  220. package/dist/sql/mssql/index.js +113 -0
  221. package/dist/testing/TestLogger.js +4 -1
  222. package/dist/testing/index.d.ts +0 -1
  223. package/dist/testing/index.js +0 -1
  224. package/dist/testing/utils.d.ts +3 -3
  225. package/dist/testing/utils.js +4 -4
  226. package/dist/x/cloudflare/CloudflareTunnel.d.ts +2 -5
  227. package/dist/x/cloudflare/CloudflareTunnel.js +14 -27
  228. package/dist/x/datastar/Datastar.d.ts +1 -1
  229. package/dist/x/datastar/Datastar.js +13 -12
  230. package/dist/x/datastar/index.d.ts +1 -2
  231. package/dist/x/datastar/index.js +1 -2
  232. package/dist/x/tailscale/TailscaleTunnel.d.ts +15 -0
  233. package/dist/x/tailscale/TailscaleTunnel.js +68 -0
  234. package/dist/x/tailscale/index.d.ts +1 -0
  235. package/dist/x/tailscale/index.js +1 -0
  236. package/dist/x/tailwind/TailwindPlugin.js +19 -19
  237. package/dist/x/tailwind/compile.d.ts +2 -2
  238. package/dist/x/tailwind/compile.js +2 -4
  239. package/package.json +22 -10
  240. package/src/ChildProcess.ts +145 -0
  241. package/src/PlatformError.ts +27 -50
  242. package/src/Route.ts +2 -2
  243. package/src/RouteError.ts +76 -0
  244. package/src/RouteHttp.ts +13 -5
  245. package/src/RouteSchema.ts +96 -1
  246. package/src/RouteTree.ts +12 -0
  247. package/src/Sql.ts +51 -0
  248. package/src/SqlIntrospect.ts +620 -0
  249. package/src/Start.ts +15 -3
  250. package/src/System.ts +43 -0
  251. package/src/Values.ts +7 -0
  252. package/src/bun/BunChildProcessSpawner.ts +143 -0
  253. package/src/bun/BunRoute.ts +5 -2
  254. package/src/bun/BunServer.ts +22 -1
  255. package/src/bun/index.ts +1 -0
  256. package/src/bundler/BundleRoute.ts +66 -0
  257. package/src/console/Console.ts +42 -0
  258. package/src/console/ConsoleErrors.ts +213 -0
  259. package/src/console/ConsoleLogger.ts +56 -0
  260. package/src/console/ConsoleMetrics.ts +72 -0
  261. package/src/console/ConsoleProcess.ts +59 -0
  262. package/src/console/ConsoleStore.ts +187 -0
  263. package/src/console/ConsoleTracer.ts +107 -0
  264. package/src/console/Simulation.ts +814 -0
  265. package/src/console/console.html +340 -0
  266. package/src/console/index.ts +3 -0
  267. package/src/console/routes/errors/route.tsx +97 -0
  268. package/src/console/routes/fiberDetail.tsx +54 -0
  269. package/src/console/routes/fibers/route.tsx +45 -0
  270. package/src/console/routes/git/route.tsx +64 -0
  271. package/src/console/routes/layout.tsx +4 -0
  272. package/src/console/routes/logs/route.tsx +77 -0
  273. package/src/console/routes/metrics/route.tsx +36 -0
  274. package/src/console/routes/route.tsx +8 -0
  275. package/src/console/routes/routes/route.tsx +30 -0
  276. package/src/console/routes/services/route.tsx +21 -0
  277. package/src/console/routes/system/route.tsx +43 -0
  278. package/src/console/routes/traceDetail.tsx +22 -0
  279. package/src/console/routes/traces/route.tsx +81 -0
  280. package/src/console/routes/tree.ts +30 -0
  281. package/src/console/ui/Errors.tsx +76 -0
  282. package/src/console/ui/Fibers.tsx +321 -0
  283. package/src/console/ui/Git.tsx +182 -0
  284. package/src/console/ui/Logs.tsx +46 -0
  285. package/src/console/ui/Metrics.tsx +78 -0
  286. package/src/console/ui/Routes.tsx +125 -0
  287. package/src/console/ui/Services.tsx +273 -0
  288. package/src/console/ui/Shell.tsx +62 -0
  289. package/src/console/ui/System.tsx +131 -0
  290. package/src/console/ui/Traces.tsx +426 -0
  291. package/src/datastar/README.md +6 -1
  292. package/src/datastar/actions/fetch.ts +0 -1
  293. package/src/datastar/attributes/on.ts +40 -20
  294. package/src/datastar/engine.ts +51 -0
  295. package/src/datastar/jsx.d.ts +79 -0
  296. package/src/hyper/Hyper.ts +1 -16
  297. package/src/hyper/HyperHtml.ts +6 -4
  298. package/src/hyper/HyperRoute.ts +2 -1
  299. package/src/hyper/html.ts +47 -0
  300. package/src/hyper/index.ts +2 -0
  301. package/src/hyper/jsx.d.ts +5 -3
  302. package/src/index.ts +1 -0
  303. package/src/lint/plugin.js +129 -0
  304. package/src/sql/bun/index.ts +147 -0
  305. package/src/sql/mssql/docker.ts +117 -0
  306. package/src/sql/mssql/index.ts +223 -0
  307. package/src/sql/mssql/mssql.d.ts +41 -0
  308. package/src/x/cloudflare/CloudflareTunnel.ts +8 -36
  309. package/src/x/tailscale/TailscaleTunnel.ts +113 -0
  310. package/src/x/tailscale/index.ts +1 -0
  311. package/src/x/datastar/Datastar.ts +0 -61
  312. package/src/x/datastar/index.ts +0 -2
  313. package/src/x/datastar/jsx-datastar.d.ts +0 -60
@@ -0,0 +1,341 @@
1
+ /**
2
+ * @see https://oxc.rs/docs/guide/usage/linter/js-plugins.html#using-js-plugins
3
+ */
4
+ const forceNamespace = new Set(["bun:test"]);
5
+ export default {
6
+ meta: {
7
+ name: "effect-start",
8
+ version: "0.1.0",
9
+ },
10
+ rules: {
11
+ "prefer-namespace-import": {
12
+ meta: {
13
+ type: "suggestion",
14
+ docs: {
15
+ description: "Enforce namespace imports for modules with capitalized base names or specific forced modules",
16
+ },
17
+ fixable: "code",
18
+ hasSuggestions: true,
19
+ schema: [],
20
+ messages: {
21
+ preferNamespace: 'Use namespace import for module "{{source}}": import {{typePrefix}}* as {{baseName}} from "{{source}}"',
22
+ },
23
+ },
24
+ create(context) {
25
+ return {
26
+ ImportDeclaration(node) {
27
+ const source = node.source.value;
28
+ if (typeof source !== "string")
29
+ return;
30
+ const baseName = getBaseName(source);
31
+ if (!baseName)
32
+ return;
33
+ const forced = forceNamespace.has(source);
34
+ if (!forced && !isCapitalized(baseName))
35
+ return;
36
+ // Already a namespace import (with or without type-only)
37
+ if (node.specifiers.length === 1 &&
38
+ node.specifiers[0].type === "ImportNamespaceSpecifier") {
39
+ return;
40
+ }
41
+ // Skip if there are no specifiers (side-effect import)
42
+ if (node.specifiers.length === 0)
43
+ return;
44
+ // Skip if it's only a default import (not applicable for forced modules)
45
+ if (!forced) {
46
+ const hasNamedImports = node.specifiers.some((s) => s.type === "ImportSpecifier");
47
+ if (!hasNamedImports)
48
+ return;
49
+ }
50
+ const typePrefix = node.importKind === "type" ? "type " : "";
51
+ context.report({
52
+ node,
53
+ messageId: "preferNamespace",
54
+ data: { source, baseName, typePrefix },
55
+ fix(fixer) {
56
+ return fixer.replaceText(node, `import ${typePrefix}* as ${baseName} from "${source}"`);
57
+ },
58
+ });
59
+ },
60
+ };
61
+ },
62
+ },
63
+ "test-space-around": {
64
+ meta: {
65
+ type: "layout",
66
+ docs: {
67
+ description: "Enforce blank lines around test calls (test.describe, test.it, etc.)",
68
+ },
69
+ fixable: "whitespace",
70
+ schema: [],
71
+ messages: {
72
+ requireBlankBefore: "Test call should be preceded by a blank line",
73
+ requireBlankAfter: "Test call should be followed by a blank line",
74
+ },
75
+ },
76
+ create(context) {
77
+ const filename = context.filename || context.getFilename();
78
+ if (!filename.endsWith(".test.ts") && !filename.endsWith(".test.tsx")) {
79
+ return {};
80
+ }
81
+ function isTestCall(node) {
82
+ return (node &&
83
+ node.type === "ExpressionStatement" &&
84
+ node.expression.type === "CallExpression" &&
85
+ node.expression.callee.type === "MemberExpression" &&
86
+ node.expression.callee.object.type === "Identifier" &&
87
+ node.expression.callee.object.name === "test");
88
+ }
89
+ function checkBlankLines(siblings) {
90
+ const sourceCode = context.sourceCode || context.getSourceCode();
91
+ for (let i = 0; i < siblings.length; i++) {
92
+ const node = siblings[i];
93
+ if (!isTestCall(node))
94
+ continue;
95
+ const prev = siblings[i - 1];
96
+ const next = siblings[i + 1];
97
+ if (prev) {
98
+ if (node.loc.start.line - prev.loc.end.line < 2) {
99
+ context.report({
100
+ node,
101
+ messageId: "requireBlankBefore",
102
+ fix(fixer) {
103
+ return fixer.insertTextAfter(sourceCode.getLastToken(prev), "\n");
104
+ },
105
+ });
106
+ }
107
+ }
108
+ if (next) {
109
+ if (next.loc.start.line - node.loc.end.line < 2) {
110
+ context.report({
111
+ node,
112
+ messageId: "requireBlankAfter",
113
+ fix(fixer) {
114
+ return fixer.insertTextAfter(sourceCode.getLastToken(node), "\n");
115
+ },
116
+ });
117
+ }
118
+ }
119
+ }
120
+ }
121
+ return {
122
+ Program(node) {
123
+ checkBlankLines(node.body);
124
+ },
125
+ BlockStatement(node) {
126
+ checkBlankLines(node.body);
127
+ },
128
+ };
129
+ },
130
+ },
131
+ // this doens't work reliably and may cause runtime errors
132
+ "export-default-before-functions": {
133
+ meta: {
134
+ type: "suggestion",
135
+ docs: {
136
+ description: "Enforce export default appears before any function declarations",
137
+ },
138
+ schema: [],
139
+ messages: {
140
+ defaultAfterFunction: "export default should appear before function declarations",
141
+ },
142
+ },
143
+ create(context) {
144
+ return {
145
+ Program(node) {
146
+ let seenFunction = false;
147
+ for (const stmt of node.body) {
148
+ if (!seenFunction && isFunction(stmt)) {
149
+ seenFunction = true;
150
+ }
151
+ if (seenFunction && stmt.type === "ExportDefaultDeclaration") {
152
+ context.report({
153
+ node: stmt,
154
+ messageId: "defaultAfterFunction",
155
+ });
156
+ }
157
+ }
158
+ },
159
+ };
160
+ },
161
+ },
162
+ "test-assertion-newline": {
163
+ meta: {
164
+ type: "layout",
165
+ docs: {
166
+ description: "Enforce newlines between chained test assertion methods (test.expect().toBe())",
167
+ },
168
+ fixable: "whitespace",
169
+ schema: [],
170
+ messages: {
171
+ requireNewline: "Each chained method in a test assertion should be on its own line",
172
+ requireBlankBefore: "Test assertion should be preceded by an empty line (unless preceded by another assertion)",
173
+ requireBlankAfter: "Test assertion should be followed by an empty line (unless followed by another assertion)",
174
+ },
175
+ },
176
+ create(context) {
177
+ const filename = context.filename || context.getFilename();
178
+ if (!filename.endsWith(".test.ts") && !filename.endsWith(".test.tsx")) {
179
+ return {};
180
+ }
181
+ /**
182
+ * Check if a call expression is rooted in test.expect or test.expectTypeOf
183
+ */
184
+ function isTestAssertionChain(node) {
185
+ let current = node;
186
+ while (current) {
187
+ if (current.type === "CallExpression" && current.callee.type === "MemberExpression") {
188
+ const obj = current.callee.object;
189
+ // Check for test.expect(...) or test.expectTypeOf(...)
190
+ if (obj.type === "CallExpression" &&
191
+ obj.callee.type === "MemberExpression" &&
192
+ obj.callee.object.type === "Identifier" &&
193
+ obj.callee.object.name === "test" &&
194
+ obj.callee.property.type === "Identifier" &&
195
+ (obj.callee.property.name === "expect" ||
196
+ obj.callee.property.name === "expectTypeOf")) {
197
+ return true;
198
+ }
199
+ // Direct: test.expect(...) or test.expectTypeOf(...)
200
+ if (current.callee.object.type === "Identifier" &&
201
+ current.callee.object.name === "test" &&
202
+ current.callee.property.type === "Identifier" &&
203
+ (current.callee.property.name === "expect" ||
204
+ current.callee.property.name === "expectTypeOf")) {
205
+ return true;
206
+ }
207
+ // Walk up the chain
208
+ current = current.callee.object;
209
+ continue;
210
+ }
211
+ // Handle non-call member access like .not
212
+ if (current.type === "MemberExpression") {
213
+ current = current.object;
214
+ continue;
215
+ }
216
+ break;
217
+ }
218
+ return false;
219
+ }
220
+ /**
221
+ * Collect all member expressions in a chained call.
222
+ */
223
+ function getChainedMembers(node) {
224
+ const members = [];
225
+ let current = node;
226
+ while (current.type === "CallExpression" && current.callee.type === "MemberExpression") {
227
+ members.push(current.callee);
228
+ current = current.callee.object;
229
+ }
230
+ return members;
231
+ }
232
+ function isAssertionStatement(stmt) {
233
+ return (stmt &&
234
+ stmt.type === "ExpressionStatement" &&
235
+ stmt.expression.type === "CallExpression" &&
236
+ isTestAssertionChain(stmt.expression));
237
+ }
238
+ function checkChainedNewlines(node) {
239
+ const members = getChainedMembers(node.expression);
240
+ for (const member of members) {
241
+ const objectEndLine = member.object.loc.end.line;
242
+ const propertyStartLine = member.property.loc.start.line;
243
+ if (objectEndLine === propertyStartLine) {
244
+ const sourceCode = context.sourceCode || context.getSourceCode();
245
+ const dot = sourceCode.getTokenBefore(member.property);
246
+ if (dot && dot.value === ".") {
247
+ context.report({
248
+ node: member.property,
249
+ messageId: "requireNewline",
250
+ fix(fixer) {
251
+ const stmtStartCol = node.loc.start.column;
252
+ const indent = " ".repeat(stmtStartCol + 2);
253
+ return fixer.replaceTextRange([member.object.range[1], member.property.range[0]], "\n" + indent + ".");
254
+ },
255
+ });
256
+ }
257
+ }
258
+ }
259
+ }
260
+ function checkBlankLines(siblings) {
261
+ const sourceCode = context.sourceCode || context.getSourceCode();
262
+ for (let i = 0; i < siblings.length; i++) {
263
+ const node = siblings[i];
264
+ if (!isAssertionStatement(node))
265
+ continue;
266
+ const prev = siblings[i - 1];
267
+ const next = siblings[i + 1];
268
+ if (prev && !isAssertionStatement(prev)) {
269
+ if (node.loc.start.line - prev.loc.end.line < 2) {
270
+ context.report({
271
+ node,
272
+ messageId: "requireBlankBefore",
273
+ fix(fixer) {
274
+ return fixer.insertTextAfter(sourceCode.getLastToken(prev), "\n");
275
+ },
276
+ });
277
+ }
278
+ }
279
+ if (next && !isAssertionStatement(next)) {
280
+ if (next.loc.start.line - node.loc.end.line < 2) {
281
+ context.report({
282
+ node,
283
+ messageId: "requireBlankAfter",
284
+ fix(fixer) {
285
+ return fixer.insertTextAfter(sourceCode.getLastToken(node), "\n");
286
+ },
287
+ });
288
+ }
289
+ }
290
+ }
291
+ }
292
+ return {
293
+ ExpressionStatement(node) {
294
+ if (node.expression.type !== "CallExpression")
295
+ return;
296
+ if (!isTestAssertionChain(node.expression))
297
+ return;
298
+ checkChainedNewlines(node);
299
+ },
300
+ Program(node) {
301
+ checkBlankLines(node.body);
302
+ },
303
+ BlockStatement(node) {
304
+ checkBlankLines(node.body);
305
+ },
306
+ };
307
+ },
308
+ },
309
+ },
310
+ };
311
+ function getBaseName(source) {
312
+ // Handle node: and bun: protocols
313
+ if (source.startsWith("node:")) {
314
+ return source.slice(5);
315
+ }
316
+ if (source.startsWith("bun:")) {
317
+ return source.slice(4);
318
+ }
319
+ // Get last path segment
320
+ const segments = source.split("/");
321
+ let last = segments[segments.length - 1];
322
+ // Strip file extension (.ts, .tsx, .js, .jsx, .mjs, .cjs)
323
+ last = last.replace(/\.(ts|tsx|js|jsx|mjs|cjs)$/, "");
324
+ return last;
325
+ }
326
+ function isCapitalized(name) {
327
+ return name.length > 0 && name[0] >= "A" && name[0] <= "Z";
328
+ }
329
+ function isFunction(stmt) {
330
+ const decl = stmt.type === "ExportNamedDeclaration" && stmt.declaration ? stmt.declaration : stmt;
331
+ if (decl.type === "FunctionDeclaration")
332
+ return true;
333
+ if (decl.type === "VariableDeclaration" &&
334
+ decl.declarations.length > 0 &&
335
+ decl.declarations[0].init &&
336
+ (decl.declarations[0].init.type === "ArrowFunctionExpression" ||
337
+ decl.declarations[0].init.type === "FunctionExpression")) {
338
+ return true;
339
+ }
340
+ return false;
341
+ }
@@ -1,7 +1,7 @@
1
- import * as FileSystem from "@effect/platform/FileSystem";
2
1
  import * as Layer from "effect/Layer";
2
+ import * as FileSystem from "../FileSystem.ts";
3
3
  import * as NFS from "node:fs";
4
4
  import * as PlatformError from "../PlatformError.ts";
5
5
  export declare const layer: Layer.Layer<FileSystem.FileSystem, never, never>;
6
- export { FileSystem, PlatformError as Error, };
6
+ export { PlatformError as Error };
7
7
  export declare function handleErrnoException(module: PlatformError.SystemError["module"], method: string): (err: NodeJS.ErrnoException, [path]: [path: NFS.PathLike | number, ...args: Array<any>]) => PlatformError.PlatformError;
@@ -1,11 +1,8 @@
1
- /*
2
- * Adapted from @effect/platform
3
- */
4
- import * as FileSystem from "@effect/platform/FileSystem";
5
1
  import * as Effect from "effect/Effect";
6
2
  import * as Function from "effect/Function";
7
3
  import * as Layer from "effect/Layer";
8
4
  import * as Option from "effect/Option";
5
+ import * as FileSystem from "../FileSystem.js";
9
6
  import * as Stream from "effect/Stream";
10
7
  import * as NCrypto from "node:crypto";
11
8
  import * as NFS from "node:fs";
@@ -66,9 +63,7 @@ const makeTempDirectoryFactory = (method) => {
66
63
  const nodeMkdtemp = Effectify.effectify(NFS.mkdtemp, handleErrnoException("FileSystem", method), handleBadArgument(method));
67
64
  return (options) => Effect.suspend(() => {
68
65
  const prefix = options?.prefix ?? "";
69
- const directory = typeof options?.directory === "string"
70
- ? NPath.join(options.directory, ".")
71
- : NOS.tmpdir();
66
+ const directory = typeof options?.directory === "string" ? NPath.join(options.directory, ".") : NOS.tmpdir();
72
67
  return nodeMkdtemp(prefix ? NPath.join(directory, prefix) : directory + "/");
73
68
  });
74
69
  };
@@ -374,13 +369,8 @@ const make = Effect.map(Effect.serviceOption(FileSystem.WatchBackend), (backend)
374
369
  },
375
370
  writeFile,
376
371
  }));
377
- export const layer = Layer.unwrapEffect(Effect
378
- .gen(function* () {
379
- const mod = yield* Effect.tryPromise(() => import("@effect/platform/FileSystem"));
380
- return Layer.effect(mod.FileSystem, make);
381
- })
382
- .pipe(Effect.catchAll(() => Effect.die(new globalThis.Error("@effect/platform is not installed")))));
383
- export { FileSystem, PlatformError as Error, };
372
+ export const layer = Layer.effect(FileSystem.FileSystem, make);
373
+ export { PlatformError as Error };
384
374
  export function handleErrnoException(module, method) {
385
375
  return function (err, [path]) {
386
376
  let reason = "Unknown";
@@ -0,0 +1,3 @@
1
+ import * as Layer from "effect/Layer";
2
+ import * as Sql from "../../Sql.ts";
3
+ export declare const layer: (config: ConstructorParameters<typeof Bun.SQL>[0]) => Layer.Layer<Sql.SqlClient, Sql.SqlError>;
@@ -0,0 +1,75 @@
1
+ import * as Effect from "effect/Effect";
2
+ import * as Exit from "effect/Exit";
3
+ import * as FiberRef from "effect/FiberRef";
4
+ import * as GlobalValue from "effect/GlobalValue";
5
+ import * as Layer from "effect/Layer";
6
+ import * as Option from "effect/Option";
7
+ import * as Sql from "../../Sql.js";
8
+ const errorCode = (error) => {
9
+ const e = error;
10
+ if (typeof e?.errno === "string")
11
+ return e.errno;
12
+ return e?.code ?? "UNKNOWN";
13
+ };
14
+ const wrapError = (error) => new Sql.SqlError({
15
+ code: errorCode(error),
16
+ message: error instanceof Error ? error.message : String(error),
17
+ cause: error,
18
+ });
19
+ const wrap = (fn) => Effect.tryPromise({ try: () => Promise.resolve(fn()), catch: wrapError });
20
+ const makeValues = (obj, ...columns) => {
21
+ const items = Array.isArray(obj) ? obj : [obj];
22
+ const cols = columns.length > 0 ? columns : Object.keys(items[0]);
23
+ return { value: items, columns: cols };
24
+ };
25
+ const currentTransaction = GlobalValue.globalValue(Symbol.for("effect-start/sql/bun/currentTransaction"), () => FiberRef.unsafeMake(Option.none()));
26
+ const makeRun = (bunSql) => (fn) => Effect.flatMap(FiberRef.get(currentTransaction), (txOpt) => wrap(() => fn(Option.isSome(txOpt) ? txOpt.value.conn : bunSql)));
27
+ const makeWithTransaction = (bunSql) => (self) => Effect.uninterruptibleMask((restore) => Effect.flatMap(FiberRef.get(currentTransaction), (txOpt) => {
28
+ if (Option.isSome(txOpt)) {
29
+ const { conn, depth } = txOpt.value;
30
+ const name = `sp_${depth}`;
31
+ return Effect.gen(function* () {
32
+ yield* wrap(() => conn.unsafe(`SAVEPOINT ${name}`));
33
+ const exit = yield* Effect.exit(restore(Effect.locally(self, currentTransaction, Option.some({ conn, depth: depth + 1 }))));
34
+ if (Exit.isSuccess(exit)) {
35
+ yield* wrap(() => conn.unsafe(`RELEASE SAVEPOINT ${name}`));
36
+ return exit.value;
37
+ }
38
+ yield* wrap(() => conn.unsafe(`ROLLBACK TO SAVEPOINT ${name}`)).pipe(Effect.orDie);
39
+ return yield* exit;
40
+ });
41
+ }
42
+ const runTx = (conn) => Effect.gen(function* () {
43
+ yield* wrap(() => conn.unsafe("BEGIN"));
44
+ const exit = yield* Effect.exit(restore(Effect.locally(self, currentTransaction, Option.some({ conn, depth: 1 }))));
45
+ if (Exit.isSuccess(exit)) {
46
+ yield* wrap(() => conn.unsafe("COMMIT"));
47
+ return exit.value;
48
+ }
49
+ yield* wrap(() => conn.unsafe("ROLLBACK")).pipe(Effect.orDie);
50
+ return yield* exit;
51
+ });
52
+ return Effect.matchEffect(wrap(() => bunSql.reserve()), {
53
+ onFailure: () => runTx(bunSql),
54
+ onSuccess: (reserved) => Effect.ensuring(runTx(reserved), Effect.sync(() => reserved.release())),
55
+ });
56
+ }));
57
+ export const layer = (config) => Layer.scoped(Sql.SqlClient, Effect.acquireRelease(Effect.try({
58
+ try: () => {
59
+ const bunSql = new Bun.SQL(config);
60
+ const run = makeRun(bunSql);
61
+ const use = (fn) => Effect.tryPromise({ try: () => Promise.resolve(fn(bunSql)), catch: wrapError });
62
+ return Object.assign((strings, ...values) => run((conn) => conn(strings, ...values)), {
63
+ unsafe: (query, values) => run((conn) => conn.unsafe(query, values)),
64
+ values: makeValues,
65
+ withTransaction: makeWithTransaction(bunSql),
66
+ reserve: Effect.acquireRelease(wrap(() => bunSql.reserve()), (reserved) => Effect.sync(() => reserved.release())).pipe(Effect.map((reserved) => Object.assign((strings, ...values) => wrap(() => reserved(strings, ...values)), {
67
+ unsafe: (query, values) => wrap(() => reserved.unsafe(query, values)),
68
+ values: makeValues,
69
+ }))),
70
+ close: (options) => use((bunSql) => bunSql.close(options)),
71
+ use,
72
+ });
73
+ },
74
+ catch: wrapError,
75
+ }), (client) => client.close().pipe(Effect.orDie)));
@@ -0,0 +1,2 @@
1
+ export declare const start: () => Promise<void>;
2
+ export declare const stop: () => Promise<void>;
@@ -0,0 +1,67 @@
1
+ import * as Effect from "effect/Effect";
2
+ import * as Stream from "effect/Stream";
3
+ import * as System from "../../System.js";
4
+ import * as BunChildProcessSpawner from "../../bun/BunChildProcessSpawner.js";
5
+ const PORT = 1433;
6
+ const PASSWORD = "TestPass123";
7
+ const CONTAINER = "effect-start-mssql";
8
+ const exec = (...args) => Effect.scoped(Effect.gen(function* () {
9
+ const handle = yield* System.spawn("docker", args, {
10
+ stdout: "ignore",
11
+ stderr: "inherit",
12
+ });
13
+ return yield* handle.exitCode;
14
+ }));
15
+ const execStdout = (...args) => Effect.scoped(Effect.gen(function* () {
16
+ const handle = yield* System.spawn("docker", args, {
17
+ stdout: "pipe",
18
+ stderr: "inherit",
19
+ });
20
+ const [stdout] = yield* Effect.all([handle.stdout.pipe(Stream.decodeText("utf-8"), Stream.mkString), handle.exitCode], { concurrency: 2 });
21
+ return stdout;
22
+ }));
23
+ const containerRunning = execStdout("ps", "-q", "-f", `name=${CONTAINER}`).pipe(Effect.map((stdout) => stdout.trim().length > 0));
24
+ const removeContainer = exec("rm", "-f", CONTAINER).pipe(Effect.ignore);
25
+ const loadMssql = () => import("mssql");
26
+ const canConnect = Effect.tryPromise({
27
+ try: async () => {
28
+ const { ConnectionPool } = await loadMssql();
29
+ const pool = new ConnectionPool({
30
+ server: "localhost",
31
+ user: "sa",
32
+ password: PASSWORD,
33
+ port: PORT,
34
+ options: { encrypt: true, trustServerCertificate: true, connectTimeout: 3000 },
35
+ });
36
+ await pool.connect();
37
+ await pool.close();
38
+ return true;
39
+ },
40
+ catch: () => false,
41
+ }).pipe(Effect.orElseSucceed(() => false));
42
+ const waitReady = Effect.gen(function* () {
43
+ const deadline = Date.now() + 60_000;
44
+ while (Date.now() < deadline) {
45
+ if (yield* canConnect)
46
+ return;
47
+ yield* Effect.sleep("2 seconds");
48
+ }
49
+ return yield* Effect.fail(new Error("Timed out waiting for MSSQL"));
50
+ });
51
+ const program = Effect.gen(function* () {
52
+ if (yield* containerRunning) {
53
+ yield* Effect.log("MSSQL container already running");
54
+ return;
55
+ }
56
+ yield* removeContainer;
57
+ yield* Effect.log("Starting MSSQL container...");
58
+ const code = yield* exec("run", "-d", "--name", CONTAINER, "-p", `${PORT}:1433`, "-e", "ACCEPT_EULA=Y", "-e", `MSSQL_SA_PASSWORD=${PASSWORD}`, "mcr.microsoft.com/azure-sql-edge");
59
+ if (code !== 0) {
60
+ return yield* Effect.fail(new Error(`docker run exited with code ${code}`));
61
+ }
62
+ yield* waitReady;
63
+ yield* Effect.log("MSSQL ready");
64
+ });
65
+ const run = (effect) => Effect.runPromise(Effect.provide(effect, BunChildProcessSpawner.layer));
66
+ export const start = () => run(program);
67
+ export const stop = () => run(removeContainer);
@@ -0,0 +1,21 @@
1
+ import * as Layer from "effect/Layer";
2
+ import * as Sql from "../../Sql.ts";
3
+ export interface MssqlConfig {
4
+ readonly server: string;
5
+ readonly database?: string;
6
+ readonly user?: string;
7
+ readonly password?: string;
8
+ readonly port?: number;
9
+ readonly pool?: {
10
+ readonly max?: number;
11
+ readonly min?: number;
12
+ readonly idleTimeoutMillis?: number;
13
+ };
14
+ readonly options?: {
15
+ readonly encrypt?: boolean;
16
+ readonly trustServerCertificate?: boolean;
17
+ readonly requestTimeout?: number;
18
+ readonly connectionTimeout?: number;
19
+ };
20
+ }
21
+ export declare const layer: (config: MssqlConfig) => Layer.Layer<Sql.SqlClient, Sql.SqlError>;