effect-start 0.25.0 → 0.26.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 (117) hide show
  1. package/package.json +18 -86
  2. package/dist/ChildProcess.js +0 -42
  3. package/dist/Commander.js +0 -410
  4. package/dist/ContentNegotiation.js +0 -465
  5. package/dist/Cookies.js +0 -371
  6. package/dist/Development.js +0 -94
  7. package/dist/Effectify.js +0 -27
  8. package/dist/Entity.js +0 -289
  9. package/dist/Fetch.js +0 -192
  10. package/dist/FilePathPattern.js +0 -97
  11. package/dist/FileRouter.js +0 -204
  12. package/dist/FileRouterCodegen.js +0 -298
  13. package/dist/FileSystem.js +0 -132
  14. package/dist/Http.js +0 -107
  15. package/dist/PathPattern.js +0 -451
  16. package/dist/PlatformError.js +0 -40
  17. package/dist/PlatformRuntime.js +0 -71
  18. package/dist/Route.js +0 -143
  19. package/dist/RouteBody.js +0 -92
  20. package/dist/RouteError.js +0 -76
  21. package/dist/RouteHook.js +0 -64
  22. package/dist/RouteHttp.js +0 -367
  23. package/dist/RouteHttpTracer.js +0 -90
  24. package/dist/RouteMount.js +0 -86
  25. package/dist/RouteSchema.js +0 -271
  26. package/dist/RouteSse.js +0 -94
  27. package/dist/RouteTree.js +0 -119
  28. package/dist/RouteTrie.js +0 -179
  29. package/dist/SchemaExtra.js +0 -99
  30. package/dist/Socket.js +0 -40
  31. package/dist/SqlIntrospect.js +0 -515
  32. package/dist/Start.js +0 -79
  33. package/dist/StartApp.js +0 -3
  34. package/dist/StreamExtra.js +0 -135
  35. package/dist/System.js +0 -38
  36. package/dist/TuplePathPattern.js +0 -74
  37. package/dist/Unique.js +0 -226
  38. package/dist/Values.js +0 -52
  39. package/dist/bun/BunBundle.js +0 -186
  40. package/dist/bun/BunChildProcessSpawner.js +0 -142
  41. package/dist/bun/BunImportTrackerPlugin.js +0 -91
  42. package/dist/bun/BunRoute.js +0 -157
  43. package/dist/bun/BunRuntime.js +0 -41
  44. package/dist/bun/BunServer.js +0 -285
  45. package/dist/bun/BunVirtualFilesPlugin.js +0 -54
  46. package/dist/bun/_BunEnhancedResolve.js +0 -127
  47. package/dist/bun/index.js +0 -5
  48. package/dist/bundler/Bundle.js +0 -92
  49. package/dist/bundler/BundleFiles.js +0 -154
  50. package/dist/bundler/BundleRoute.js +0 -62
  51. package/dist/client/Overlay.js +0 -33
  52. package/dist/client/ScrollState.js +0 -106
  53. package/dist/client/index.js +0 -97
  54. package/dist/console/Console.js +0 -42
  55. package/dist/console/ConsoleErrors.js +0 -211
  56. package/dist/console/ConsoleLogger.js +0 -56
  57. package/dist/console/ConsoleMetrics.js +0 -72
  58. package/dist/console/ConsoleProcess.js +0 -59
  59. package/dist/console/ConsoleStore.js +0 -72
  60. package/dist/console/ConsoleTracer.js +0 -107
  61. package/dist/console/Simulation.js +0 -784
  62. package/dist/console/index.js +0 -3
  63. package/dist/console/routes/tree.js +0 -30
  64. package/dist/datastar/actions/fetch.js +0 -536
  65. package/dist/datastar/actions/peek.js +0 -13
  66. package/dist/datastar/actions/setAll.js +0 -19
  67. package/dist/datastar/actions/toggleAll.js +0 -19
  68. package/dist/datastar/attributes/attr.js +0 -49
  69. package/dist/datastar/attributes/bind.js +0 -194
  70. package/dist/datastar/attributes/class.js +0 -54
  71. package/dist/datastar/attributes/computed.js +0 -25
  72. package/dist/datastar/attributes/effect.js +0 -10
  73. package/dist/datastar/attributes/indicator.js +0 -33
  74. package/dist/datastar/attributes/init.js +0 -27
  75. package/dist/datastar/attributes/jsonSignals.js +0 -33
  76. package/dist/datastar/attributes/on.js +0 -81
  77. package/dist/datastar/attributes/onIntersect.js +0 -53
  78. package/dist/datastar/attributes/onInterval.js +0 -31
  79. package/dist/datastar/attributes/onSignalPatch.js +0 -51
  80. package/dist/datastar/attributes/ref.js +0 -11
  81. package/dist/datastar/attributes/show.js +0 -32
  82. package/dist/datastar/attributes/signals.js +0 -18
  83. package/dist/datastar/attributes/style.js +0 -57
  84. package/dist/datastar/attributes/text.js +0 -29
  85. package/dist/datastar/engine.js +0 -1145
  86. package/dist/datastar/index.js +0 -25
  87. package/dist/datastar/utils.js +0 -250
  88. package/dist/datastar/watchers/patchElements.js +0 -486
  89. package/dist/datastar/watchers/patchSignals.js +0 -14
  90. package/dist/experimental/EncryptedCookies.js +0 -328
  91. package/dist/experimental/index.js +0 -1
  92. package/dist/hyper/Hyper.js +0 -28
  93. package/dist/hyper/HyperHtml.js +0 -165
  94. package/dist/hyper/HyperNode.js +0 -13
  95. package/dist/hyper/HyperRoute.js +0 -45
  96. package/dist/hyper/html.js +0 -30
  97. package/dist/hyper/index.js +0 -5
  98. package/dist/hyper/jsx-runtime.js +0 -14
  99. package/dist/index.js +0 -8
  100. package/dist/node/NodeFileSystem.js +0 -675
  101. package/dist/node/NodeUtils.js +0 -23
  102. package/dist/sql/Sql.js +0 -8
  103. package/dist/sql/bun/index.js +0 -142
  104. package/dist/sql/index.js +0 -1
  105. package/dist/sql/libsql/index.js +0 -156
  106. package/dist/sql/mssql/docker.js +0 -110
  107. package/dist/sql/mssql/index.js +0 -194
  108. package/dist/testing/TestLogger.js +0 -42
  109. package/dist/testing/index.js +0 -2
  110. package/dist/testing/utils.js +0 -61
  111. package/dist/x/cloudflare/CloudflareTunnel.js +0 -63
  112. package/dist/x/cloudflare/index.js +0 -1
  113. package/dist/x/tailscale/TailscaleTunnel.js +0 -94
  114. package/dist/x/tailscale/index.js +0 -1
  115. package/dist/x/tailwind/TailwindPlugin.js +0 -294
  116. package/dist/x/tailwind/compile.js +0 -210
  117. package/dist/x/tailwind/plugin.js +0 -17
