effect-start 0.20.1 → 0.22.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 (105) hide show
  1. package/README.md +1 -4
  2. package/dist/Cookies.js +392 -0
  3. package/dist/FileSystem.js +131 -0
  4. package/dist/Socket.js +37 -0
  5. package/package.json +39 -40
  6. package/src/Commander.ts +73 -130
  7. package/src/ContentNegotiation.ts +68 -100
  8. package/src/Cookies.ts +408 -0
  9. package/src/Development.ts +48 -63
  10. package/src/Effectify.ts +222 -206
  11. package/src/Entity.ts +59 -86
  12. package/src/FilePathPattern.ts +5 -5
  13. package/src/FileRouter.ts +38 -63
  14. package/src/FileRouterCodegen.ts +64 -56
  15. package/src/FileSystem.ts +390 -0
  16. package/src/Http.ts +17 -50
  17. package/src/PathPattern.ts +33 -41
  18. package/src/PlatformError.ts +29 -50
  19. package/src/PlatformRuntime.ts +39 -47
  20. package/src/Route.ts +68 -187
  21. package/src/RouteBody.ts +45 -161
  22. package/src/RouteHook.ts +22 -45
  23. package/src/RouteHttp.ts +88 -142
  24. package/src/RouteHttpTracer.ts +25 -26
  25. package/src/RouteMount.ts +100 -238
  26. package/src/RouteSchema.ts +67 -201
  27. package/src/RouteSse.ts +28 -82
  28. package/src/RouteTree.ts +31 -79
  29. package/src/RouteTrie.ts +13 -32
  30. package/src/SchemaExtra.ts +3 -5
  31. package/src/Socket.ts +51 -0
  32. package/src/Start.ts +20 -21
  33. package/src/StreamExtra.ts +93 -96
  34. package/src/TuplePathPattern.ts +54 -43
  35. package/src/Unique.ts +9 -15
  36. package/src/Values.ts +26 -30
  37. package/src/bun/BunBundle.ts +27 -73
  38. package/src/bun/BunImportTrackerPlugin.ts +67 -65
  39. package/src/bun/BunRoute.ts +12 -31
  40. package/src/bun/BunRuntime.ts +3 -10
  41. package/src/bun/BunServer.ts +50 -60
  42. package/src/bun/BunVirtualFilesPlugin.ts +1 -4
  43. package/src/bun/_BunEnhancedResolve.ts +17 -42
  44. package/src/bun/_empty.html +0 -1
  45. package/src/bundler/Bundle.ts +20 -36
  46. package/src/bundler/BundleFiles.ts +36 -56
  47. package/src/client/Overlay.ts +1 -2
  48. package/src/client/ScrollState.ts +5 -9
  49. package/src/client/index.ts +10 -13
  50. package/src/datastar/actions/fetch.ts +29 -48
  51. package/src/datastar/actions/peek.ts +1 -5
  52. package/src/datastar/actions/setAll.ts +2 -2
  53. package/src/datastar/actions/toggleAll.ts +2 -2
  54. package/src/datastar/attributes/attr.ts +17 -18
  55. package/src/datastar/attributes/bind.ts +41 -61
  56. package/src/datastar/attributes/class.ts +2 -5
  57. package/src/datastar/attributes/computed.ts +2 -10
  58. package/src/datastar/attributes/effect.ts +1 -2
  59. package/src/datastar/attributes/indicator.ts +2 -8
  60. package/src/datastar/attributes/init.ts +2 -10
  61. package/src/datastar/attributes/jsonSignals.ts +1 -6
  62. package/src/datastar/attributes/on.ts +4 -13
  63. package/src/datastar/attributes/onIntersect.ts +10 -22
  64. package/src/datastar/attributes/onInterval.ts +2 -10
  65. package/src/datastar/attributes/onSignalPatch.ts +18 -28
  66. package/src/datastar/attributes/ref.ts +1 -2
  67. package/src/datastar/attributes/show.ts +1 -2
  68. package/src/datastar/attributes/signals.ts +1 -5
  69. package/src/datastar/attributes/style.ts +6 -12
  70. package/src/datastar/attributes/text.ts +1 -2
  71. package/src/datastar/engine.ts +102 -158
  72. package/src/datastar/index.ts +2 -2
  73. package/src/datastar/utils.ts +16 -51
  74. package/src/datastar/watchers/patchElements.ts +35 -93
  75. package/src/datastar/watchers/patchSignals.ts +1 -2
  76. package/src/experimental/EncryptedCookies.ts +81 -175
  77. package/src/experimental/index.ts +0 -1
  78. package/src/hyper/Hyper.ts +14 -33
  79. package/src/hyper/HyperHtml.ts +13 -10
  80. package/src/hyper/HyperNode.ts +2 -7
  81. package/src/hyper/HyperRoute.ts +2 -5
  82. package/src/hyper/jsx-runtime.ts +2 -10
  83. package/src/hyper/jsx.d.ts +171 -440
  84. package/src/lint/plugin.js +276 -0
  85. package/src/node/NodeFileSystem.ts +140 -202
  86. package/src/node/NodeUtils.ts +1 -3
  87. package/src/testing/TestLogger.ts +9 -22
  88. package/src/testing/index.ts +0 -1
  89. package/src/testing/utils.ts +30 -31
  90. package/src/x/cloudflare/CloudflareTunnel.ts +53 -65
  91. package/src/x/datastar/Datastar.ts +3 -10
  92. package/src/x/datastar/index.ts +1 -3
  93. package/src/x/datastar/jsx-datastar.d.ts +1 -4
  94. package/src/x/tailwind/TailwindPlugin.ts +119 -112
  95. package/src/x/tailwind/compile.ts +10 -33
  96. package/src/x/tailwind/plugin.ts +2 -2
  97. package/src/HttpAppExtra.ts +0 -478
  98. package/src/HttpUtils.ts +0 -17
  99. package/src/bun/BunPlatformHttpServer.ts +0 -88
  100. package/src/bun/BunServerRequest.ts +0 -396
  101. package/src/bundler/BundleHttp.ts +0 -259
  102. package/src/experimental/SseHttpResponse.ts +0 -55
  103. package/src/middlewares/BasicAuthMiddleware.ts +0 -36
  104. package/src/middlewares/index.ts +0 -1
  105. package/src/testing/TestHttpClient.ts +0 -148
