@opencode/util 0.0.0-reserved → 2.0.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.
- package/dist/activity-calendar.d.ts +21 -0
- package/dist/activity-calendar.js +42 -0
- package/dist/activity-calendar.test.d.ts +1 -0
- package/dist/activity-calendar.test.js +119 -0
- package/dist/binary.d.ts +7 -0
- package/dist/binary.js +34 -0
- package/dist/binary.test.d.ts +1 -0
- package/dist/binary.test.js +16 -0
- package/dist/bom.d.ts +22 -0
- package/dist/bom.js +36 -0
- package/dist/bom.test.d.ts +1 -0
- package/dist/bom.test.js +19 -0
- package/dist/cross-spawn-spawner.d.ts +3 -0
- package/dist/cross-spawn-spawner.js +438 -0
- package/dist/effect/app-node-platform.d.ts +6 -0
- package/dist/effect/app-node-platform.js +11 -0
- package/dist/effect/app-node.d.ts +62 -0
- package/dist/effect/app-node.js +8 -0
- package/dist/effect/layer-node.d.ts +135 -0
- package/dist/effect/layer-node.js +156 -0
- package/dist/effect/memo-map.d.ts +2 -0
- package/dist/effect/memo-map.js +2 -0
- package/dist/effect/runtime.d.ts +8 -0
- package/dist/effect/runtime.js +16 -0
- package/dist/effect/service-use.d.ts +7 -0
- package/dist/effect/service-use.js +27 -0
- package/dist/effect-flock.d.ts +31 -0
- package/dist/effect-flock.js +186 -0
- package/dist/encode.d.ts +4 -0
- package/dist/encode.js +38 -0
- package/dist/encode.test.d.ts +1 -0
- package/dist/encode.test.js +23 -0
- package/dist/flock.d.ts +30 -0
- package/dist/flock.js +273 -0
- package/dist/fs-util.d.ts +137 -0
- package/dist/fs-util.js +211 -0
- package/dist/glob.d.ts +12 -0
- package/dist/glob.js +26 -0
- package/dist/global-roots.d.ts +8 -0
- package/dist/global-roots.js +17 -0
- package/dist/global-roots.workerd.d.ts +7 -0
- package/dist/global-roots.workerd.js +15 -0
- package/dist/global.d.ts +30 -0
- package/dist/global.js +54 -0
- package/dist/hash.d.ts +4 -0
- package/dist/hash.js +12 -0
- package/dist/npm-config.d.ts +4 -0
- package/dist/npm-config.js +34 -0
- package/dist/npm.d.ts +37 -0
- package/dist/npm.js +386 -0
- package/dist/observability/logging.d.ts +14 -0
- package/dist/observability/logging.js +151 -0
- package/dist/observability/otlp.d.ts +18 -0
- package/dist/observability/otlp.js +76 -0
- package/dist/observability/shared.d.ts +1 -0
- package/dist/observability/shared.js +7 -0
- package/dist/observability.d.ts +13 -0
- package/dist/observability.js +37 -0
- package/dist/patch.d.ts +43 -0
- package/dist/patch.js +331 -0
- package/dist/path.d.ts +4 -0
- package/dist/path.js +33 -0
- package/dist/path.test.d.ts +1 -0
- package/dist/path.test.js +36 -0
- package/dist/process.d.ts +54 -0
- package/dist/process.js +167 -0
- package/dist/retry.d.ts +8 -0
- package/dist/retry.js +37 -0
- package/dist/retry.test.d.ts +1 -0
- package/dist/retry.test.js +31 -0
- package/dist/runtime/import.bun.d.ts +2 -0
- package/dist/runtime/import.bun.js +8 -0
- package/dist/runtime/import.node.d.ts +2 -0
- package/dist/runtime/import.node.js +62 -0
- package/dist/runtime/import.workerd.d.ts +2 -0
- package/dist/runtime/import.workerd.js +7 -0
- package/dist/runtime-import.d.ts +1 -0
- package/dist/runtime-import.js +1 -0
- package/dist/session-title-fallback.d.ts +16 -0
- package/dist/session-title-fallback.js +23 -0
- package/package.json +66 -6
- package/README.md +0 -5
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import { Effect, FileSystem, Formatter, Logger, Option, Schedule, Stream } from "effect";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import { Global } from "../global.js";
|
|
4
|
+
import { runID } from "./shared.js";
|
|
5
|
+
// One log file is shared by every opencode process on the machine and only ever appended to, so it
|
|
6
|
+
// is bounded by compacting in place instead of rotating: once it passes LOG_MAX_BYTES the head is
|
|
7
|
+
// dropped so roughly LOG_KEEP_BYTES remain, rounded forward to the next line boundary.
|
|
8
|
+
export const LOG_MAX_BYTES = 50 * 1024 * 1024;
|
|
9
|
+
export const LOG_KEEP_BYTES = 25 * 1024 * 1024;
|
|
10
|
+
export const LOG_TRIM_INTERVAL = "1 hour";
|
|
11
|
+
// A trim of a 1 GB log takes well under a second, so a lock older than this belongs to a dead process.
|
|
12
|
+
export const LOG_TRIM_LOCK_STALE_MS = 5 * 60 * 1000;
|
|
13
|
+
const LOG_TRIM_CHUNK = 64 * 1024;
|
|
14
|
+
function formatter(id = runID()) {
|
|
15
|
+
return Logger.map(Logger.formatStructured, (output) => {
|
|
16
|
+
const messages = Array.isArray(output.message) ? output.message : [output.message];
|
|
17
|
+
return [
|
|
18
|
+
["timestamp", output.timestamp],
|
|
19
|
+
["level", output.level],
|
|
20
|
+
["run", id],
|
|
21
|
+
...messages.flatMap((value) => (plain(value) ? flatten(value) : [["message", value]])),
|
|
22
|
+
...(output.cause === undefined ? [] : [["cause", output.cause]]),
|
|
23
|
+
...flatten(output.spans),
|
|
24
|
+
...flatten(output.annotations),
|
|
25
|
+
]
|
|
26
|
+
.map(([key, value]) => `${key}=${format(value)}`)
|
|
27
|
+
.join(" ");
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
function flatten(input, prefix = "", seen = new WeakSet()) {
|
|
31
|
+
if (seen.has(input))
|
|
32
|
+
return [[prefix, "[Circular]"]];
|
|
33
|
+
seen.add(input);
|
|
34
|
+
const entries = Object.entries(input);
|
|
35
|
+
if (entries.length === 0 && prefix)
|
|
36
|
+
return [[prefix, input]];
|
|
37
|
+
return entries.flatMap(([key, value]) => {
|
|
38
|
+
const path = prefix ? `${prefix}.${key}` : key;
|
|
39
|
+
return plain(value) ? flatten(value, path, seen) : [[path, value]];
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
function plain(input) {
|
|
43
|
+
if (input === null || typeof input !== "object" || Array.isArray(input))
|
|
44
|
+
return false;
|
|
45
|
+
const prototype = Object.getPrototypeOf(input);
|
|
46
|
+
return prototype === Object.prototype || prototype === null;
|
|
47
|
+
}
|
|
48
|
+
function format(input) {
|
|
49
|
+
const value = typeof input === "string" ? input : Formatter.format(input);
|
|
50
|
+
return /^[^\s="\\]+$/.test(value) ? value : JSON.stringify(value);
|
|
51
|
+
}
|
|
52
|
+
export function file(local = true, channel = "local") {
|
|
53
|
+
if (!local)
|
|
54
|
+
return path.join(Global.Path.log, "opencode.log");
|
|
55
|
+
return path.join(Global.Path.log, `opencode-${channel.replace(/[^a-zA-Z0-9._-]/g, "-")}.log`);
|
|
56
|
+
}
|
|
57
|
+
export function fileLogger(target = file(), id = runID()) {
|
|
58
|
+
// Do not set batchWindow to 0; it causes high idle CPU usage.
|
|
59
|
+
return Effect.gen(function* () {
|
|
60
|
+
const fs = yield* FileSystem.FileSystem;
|
|
61
|
+
yield* fs.makeDirectory(path.dirname(target), { recursive: true });
|
|
62
|
+
const logger = yield* Logger.toFile(formatter(id), target, { flag: "a" });
|
|
63
|
+
yield* trim(target).pipe(Effect.ignore, Effect.repeat(Schedule.spaced(LOG_TRIM_INTERVAL)), Effect.forkScoped({ startImmediately: true }));
|
|
64
|
+
return logger;
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
// Compacts in place rather than writing a temp file and renaming over the log. Other processes hold
|
|
68
|
+
// the same file open with O_APPEND, so they keep appending to the compacted file, whereas a rename
|
|
69
|
+
// would strand them on the unlinked inode and lose their output.
|
|
70
|
+
//
|
|
71
|
+
// Appenders are not coordinated with the final truncate, so a batch flushed by another process
|
|
72
|
+
// between the last read and the truncate is lost. That window is a few milliseconds once per trim,
|
|
73
|
+
// which is an accepted trade for not wrapping every log write in a cross-process lock.
|
|
74
|
+
export const trim = Effect.fn("Logging.trim")(function* (target, options = {}) {
|
|
75
|
+
const max = options.max ?? LOG_MAX_BYTES;
|
|
76
|
+
const keep = options.keep ?? LOG_KEEP_BYTES;
|
|
77
|
+
const fs = yield* FileSystem.FileSystem;
|
|
78
|
+
// Every opencode process on the machine runs this against the same file. Two trimmers racing
|
|
79
|
+
// would have one compute its cut from a size the other already shrank, so only one may proceed
|
|
80
|
+
// and the rest skip until the next interval. mkdir is the atomic primitive on every platform.
|
|
81
|
+
// acquireRelease keeps the mkdir uninterruptible, so a scope closing mid-call cannot leave a lock
|
|
82
|
+
// on disk with no finalizer registered to remove it.
|
|
83
|
+
const lock = `${target}.trim`;
|
|
84
|
+
const acquired = yield* Effect.acquireRelease(fs.makeDirectory(lock).pipe(Effect.as(true), Effect.catchIf((error) => error.reason._tag === "AlreadyExists", () => breakStaleLock(fs, lock))), (acquired) => (acquired ? fs.remove(lock, { recursive: true }).pipe(Effect.ignore) : Effect.void));
|
|
85
|
+
if (!acquired)
|
|
86
|
+
return;
|
|
87
|
+
const size = Number((yield* fs.stat(target)).size);
|
|
88
|
+
if (size <= max)
|
|
89
|
+
return;
|
|
90
|
+
const handle = yield* fs.open(target, { flag: "r+" });
|
|
91
|
+
const start = yield* lineStart(handle, size - keep);
|
|
92
|
+
yield* handle.seek(0, "start");
|
|
93
|
+
// Reads run to the current EOF so lines appended since the stat survive; the write cursor always
|
|
94
|
+
// trails the read cursor so the forward copy never overwrites unread bytes.
|
|
95
|
+
const written = yield* fs.stream(target, { offset: start, chunkSize: LOG_TRIM_CHUNK }).pipe(Stream.runFoldEffect(() => 0, (total, chunk) => handle.writeAll(chunk).pipe(Effect.as(total + chunk.length))));
|
|
96
|
+
yield* handle.truncate(written);
|
|
97
|
+
}, Effect.scoped);
|
|
98
|
+
// Removes a lock left by a process that died mid-trim. Still yields this round: the next interval
|
|
99
|
+
// acquires cleanly, and a process that is legitimately trimming right now keeps its lock.
|
|
100
|
+
//
|
|
101
|
+
// Two processes that both observe the same stale lock can race here: one removes it, a third
|
|
102
|
+
// acquires fresh, and the other removes that fresh lock. That needs a crash inside a sub-second trim
|
|
103
|
+
// followed by three processes ticking within the same few milliseconds, and the consequence is the
|
|
104
|
+
// same bounded tail loss documented on `trim`. Not worth a breaker protocol; see EffectFlock if it is.
|
|
105
|
+
function breakStaleLock(fs, lock) {
|
|
106
|
+
return Effect.gen(function* () {
|
|
107
|
+
const info = yield* fs.stat(lock).pipe(Effect.option);
|
|
108
|
+
const modified = Option.flatMap(info, (value) => value.mtime);
|
|
109
|
+
if (Option.isNone(modified))
|
|
110
|
+
return false;
|
|
111
|
+
if (Date.now() - modified.value.getTime() < LOG_TRIM_LOCK_STALE_MS)
|
|
112
|
+
return false;
|
|
113
|
+
yield* fs.remove(lock, { recursive: true }).pipe(Effect.ignore);
|
|
114
|
+
return false;
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
// First byte after the first newline at or beyond `from`, or EOF when the tail has no newline.
|
|
118
|
+
function lineStart(handle, from) {
|
|
119
|
+
return Effect.gen(function* () {
|
|
120
|
+
let cursor = from;
|
|
121
|
+
while (true) {
|
|
122
|
+
yield* handle.seek(cursor, "start");
|
|
123
|
+
const chunk = yield* handle.readAlloc(LOG_TRIM_CHUNK);
|
|
124
|
+
if (Option.isNone(chunk))
|
|
125
|
+
return cursor;
|
|
126
|
+
const newline = chunk.value.indexOf(10);
|
|
127
|
+
if (newline !== -1)
|
|
128
|
+
return cursor + newline + 1;
|
|
129
|
+
cursor += chunk.value.length;
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
const stderrLogger = Logger.make((options) => {
|
|
134
|
+
if (process.env.OPENCODE_PRINT_LOGS !== "1")
|
|
135
|
+
return;
|
|
136
|
+
process.stderr.write(formatter().log(options) + "\n");
|
|
137
|
+
});
|
|
138
|
+
export function minimumLogLevel() {
|
|
139
|
+
const value = process.env.OPENCODE_LOG_LEVEL?.toUpperCase();
|
|
140
|
+
const levels = {
|
|
141
|
+
DEBUG: "Debug",
|
|
142
|
+
INFO: "Info",
|
|
143
|
+
WARN: "Warn",
|
|
144
|
+
ERROR: "Error",
|
|
145
|
+
};
|
|
146
|
+
return value && value in levels ? levels[value] : levels.INFO;
|
|
147
|
+
}
|
|
148
|
+
export function loggers(local = true, channel = "local") {
|
|
149
|
+
return [fileLogger(file(local, channel)), stderrLogger];
|
|
150
|
+
}
|
|
151
|
+
export * as Logging from "./logging.js";
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { Effect, Layer, Scope } from "effect";
|
|
2
|
+
export interface Options {
|
|
3
|
+
readonly endpoint?: string;
|
|
4
|
+
readonly headers?: string;
|
|
5
|
+
}
|
|
6
|
+
export interface App {
|
|
7
|
+
readonly client: string;
|
|
8
|
+
readonly version: string;
|
|
9
|
+
readonly channel: string;
|
|
10
|
+
}
|
|
11
|
+
export declare function resource(app?: App): {
|
|
12
|
+
serviceName: string;
|
|
13
|
+
serviceVersion: string;
|
|
14
|
+
attributes: Record<string, string>;
|
|
15
|
+
};
|
|
16
|
+
export declare function loggers(options: Options | undefined, app: App): Effect.Effect<import("effect/Logger").Logger<unknown, void>, never, Scope.Scope | import("effect/unstable/http/HttpClient").HttpClient | import("effect/unstable/observability/OtlpExporter").Flusher | import("effect/unstable/observability/OtlpSerialization").OtlpSerialization>[];
|
|
17
|
+
export declare const tracingLayer: (options: Options | undefined, app: App) => Effect.Effect<Layer.Layer<never, never, never>, never, never>;
|
|
18
|
+
export * as Otlp from "./otlp.js";
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { Effect, Layer, Scope } from "effect";
|
|
2
|
+
import { OtlpLogger } from "effect/unstable/observability";
|
|
3
|
+
import { runID } from "./shared.js";
|
|
4
|
+
function parseHeaders(value) {
|
|
5
|
+
return value
|
|
6
|
+
? value.split(",").reduce((acc, entry) => {
|
|
7
|
+
const [key, ...value] = entry.split("=");
|
|
8
|
+
acc[key] = value.join("=");
|
|
9
|
+
return acc;
|
|
10
|
+
}, {})
|
|
11
|
+
: undefined;
|
|
12
|
+
}
|
|
13
|
+
function resourceAttributes() {
|
|
14
|
+
const value = process.env.OTEL_RESOURCE_ATTRIBUTES;
|
|
15
|
+
if (!value)
|
|
16
|
+
return {};
|
|
17
|
+
try {
|
|
18
|
+
return Object.fromEntries(value.split(",").map((entry) => {
|
|
19
|
+
const index = entry.indexOf("=");
|
|
20
|
+
if (index < 1)
|
|
21
|
+
throw new Error("Invalid OTEL_RESOURCE_ATTRIBUTES entry");
|
|
22
|
+
return [decodeURIComponent(entry.slice(0, index)), decodeURIComponent(entry.slice(index + 1))];
|
|
23
|
+
}));
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
return {};
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
export function resource(app = { client: "opencode", version: "unknown", channel: "local" }) {
|
|
30
|
+
return {
|
|
31
|
+
serviceName: "opencode",
|
|
32
|
+
serviceVersion: app.version,
|
|
33
|
+
attributes: {
|
|
34
|
+
...resourceAttributes(),
|
|
35
|
+
"deployment.environment.name": app.channel,
|
|
36
|
+
"opencode.client": app.client,
|
|
37
|
+
"opencode.run": runID(),
|
|
38
|
+
"service.instance.id": runID(),
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
export function loggers(options, app) {
|
|
43
|
+
if (!options?.endpoint)
|
|
44
|
+
return [];
|
|
45
|
+
return [
|
|
46
|
+
OtlpLogger.make({
|
|
47
|
+
url: `${options.endpoint}/v1/logs`,
|
|
48
|
+
resource: resource(app),
|
|
49
|
+
headers: parseHeaders(options.headers),
|
|
50
|
+
}),
|
|
51
|
+
];
|
|
52
|
+
}
|
|
53
|
+
export const tracingLayer = Effect.fnUntraced(function* (options, app) {
|
|
54
|
+
if (!options?.endpoint)
|
|
55
|
+
return Layer.empty;
|
|
56
|
+
const [{ layer }, { OTLPTraceExporter }, { BatchSpanProcessor }, { AsyncLocalStorageContextManager }, { context }] = yield* Effect.all([
|
|
57
|
+
Effect.promise(() => import("@effect/opentelemetry/NodeSdk")),
|
|
58
|
+
Effect.promise(() => import("@opentelemetry/exporter-trace-otlp-http")),
|
|
59
|
+
Effect.promise(() => import("@opentelemetry/sdk-trace-base")),
|
|
60
|
+
Effect.promise(() => import("@opentelemetry/context-async-hooks")),
|
|
61
|
+
Effect.promise(() => import("@opentelemetry/api")),
|
|
62
|
+
], { concurrency: "unbounded" });
|
|
63
|
+
// The Effect Node SDK does not register a global context manager, but the AI SDK uses it to parent spans.
|
|
64
|
+
const manager = new AsyncLocalStorageContextManager();
|
|
65
|
+
manager.enable();
|
|
66
|
+
context.setGlobalContextManager(manager);
|
|
67
|
+
const tracing = layer(() => ({
|
|
68
|
+
resource: resource(app),
|
|
69
|
+
spanProcessor: new BatchSpanProcessor(new OTLPTraceExporter({
|
|
70
|
+
url: `${options.endpoint}/v1/traces`,
|
|
71
|
+
headers: parseHeaders(options.headers),
|
|
72
|
+
})),
|
|
73
|
+
}));
|
|
74
|
+
return Layer.effectContext(Effect.acquireRelease(Scope.make(), (scope, exit) => Scope.close(scope, exit).pipe(Effect.ignoreCause)).pipe(Effect.flatMap((scope) => Layer.buildWithScope(tracing, scope))));
|
|
75
|
+
});
|
|
76
|
+
export * as Otlp from "./otlp.js";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function runID(): string;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
// Lazy: workerd forbids generating random values in global scope, so the id
|
|
2
|
+
// materializes on first call (inside a handler) and stays stable afterwards.
|
|
3
|
+
let generated;
|
|
4
|
+
export function runID() {
|
|
5
|
+
generated ??= crypto.randomUUID().slice(0, 8);
|
|
6
|
+
return generated;
|
|
7
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export * as Observability from "./observability.js";
|
|
2
|
+
import { LayerNode } from "./effect/layer-node.js";
|
|
3
|
+
import { Layer, Schema } from "effect";
|
|
4
|
+
export declare const Options: Schema.Struct<{
|
|
5
|
+
readonly endpoint: Schema.optional<Schema.String>;
|
|
6
|
+
readonly headers: Schema.optional<Schema.String>;
|
|
7
|
+
readonly client: Schema.optional<Schema.String>;
|
|
8
|
+
readonly version: Schema.optional<Schema.String>;
|
|
9
|
+
readonly channel: Schema.optional<Schema.String>;
|
|
10
|
+
}>;
|
|
11
|
+
export type Options = typeof Options.Type;
|
|
12
|
+
export declare function layer(options?: Options): Layer.Layer<never, never, never>;
|
|
13
|
+
export declare const node: LayerNode.Provider<never, never, undefined>;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export * as Observability from "./observability.js";
|
|
2
|
+
import * as NodeFileSystem from "@effect/platform-node/NodeFileSystem";
|
|
3
|
+
import { LayerNode } from "./effect/layer-node.js";
|
|
4
|
+
import { Effect, Layer, Logger, References, Schema } from "effect";
|
|
5
|
+
import { FetchHttpClient } from "effect/unstable/http";
|
|
6
|
+
import { OtlpExporter, OtlpSerialization } from "effect/unstable/observability";
|
|
7
|
+
import { Logging } from "./observability/logging.js";
|
|
8
|
+
import { Otlp } from "./observability/otlp.js";
|
|
9
|
+
export const Options = Schema.Struct({
|
|
10
|
+
endpoint: Schema.optional(Schema.String),
|
|
11
|
+
headers: Schema.optional(Schema.String),
|
|
12
|
+
client: Schema.optional(Schema.String),
|
|
13
|
+
version: Schema.optional(Schema.String),
|
|
14
|
+
channel: Schema.optional(Schema.String),
|
|
15
|
+
});
|
|
16
|
+
export function layer(options = {
|
|
17
|
+
endpoint: process.env.OTEL_EXPORTER_OTLP_ENDPOINT,
|
|
18
|
+
headers: process.env.OTEL_EXPORTER_OTLP_HEADERS,
|
|
19
|
+
}) {
|
|
20
|
+
const app = {
|
|
21
|
+
client: options.client ?? "opencode",
|
|
22
|
+
version: options.version ?? "unknown",
|
|
23
|
+
channel: options.channel ?? "local",
|
|
24
|
+
};
|
|
25
|
+
const local = Logger.layer(Logging.loggers(app.channel === "local", app.channel), { mergeWithExisting: false }).pipe(Layer.provide(NodeFileSystem.layer), Layer.orDie, Layer.merge(Layer.succeed(References.MinimumLogLevel, Logging.minimumLogLevel())));
|
|
26
|
+
return Layer.unwrap(Effect.gen(function* () {
|
|
27
|
+
const logs = Logger.layer([...Logging.loggers(app.channel === "local", app.channel), ...Otlp.loggers(options, app)], { mergeWithExisting: false }).pipe(Layer.provide(NodeFileSystem.layer), Layer.provide(OtlpSerialization.layerJson), Layer.provide(FetchHttpClient.layer), Layer.provide(OtlpExporter.layerFlusher), Layer.orDie, Layer.merge(Layer.succeed(References.MinimumLogLevel, Logging.minimumLogLevel())));
|
|
28
|
+
return Layer.merge(logs, yield* Otlp.tracingLayer(options, app));
|
|
29
|
+
})).pipe(Layer.catchCause(() => local));
|
|
30
|
+
}
|
|
31
|
+
// Layer.suspend: constructing the loggers eagerly at module scope performs
|
|
32
|
+
// I/O (file logger, run id) that workerd forbids in global scope.
|
|
33
|
+
export const node = LayerNode.make({
|
|
34
|
+
name: "observability",
|
|
35
|
+
layer: Layer.suspend(() => layer()),
|
|
36
|
+
deps: [],
|
|
37
|
+
});
|
package/dist/patch.d.ts
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
export * as Patch from "./patch.js";
|
|
2
|
+
import { Result, Schema } from "effect";
|
|
3
|
+
declare const BoundaryError_base: Schema.Class<BoundaryError, Schema.TaggedStruct<"Patch.BoundaryError", {
|
|
4
|
+
readonly boundary: Schema.Literals<readonly ["first", "last"]>;
|
|
5
|
+
}>, import("effect/Cause").YieldableError>;
|
|
6
|
+
export declare class BoundaryError extends BoundaryError_base {
|
|
7
|
+
get message(): string;
|
|
8
|
+
}
|
|
9
|
+
declare const InvalidHunkError_base: Schema.Class<InvalidHunkError, Schema.TaggedStruct<"Patch.InvalidHunkError", {
|
|
10
|
+
readonly line: Schema.String;
|
|
11
|
+
readonly lineNumber: Schema.Number;
|
|
12
|
+
readonly reason: Schema.optional<Schema.String>;
|
|
13
|
+
}>, import("effect/Cause").YieldableError>;
|
|
14
|
+
export declare class InvalidHunkError extends InvalidHunkError_base {
|
|
15
|
+
get message(): string;
|
|
16
|
+
}
|
|
17
|
+
export type ParseError = BoundaryError | InvalidHunkError;
|
|
18
|
+
export type Hunk = {
|
|
19
|
+
readonly type: "add";
|
|
20
|
+
readonly path: string;
|
|
21
|
+
readonly contents: string;
|
|
22
|
+
} | {
|
|
23
|
+
readonly type: "delete";
|
|
24
|
+
readonly path: string;
|
|
25
|
+
} | {
|
|
26
|
+
readonly type: "update";
|
|
27
|
+
readonly path: string;
|
|
28
|
+
readonly movePath?: string;
|
|
29
|
+
readonly chunks: ReadonlyArray<UpdateFileChunk>;
|
|
30
|
+
};
|
|
31
|
+
export interface UpdateFileChunk {
|
|
32
|
+
readonly oldLines: ReadonlyArray<string>;
|
|
33
|
+
readonly newLines: ReadonlyArray<string>;
|
|
34
|
+
readonly changeContext?: string;
|
|
35
|
+
readonly endOfFile?: boolean;
|
|
36
|
+
}
|
|
37
|
+
export interface FileUpdate {
|
|
38
|
+
readonly content: string;
|
|
39
|
+
readonly bom: boolean;
|
|
40
|
+
}
|
|
41
|
+
export declare function parse(patchText: string): Result.Result<ReadonlyArray<Hunk>, ParseError>;
|
|
42
|
+
export declare function derive(path: string, chunks: ReadonlyArray<UpdateFileChunk>, original: string): FileUpdate;
|
|
43
|
+
export declare function joinBom(text: string, bom: boolean): string;
|