@@ -1,154 +0,0 @@
1
- import * as FileSystem from "../FileSystem.js"
2
- import * as Array from "effect/Array"
3
- import * as Effect from "effect/Effect"
4
- import * as Function from "effect/Function"
5
- import * as Iterable from "effect/Iterable"
6
- import * as Record from "effect/Record"
7
- import * as S from "effect/Schema"
8
- import * as Bundle from "./Bundle.js"
9
-
10
- /**
11
- * Exports a bundle to a file system under specified directory.
12
- */
13
- export const toFiles = (context, outDir) => {
14
- return Effect.gen(function* () {
15
- const fs = yield* FileSystem.FileSystem
16
- const manifest = {
17
- entrypoints: context.entrypoints,
18
- artifacts: context.artifacts,
19
- }
20
-
21
- const normalizedOutDir = outDir.replace(/\/$/, "")
22
-
23
- const bundleArtifacts = Function.pipe(
24
- manifest.artifacts,
25
- Array.map((artifact) => /** @type {const} */ [artifact.path, context.getArtifact(artifact.path)]),
26
- Record.fromEntries,
27
- )
28
- const extraArtifacts = {
29
- "manifest.json": new Blob([JSON.stringify(manifest, undefined, 2)], {
30
- type: "application/json",
31
- }),
32
- }
33
-
34
- const allArtifacts = {
35
- ...bundleArtifacts,
36
- ...extraArtifacts,
37
- }
38
-
39
- const existingOutDirFiles = yield* fs
40
- .readDirectory(normalizedOutDir)
41
- .pipe(Effect.catchAll(() => Effect.succeed(null)))
42
-
43
- // check if the output directory is empty. if it contains previous build,
44
- // remove it. Otherwise fail.
45
- if (existingOutDirFiles && existingOutDirFiles.length > 0) {
46
- if (existingOutDirFiles.includes("manifest.json")) {
47
- yield* Effect.logWarning("Output directory seems to contain previous build. Overwriting...")
48
-
49
- yield* fs.remove(normalizedOutDir, {
50
- recursive: true,
51
- })
52
- } else {
53
- return yield* Effect.fail(
54
- new Bundle.BundleError({
55
- message: "Output directory is not empty",
56
- }),
57
- )
58
- }
59
- }
60
-
61
- yield* fs.makeDirectory(normalizedOutDir, {
62
- recursive: true,
63
- })
64
-
65
- // write all artifacts to files
66
- yield* Effect.all(
67
- Function.pipe(
68
- allArtifacts,
69
- Record.toEntries,
70
- Array.map(([p, b]) =>
71
- Function.pipe(
72
- Effect.tryPromise({
73
- try: () => b.arrayBuffer(),
74
- catch: (e) =>
75
- new Bundle.BundleError({
76
- message: "Failed to read an artifact as a buffer",
77
- cause: e,
78
- }),
79
- }),
80
- Effect.andThen((b) => fs.writeFile(`${normalizedOutDir}/${p}`, new Uint8Array(b))),
81
- ),
82
- ),
83
- ),
84
- { concurrency: 16 },
85
- )
86
- })
87
- }
88
-
89
- /**
90
- * Loads a bundle from a directory and returns a Bundle.BundleContext.
91
- * Expects the directory to contain a manifest.json file and all the artifacts
92
- * referenced in the manifest.
93
- */
94
- export const fromFiles = (
95
- directory,
96
- ) => {
97
- return Effect.gen(function* () {
98
- const fs = yield* FileSystem.FileSystem
99
- const normalizedDir = directory.replace(/\/$/, "")
100
- const manifest = yield* Function.pipe(
101
- fs.readFileString(`${normalizedDir}/manifest.json`),
102
- Effect.andThen((v) => JSON.parse(v)),
103
- Effect.andThen(S.decodeUnknownSync(Bundle.BundleManifestSchema)),
104
- Effect.catchAll((e) =>
105
- Effect.fail(
106
- new Bundle.BundleError({
107
- message: `Failed to read manifest.json from ${normalizedDir}`,
108
- cause: e,
109
- }),
110
- ),
111
- ),
112
- )
113
- const artifactPaths = Array.map(manifest.artifacts, (a) => a.path)
114
- const artifactBlobs = yield* Function.pipe(
115
- artifactPaths,
116
- Iterable.map((path) => fs.readFile(`${normalizedDir}/${path}`)),
117
- Effect.all,
118
- Effect.catchAll(
119
- (e) =>
120
- new Bundle.BundleError({
121
- message: `Failed to read an artifact from ${normalizedDir}`,
122
- cause: e,
123
- }),
124
- ),
125
- Effect.andThen(
126
- Iterable.map(
127
- (v, i) =>
128
- new Blob([v.slice(0)], {
129
- type: manifest.artifacts[i].type,
130
- }),
131
- ),
132
- ),
133
- )
134
- const artifactsRecord = Function.pipe(
135
- Iterable.zip(artifactPaths, artifactBlobs),
136
- Record.fromEntries,
137
- )
138
-
139
- const bundleContext = {
140
- ...manifest,
141
- // TODO: support fullpath file:// urls
142
- // this will require having an access to base path of a build
143
- // and maybe problematic because bundlers transform urls on build
144
- resolve: (url) => {
145
- return manifest.entrypoints[url] ?? null
146
- },
147
- getArtifact: (path) => {
148
- return artifactsRecord[path] ?? null
149
- },
150
- }
151
-
152
- return bundleContext
153
- })
154
- }
@@ -1,62 +0,0 @@
1
- import * as Effect from "effect/Effect"
2
- import * as Layer from "effect/Layer"
3
- import * as Option from "effect/Option"
4
- import * as Entity from "../Entity.js"
5
- import * as PathPattern from "../PathPattern.js"
6
- import * as Route from "../Route.js"
7
- import * as RouteTree from "../RouteTree.js"
8
- import * as Values from "../Values.js"
9
- import * as Bundle from "./Bundle.js"
10
-
11
- /**
12
- * Creates a GET route that serves bundle artifacts.
13
- * Mount at a path with a wildcard param (any name works).
14
- *
15
- * ```ts
16
- * RouteTree.make({
17
- * "/_bundle/:path+": BundleRoute.make(Bundle.ClientBundle),
18
- * })
19
- * ```
20
- */
21
- export const make = (tag) =>
22
- Route.get(
23
- Route.render(function* (ctx) {
24
- const bundle = yield* tag
25
- const url = new URL(ctx.request.url)
26
- const mountPath = (ctx).path ?? "/"
27
- const params = PathPattern.match(mountPath, url.pathname)
28
- const artifactPath = params ? Values.firstValue(params) : undefined
29
- if (!artifactPath) {
30
- return Entity.make(new Uint8Array(0), { status: 404 })
31
- }
32
- const blob = bundle.getArtifact(artifactPath)
33
- if (!blob) {
34
- return Entity.make(new Uint8Array(0), { status: 404 })
35
- }
36
- const bytes = new Uint8Array(yield* Effect.promise(() => blob.arrayBuffer()))
37
- return Entity.make(bytes, {
38
- headers: {
39
- "content-type": blob.type || "application/octet-stream",
40
- "cache-control": "public, max-age=31536000, immutable",
41
- },
42
- })
43
- }),
44
- )
45
-
46
- export const client = () => make(Bundle.ClientBundle)
47
-
48
- export const layer = (options) =>
49
- Layer.effect(
50
- Route.Routes,
51
- Effect.gen(function* () {
52
- const existing = yield* Effect.serviceOption(Route.Routes).pipe(
53
- Effect.andThen(Option.getOrUndefined),
54
- )
55
- const path = options?.path ?? "/_bundle/:path+"
56
- const bundleTree = Route.tree({
57
- [path]: make(options?.bundle ?? Bundle.ClientBundle),
58
- })
59
- if (!existing) return bundleTree
60
- return RouteTree.merge(existing, bundleTree)
61
- }),
62
- )
@@ -1,33 +0,0 @@
1
- const OVERLAY_ID = "_bundler_error_overlay"
2
-
3
- export function getOverlay() {
4
- let overlay = document.getElementById(OVERLAY_ID)
5
- if (!overlay) {
6
- overlay = document.createElement("pre")
7
- overlay.id = OVERLAY_ID
8
- Object.assign(overlay.style, {
9
- position: "fixed",
10
- top: "0",
11
- left: "0",
12
- right: "0",
13
- maxHeight: "40%",
14
- overflowY: "auto",
15
- margin: "0",
16
- padding: "4px",
17
- background: "black",
18
- color: "red",
19
- fontFamily: "monospace",
20
- zIndex: "2147483647",
21
- whiteSpace: "pre-wrap",
22
- })
23
- document.body.appendChild(overlay)
24
- }
25
- return overlay
26
- }
27
-
28
- export function showBuildError(message) {
29
- const overlay = getOverlay()
30
- const atBottom = overlay.scrollTop + overlay.clientHeight >= overlay.scrollHeight - 1
31
- overlay.textContent += message + "\n"
32
- if (atBottom) overlay.scrollTop = overlay.scrollHeight
33
- }
@@ -1,106 +0,0 @@
1
- const ScrollKey = "_BUNDLER_SCROLL"
2
-
3
- /**
4
- * Persist current scroll state to session storage.
5
- * Scroll state is saved relatively to visible elements.
6
- */
7
- export function persist() {
8
- const anchors = []
9
- const step = window.innerHeight / 4
10
-
11
- for (let i = 1; i <= 3; i++) {
12
- const y = step * i
13
- const element = document.elementFromPoint(0, y)
14
- if (!element) continue
15
- const target = element.id ? element : (element.closest("[id]") ?? element)
16
-
17
- anchors.push({
18
- selector: selectorFromElement(target),
19
- offset: target.getBoundingClientRect().top,
20
- })
21
- }
22
-
23
- const state = {
24
- anchors,
25
- scrollY: window.scrollY,
26
- }
27
-
28
- sessionStorage.setItem(ScrollKey, JSON.stringify(state))
29
- }
30
-
31
- export function restore() {
32
- const timeout = 3000
33
- const tick = 50
34
- const raw = sessionStorage.getItem(ScrollKey)
35
- if (!raw) return
36
-
37
- sessionStorage.removeItem(ScrollKey)
38
-
39
- const state = JSON.parse(raw)
40
-
41
- const apply = () => {
42
- for (const anchor of state.anchors) {
43
- const element = document.querySelector(anchor.selector)
44
- if (element) {
45
- const rect = element.getBoundingClientRect()
46
- const top = window.scrollY + rect.top - anchor.offset
47
- window.scrollTo({
48
- top,
49
- })
50
- return
51
- }
52
- }
53
-
54
- window.scrollTo({
55
- top: state.scrollY,
56
- })
57
- }
58
-
59
- let observer
60
- let stableTimer
61
- const deadline = setTimeout(() => {
62
- observer.disconnect()
63
- if (stableTimer) clearTimeout(stableTimer)
64
- apply()
65
- }, timeout)
66
-
67
- observer = new MutationObserver(() => {
68
- if (stableTimer) clearTimeout(stableTimer)
69
- stableTimer = setTimeout(() => {
70
- observer.disconnect()
71
- clearTimeout(deadline)
72
- apply()
73
- }, tick)
74
- })
75
-
76
- observer.observe(document.body, {
77
- subtree: true,
78
- childList: true,
79
- attributes: true,
80
- characterData: true,
81
- })
82
-
83
- stableTimer = setTimeout(() => {
84
- observer.disconnect()
85
- clearTimeout(deadline)
86
- apply()
87
- }, tick)
88
- }
89
-
90
- function selectorFromElement(element) {
91
- if (element.id) {
92
- return `#${CSS.escape(element.id)}`
93
- }
94
- const parts = []
95
- let current = element
96
-
97
- while (current && current !== document.body) {
98
- const parent = current.parentElement
99
- if (!parent) break
100
- const index = Array.from(parent.children).indexOf(current) + 1
101
- parts.unshift(`${current.tagName.toLowerCase()}:nth-child(${index})`)
102
- current = parent
103
- }
104
-
105
- return parts.join(" > ")
106
- }
@@ -1,97 +0,0 @@
1
- /**
2
- * This module is intended to be imported in a browser bundle in a development.
3
- * It is responsible for live reloading the page when bundle changes.
4
- * When NODE_ENV=production, it does nothing.
5
- */
6
-
7
- /// <reference lib="dom" />
8
- /// <reference lib="dom.iterable" />
9
-
10
- import * as Overlay from "./Overlay.js"
11
- import * as ScrollState from "./ScrollState.js"
12
-
13
- const BUNDLE_URL = globalThis._BUNDLE_URL ?? "/_bundle"
14
-
15
- function reload() {
16
- ScrollState.persist()
17
- window.location.reload()
18
- }
19
-
20
- async function loadAllEntrypoints() {
21
- const manifest = await fetch(`/${BUNDLE_URL}/manifest.json`).then((v) =>
22
- v.json(),
23
- )
24
-
25
- manifest.artifacts
26
- .filter((v) => v.path.endsWith(".js"))
27
- .forEach((artifact) => {
28
- console.log(artifact.path)
29
- const script = document.createElement("script")
30
- script.src = `${BUNDLE_URL}/${artifact.path}`
31
- script.type = "module"
32
- script.onload = () => {
33
- console.debug("Bundle reloaded")
34
- }
35
- document.body.appendChild(script)
36
- })
37
- }
38
-
39
- function handleBundleEvent(event) {
40
- switch (event._tag) {
41
- case "Change":
42
- console.debug("Bundle change detected...")
43
- reload()
44
- break
45
- case "BuildError":
46
- Overlay.showBuildError(event.error)
47
- break
48
- }
49
- }
50
-
51
- function listen() {
52
- const eventSource = new EventSource(`${BUNDLE_URL}/events`)
53
-
54
- eventSource.addEventListener("message", (event) => {
55
- try {
56
- reloadAllMetaLinks()
57
- const data = JSON.parse(event.data)
58
-
59
- handleBundleEvent(data)
60
- } catch (error) {
61
- console.error("Error parsing SSE event", {
62
- error,
63
- event,
64
- })
65
- }
66
- })
67
-
68
- eventSource.addEventListener("error", (error) => {
69
- console.error("SSE connection error:", error)
70
- })
71
-
72
- return () => {
73
- eventSource.close()
74
- }
75
- }
76
-
77
- function reloadAllMetaLinks() {
78
- for (const link of document.getElementsByTagName("link")) {
79
- const url = new URL(link.href)
80
-
81
- if (url.host === window.location.host) {
82
- const next = link.cloneNode()
83
- // TODO: this won't work when link already has query params
84
- next.href = next.href + "?" + Math.random().toString(36).slice(2)
85
- next.onload = () => link.remove()
86
- link.parentNode.insertBefore(next, link.nextSibling)
87
- return
88
- }
89
- }
90
- }
91
-
92
- if (process.env.NODE_ENV !== "production") {
93
- window.addEventListener("load", () => {
94
- ScrollState.restore()
95
- listen()
96
- })
97
- }
@@ -1,42 +0,0 @@
1
- import * as Effect from "effect/Effect"
2
- import * as Layer from "effect/Layer"
3
- import * as Route from "../Route.js"
4
- import * as RouteTree from "../RouteTree.js"
5
- import * as ConsoleErrors from "./ConsoleErrors.js"
6
- import * as ConsoleLogger from "./ConsoleLogger.js"
7
- import * as ConsoleMetrics from "./ConsoleMetrics.js"
8
- import * as ConsoleProcess from "./ConsoleProcess.js"
9
- import * as ConsoleStore from "./ConsoleStore.js"
10
- import * as ConsoleTracer from "./ConsoleTracer.js"
11
- import consoleRoutes from "./routes/tree.js"
12
-
13
- export { ConsoleStore } from "./ConsoleStore.js"
14
-
15
- export function layer(
16
- options,
17
- ) {
18
- const store = ConsoleStore.layer(options)
19
- return Layer.mergeAll(
20
- ConsoleTracer.layer,
21
- ConsoleLogger.layer,
22
- ConsoleMetrics.layer,
23
- ConsoleErrors.layer,
24
- ConsoleProcess.layer,
25
- ).pipe(Layer.provideMerge(store))
26
- }
27
-
28
- export function routeLayer(
29
- prefix,
30
- ) {
31
- return Layer.effect(
32
- Route.Routes,
33
- Effect.gen(function* () {
34
- const existing = yield* Route.Routes
35
- ConsoleStore.store.prefix = prefix
36
- const tree = Route.tree({
37
- [prefix as "/"]: consoleRoutes,
38
- })
39
- return RouteTree.merge(existing, tree)
40
- }),
41
- )
42
- }
@@ -1,211 +0,0 @@
1
- import * as Cause from "effect/Cause"
2
- import * as Chunk from "effect/Chunk"
3
- import * as Effect from "effect/Effect"
4
- import * as Exit from "effect/Exit"
5
- import * as FiberId from "effect/FiberId"
6
- import * as FiberRef from "effect/FiberRef"
7
- import * as HashMap from "effect/HashMap"
8
- import * as Layer from "effect/Layer"
9
- import * as Option from "effect/Option"
10
- import * as PubSub from "effect/PubSub"
11
- import * as Supervisor from "effect/Supervisor"
12
- import * as ConsoleStore from "./ConsoleStore.js"
13
-
14
- let errorId = 0
15
-
16
- function safeSerialize(value, depth = 0) {
17
- if (depth > 4) return "<deep>"
18
- if (value === null || value === undefined) return value
19
- if (typeof value === "bigint") return `${value}n`
20
- if (typeof value === "function") return undefined
21
- if (typeof value === "symbol") return value.toString()
22
- if (typeof value !== "object") return value
23
- if (value instanceof Date) return value.toISOString()
24
- if (value instanceof Error) return value.message
25
- if (Array.isArray(value)) return value.slice(0, 20).map((v) => safeSerialize(v, depth + 1))
26
- const proto = Object.getPrototypeOf(value)
27
- if (proto !== null && proto !== Object.prototype) {
28
- if (typeof (value)._tag === "string") {
29
- return serializeTaggedObject(value, depth)
30
- }
31
- return `<${proto.constructor?.name ?? "object"}>`
32
- }
33
- return serializePlainObject(value, depth)
34
- }
35
-
36
- function serializePlainObject(
37
- obj,
38
- depth,
39
- ) {
40
- const out = {}
41
- let count = 0
42
- for (const [k, v] of Object.entries(obj)) {
43
- if (count >= 20) break
44
- if (typeof v === "function") continue
45
- const serialized = safeSerialize(v, depth + 1)
46
- if (serialized !== undefined) {
47
- out[k] = serialized
48
- count++
49
- }
50
- }
51
- return out
52
- }
53
-
54
- function serializeTaggedObject(
55
- obj,
56
- depth,
57
- ) {
58
- const out = { _tag: obj._tag }
59
- let count = 0
60
- for (const [k, v] of Object.entries(obj)) {
61
- if (count >= 20) break
62
- if (k === "_tag") continue
63
- if (typeof v === "function") continue
64
- if (k === "stack" || k === "name") continue
65
- const serialized = safeSerialize(v, depth + 1)
66
- if (serialized !== undefined) {
67
- out[k] = serialized
68
- count++
69
- }
70
- }
71
- return out
72
- }
73
-
74
- function extractTag(error) {
75
- if (
76
- error !== null &&
77
- typeof error === "object" &&
78
- "_tag" in error &&
79
- typeof (error)._tag === "string"
80
- ) {
81
- return (error)._tag
82
- }
83
- return undefined
84
- }
85
-
86
- function extractMessage(error) {
87
- if (error instanceof Error) return error.message
88
- if (typeof error === "string") return error
89
- const tag = extractTag(error)
90
- if (tag) return tag
91
- try {
92
- return String(error)
93
- } catch {
94
- return "<unknown>"
95
- }
96
- }
97
-
98
- function extractProperties(error) {
99
- if (error === null || error === undefined || typeof error !== "object") return {}
100
- const out = {}
101
- let count = 0
102
- for (const [k, v] of Object.entries(error)) {
103
- if (count >= 20) break
104
- if (k === "_tag" || k === "stack" || k === "name") continue
105
- if (typeof v === "function") continue
106
- const serialized = safeSerialize(v, 0)
107
- if (serialized !== undefined) {
108
- out[k] = serialized
109
- count++
110
- }
111
- }
112
- return out
113
- }
114
-
115
- const spanSymbol = Symbol.for("effect/SpanAnnotation")
116
-
117
- function extractSpanName(error) {
118
- if (error !== null && typeof error === "object" && spanSymbol in error) {
119
- const span = (error)[spanSymbol]
120
- return typeof span?.name === "string" ? span.name : undefined
121
- }
122
- return undefined
123
- }
124
-
125
- function extractDetails(cause) {
126
- const details = []
127
-
128
- const failures = Chunk.toArray(Cause.failures(cause))
129
- for (const error of failures) {
130
- details.push({
131
- kind: "fail",
132
- tag: extractTag(error),
133
- message: extractMessage(error),
134
- properties: extractProperties(error),
135
- span: extractSpanName(error),
136
- })
137
- }
138
-
139
- const defects = Chunk.toArray(Cause.defects(cause))
140
- for (const defect of defects) {
141
- details.push({
142
- kind: "die",
143
- tag: extractTag(defect),
144
- message: extractMessage(defect),
145
- properties: extractProperties(defect),
146
- span: extractSpanName(defect),
147
- })
148
- }
149
-
150
- return details
151
- }
152
-
153
- function make(store) {
154
- return new (class extends Supervisor.AbstractSupervisor<void> {
155
- value = Effect.void
156
-
157
- onStart<A, E, R>(
158
- _context: Context.Context<R>,
159
- _effect: Effect.Effect<A, E, R>,
160
- parent: Option.Option<Fiber.RuntimeFiber<any, any>>,
161
- fiber: Fiber.RuntimeFiber<A, E>,
162
- ) {
163
- const childId = FiberId.threadName(fiber.id())
164
- if (Option.isSome(parent)) {
165
- const parentId = FiberId.threadName(parent.value.id())
166
- if (childId !== parentId) {
167
- store.fiberParents.set(childId, parentId)
168
- }
169
- }
170
-
171
- const span = fiber.currentSpan
172
- const annotations: Record<string, unknown> = {}
173
- const spanAnnotations = fiber.getFiberRef(FiberRef.currentTracerSpanAnnotations)
174
- HashMap.forEach(spanAnnotations, (value, key) => {
175
- annotations[key] = value
176
- })
177
- const logAnnotations = fiber.getFiberRef(FiberRef.currentLogAnnotations)
178
- HashMap.forEach(logAnnotations, (value, key) => {
179
- annotations[key] = value
180
- })
181
-
182
- store.fiberContexts.set(childId, {
183
- spanName: span?._tag === "Span" ? span.name : undefined,
184
- traceId: span ? span.traceId : undefined,
185
- annotations,
186
- })
187
- }
188
-
189
- onEnd<A, E>(exit: Exit.Exit<A, E>, fiber: Fiber.RuntimeFiber<A, E>) {
190
- if (Exit.isFailure(exit) && !Cause.isInterruptedOnly(exit.cause)) {
191
- const error: ConsoleStore.ConsoleError = {
192
- id: String(++errorId),
193
- date: new Date(),
194
- fiberId: FiberId.threadName(fiber.id()),
195
- interrupted: Cause.isInterrupted(exit.cause),
196
- prettyPrint: Cause.pretty(exit.cause, { renderErrorCause: true }),
197
- details: extractDetails(exit.cause),
198
- }
199
- store.errors.push(error)
200
- Effect.runSync(PubSub.publish(store.events, { _tag: "Error", error }))
201
- }
202
- }
203
- })()
204
- }
205
-
206
- export const layer = Layer.unwrapEffect(
207
- Effect.gen(function* () {
208
- const store = yield* ConsoleStore.ConsoleStore
209
- return Supervisor.addSupervisor(make(store))
210
- }),
211
- )