@@ -0,0 +1,276 @@
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
+
6
+ export default {
7
+ meta: {
8
+ name: "effect-start",
9
+ version: "0.1.0",
10
+ },
11
+ rules: {
12
+ "prefer-namespace-import": {
13
+ meta: {
14
+ type: "suggestion",
15
+ docs: {
16
+ description:
17
+ "Enforce namespace imports for modules with capitalized base names or specific forced modules",
18
+ },
19
+ fixable: "code",
20
+ hasSuggestions: true,
21
+ schema: [],
22
+ messages: {
23
+ preferNamespace:
24
+ 'Use namespace import for module "{{source}}": import {{typePrefix}}* as {{baseName}} from "{{source}}"',
25
+ },
26
+ },
27
+ create(context) {
28
+ return {
29
+ ImportDeclaration(node) {
30
+ const source = node.source.value
31
+ if (typeof source !== "string") return
32
+
33
+ const baseName = getBaseName(source)
34
+ if (!baseName) return
35
+
36
+ const forced = forceNamespace.has(source)
37
+ if (!forced && !isCapitalized(baseName)) return
38
+
39
+ // Already a namespace import (with or without type-only)
40
+ if (
41
+ node.specifiers.length === 1 &&
42
+ node.specifiers[0].type === "ImportNamespaceSpecifier"
43
+ ) {
44
+ return
45
+ }
46
+
47
+ // Skip if there are no specifiers (side-effect import)
48
+ if (node.specifiers.length === 0) return
49
+
50
+ // Skip if it's only a default import (not applicable for forced modules)
51
+ if (!forced) {
52
+ const hasNamedImports = node.specifiers.some((s) => s.type === "ImportSpecifier")
53
+ if (!hasNamedImports) return
54
+ }
55
+
56
+ const typePrefix = node.importKind === "type" ? "type " : ""
57
+
58
+ context.report({
59
+ node,
60
+ messageId: "preferNamespace",
61
+ data: { source, baseName, typePrefix },
62
+ fix(fixer) {
63
+ return fixer.replaceText(
64
+ node,
65
+ `import ${typePrefix}* as ${baseName} from "${source}"`,
66
+ )
67
+ },
68
+ })
69
+ },
70
+ }
71
+ },
72
+ },
73
+
74
+ "test-assertion-newline": {
75
+ meta: {
76
+ type: "layout",
77
+ docs: {
78
+ description:
79
+ "Enforce newlines between chained test assertion methods (test.expect().toBe())",
80
+ },
81
+ fixable: "whitespace",
82
+ schema: [],
83
+ messages: {
84
+ requireNewline: "Each chained method in a test assertion should be on its own line",
85
+ requireBlankBefore:
86
+ "Test assertion should be preceded by an empty line (unless preceded by another assertion)",
87
+ requireBlankAfter:
88
+ "Test assertion should be followed by an empty line (unless followed by another assertion)",
89
+ },
90
+ },
91
+ create(context) {
92
+ const filename = context.filename || context.getFilename()
93
+ if (!filename.endsWith(".test.ts") && !filename.endsWith(".test.tsx")) {
94
+ return {}
95
+ }
96
+
97
+ /**
98
+ * Check if a call expression is rooted in test.expect or test.expectTypeOf
99
+ */
100
+ function isTestAssertionChain(node) {
101
+ let current = node
102
+
103
+ while (current) {
104
+ if (current.type === "CallExpression" && current.callee.type === "MemberExpression") {
105
+ const obj = current.callee.object
106
+
107
+ // Check for test.expect(...) or test.expectTypeOf(...)
108
+ if (
109
+ obj.type === "CallExpression" &&
110
+ obj.callee.type === "MemberExpression" &&
111
+ obj.callee.object.type === "Identifier" &&
112
+ obj.callee.object.name === "test" &&
113
+ obj.callee.property.type === "Identifier" &&
114
+ (obj.callee.property.name === "expect" ||
115
+ obj.callee.property.name === "expectTypeOf")
116
+ ) {
117
+ return true
118
+ }
119
+
120
+ // Direct: test.expect(...) or test.expectTypeOf(...)
121
+ if (
122
+ current.callee.object.type === "Identifier" &&
123
+ current.callee.object.name === "test" &&
124
+ current.callee.property.type === "Identifier" &&
125
+ (current.callee.property.name === "expect" ||
126
+ current.callee.property.name === "expectTypeOf")
127
+ ) {
128
+ return true
129
+ }
130
+
131
+ // Walk up the chain
132
+ current = current.callee.object
133
+ continue
134
+ }
135
+
136
+ // Handle non-call member access like .not
137
+ if (current.type === "MemberExpression") {
138
+ current = current.object
139
+ continue
140
+ }
141
+
142
+ break
143
+ }
144
+
145
+ return false
146
+ }
147
+
148
+ /**
149
+ * Collect all member expressions in a chained call.
150
+ */
151
+ function getChainedMembers(node) {
152
+ const members = []
153
+ let current = node
154
+
155
+ while (current.type === "CallExpression" && current.callee.type === "MemberExpression") {
156
+ members.push(current.callee)
157
+ current = current.callee.object
158
+ }
159
+
160
+ return members
161
+ }
162
+
163
+ function isAssertionStatement(stmt) {
164
+ return (
165
+ stmt &&
166
+ stmt.type === "ExpressionStatement" &&
167
+ stmt.expression.type === "CallExpression" &&
168
+ isTestAssertionChain(stmt.expression)
169
+ )
170
+ }
171
+
172
+ function checkChainedNewlines(node) {
173
+ const members = getChainedMembers(node.expression)
174
+
175
+ for (const member of members) {
176
+ const objectEndLine = member.object.loc.end.line
177
+ const propertyStartLine = member.property.loc.start.line
178
+
179
+ if (objectEndLine === propertyStartLine) {
180
+ const sourceCode = context.sourceCode || context.getSourceCode()
181
+ const dot = sourceCode.getTokenBefore(member.property)
182
+
183
+ if (dot && dot.value === ".") {
184
+ context.report({
185
+ node: member.property,
186
+ messageId: "requireNewline",
187
+ fix(fixer) {
188
+ const stmtStartCol = node.loc.start.column
189
+ const indent = " ".repeat(stmtStartCol + 2)
190
+ return fixer.replaceTextRange(
191
+ [member.object.range[1], member.property.range[0]],
192
+ "\n" + indent + ".",
193
+ )
194
+ },
195
+ })
196
+ }
197
+ }
198
+ }
199
+ }
200
+
201
+ function checkBlankLines(siblings) {
202
+ const sourceCode = context.sourceCode || context.getSourceCode()
203
+
204
+ for (let i = 0; i < siblings.length; i++) {
205
+ const node = siblings[i]
206
+ if (!isAssertionStatement(node)) continue
207
+
208
+ const prev = siblings[i - 1]
209
+ const next = siblings[i + 1]
210
+
211
+ if (prev && !isAssertionStatement(prev)) {
212
+ if (node.loc.start.line - prev.loc.end.line < 2) {
213
+ context.report({
214
+ node,
215
+ messageId: "requireBlankBefore",
216
+ fix(fixer) {
217
+ return fixer.insertTextAfter(sourceCode.getLastToken(prev), "\n")
218
+ },
219
+ })
220
+ }
221
+ }
222
+
223
+ if (next && !isAssertionStatement(next)) {
224
+ if (next.loc.start.line - node.loc.end.line < 2) {
225
+ context.report({
226
+ node,
227
+ messageId: "requireBlankAfter",
228
+ fix(fixer) {
229
+ return fixer.insertTextAfter(sourceCode.getLastToken(node), "\n")
230
+ },
231
+ })
232
+ }
233
+ }
234
+ }
235
+ }
236
+
237
+ return {
238
+ ExpressionStatement(node) {
239
+ if (node.expression.type !== "CallExpression") return
240
+ if (!isTestAssertionChain(node.expression)) return
241
+ checkChainedNewlines(node)
242
+ },
243
+ Program(node) {
244
+ checkBlankLines(node.body)
245
+ },
246
+ BlockStatement(node) {
247
+ checkBlankLines(node.body)
248
+ },
249
+ }
250
+ },
251
+ },
252
+ },
253
+ }
254
+
255
+ function getBaseName(source) {
256
+ // Handle node: and bun: protocols
257
+ if (source.startsWith("node:")) {
258
+ return source.slice(5)
259
+ }
260
+ if (source.startsWith("bun:")) {
261
+ return source.slice(4)
262
+ }
263
+
264
+ // Get last path segment
265
+ const segments = source.split("/")
266
+ let last = segments[segments.length - 1]
267
+
268
+ // Strip file extension (.ts, .tsx, .js, .jsx, .mjs, .cjs)
269
+ last = last.replace(/\.(ts|tsx|js|jsx|mjs|cjs)$/, "")
270
+
271
+ return last
272
+ }
273
+
274
+ function isCapitalized(name) {
275
+ return name.length > 0 && name[0] >= "A" && name[0] <= "Z"
276
+ }