sync
parent
a4183c3b2c
commit
38f735bfc6
3
bun.lock
3
bun.lock
|
|
@ -326,7 +326,6 @@
|
|||
"solid-js": "catalog:",
|
||||
"strip-ansi": "7.1.2",
|
||||
"tree-sitter-bash": "0.25.0",
|
||||
"trust": "0.1.0",
|
||||
"turndown": "7.2.0",
|
||||
"ulid": "catalog:",
|
||||
"vscode-jsonrpc": "8.2.1",
|
||||
|
|
@ -3780,8 +3779,6 @@
|
|||
|
||||
"trough": ["trough@2.2.0", "", {}, "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw=="],
|
||||
|
||||
"trust": ["trust@0.1.0", "", {}, "sha512-BzU8tL0AD8ftb9008U9Wv6ww+ha5Z9fwQMmU0ICTTMKlazW3vGZkNCwX5L6t3Mf08vSA+aDCGsGjDT9nxl0vig=="],
|
||||
|
||||
"ts-algebra": ["ts-algebra@2.0.0", "", {}, "sha512-FPAhNPFMrkwz76P7cdjdmiShwMynZYN6SgOujD1urY4oNm80Ou9oMdmbR45LotcKOXoy7wSmHkRFE6Mxbrhefw=="],
|
||||
|
||||
"ts-interface-checker": ["ts-interface-checker@0.1.13", "", {}, "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA=="],
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@ CREATE TABLE `project` (
|
|||
CREATE TABLE `message` (
|
||||
`id` text PRIMARY KEY NOT NULL,
|
||||
`session_id` text NOT NULL,
|
||||
`created_at` integer NOT NULL,
|
||||
`data` text NOT NULL,
|
||||
FOREIGN KEY (`session_id`) REFERENCES `session`(`id`) ON UPDATE no action ON DELETE cascade
|
||||
);
|
||||
|
|
@ -46,9 +45,24 @@ CREATE TABLE `session` (
|
|||
`id` text PRIMARY KEY NOT NULL,
|
||||
`project_id` text NOT NULL,
|
||||
`parent_id` text,
|
||||
`created_at` integer NOT NULL,
|
||||
`updated_at` integer NOT NULL,
|
||||
`data` text NOT NULL,
|
||||
`slug` text NOT NULL,
|
||||
`directory` text NOT NULL,
|
||||
`title` text NOT NULL,
|
||||
`version` text NOT NULL,
|
||||
`share_url` text,
|
||||
`summary_additions` integer,
|
||||
`summary_deletions` integer,
|
||||
`summary_files` integer,
|
||||
`summary_diffs` text,
|
||||
`revert_message_id` text,
|
||||
`revert_part_id` text,
|
||||
`revert_snapshot` text,
|
||||
`revert_diff` text,
|
||||
`permission` text,
|
||||
`time_created` integer NOT NULL,
|
||||
`time_updated` integer NOT NULL,
|
||||
`time_compacting` integer,
|
||||
`time_archived` integer,
|
||||
FOREIGN KEY (`project_id`) REFERENCES `project`(`id`) ON UPDATE no action ON DELETE cascade
|
||||
);
|
||||
--> statement-breakpoint
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"version": "6",
|
||||
"dialect": "sqlite",
|
||||
"id": "f79c82ae-4de1-4a4c-a5f3-857bc3ee97f2",
|
||||
"id": "86d0107e-84d7-4b08-8411-afab7d6b1ee2",
|
||||
"prevId": "00000000-0000-0000-0000-000000000000",
|
||||
"tables": {
|
||||
"project": {
|
||||
|
|
@ -101,13 +101,6 @@
|
|||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"created_at": {
|
||||
"name": "created_at",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"data": {
|
||||
"name": "data",
|
||||
"type": "text",
|
||||
|
|
@ -311,26 +304,131 @@
|
|||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"created_at": {
|
||||
"name": "created_at",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"updated_at": {
|
||||
"name": "updated_at",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"data": {
|
||||
"name": "data",
|
||||
"slug": {
|
||||
"name": "slug",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"directory": {
|
||||
"name": "directory",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"title": {
|
||||
"name": "title",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"version": {
|
||||
"name": "version",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"share_url": {
|
||||
"name": "share_url",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"summary_additions": {
|
||||
"name": "summary_additions",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"summary_deletions": {
|
||||
"name": "summary_deletions",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"summary_files": {
|
||||
"name": "summary_files",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"summary_diffs": {
|
||||
"name": "summary_diffs",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"revert_message_id": {
|
||||
"name": "revert_message_id",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"revert_part_id": {
|
||||
"name": "revert_part_id",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"revert_snapshot": {
|
||||
"name": "revert_snapshot",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"revert_diff": {
|
||||
"name": "revert_diff",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"permission": {
|
||||
"name": "permission",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"time_created": {
|
||||
"name": "time_created",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"time_updated": {
|
||||
"name": "time_updated",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"time_compacting": {
|
||||
"name": "time_compacting",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"time_archived": {
|
||||
"name": "time_archived",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@
|
|||
{
|
||||
"idx": 0,
|
||||
"version": "6",
|
||||
"when": 1768501411495,
|
||||
"tag": "0000_easy_albert_cleary",
|
||||
"when": 1768518709430,
|
||||
"tag": "0000_vengeful_the_watchers",
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ export const migrations: { name: string; sql: string }[] = []
|
|||
process.exit(0)
|
||||
}
|
||||
|
||||
const imports = files.map((f, i) => `import m${i} from "../../drizzle/${f}" with { type: "text" }`).join("\n")
|
||||
const imports = files.map((f, i) => `import m${i} from "../../migration/${f}" with { type: "text" }`).join("\n")
|
||||
|
||||
const entries = files.map((f, i) => ` { name: "${path.basename(f, ".sql")}", sql: m${i} },`).join("\n")
|
||||
|
||||
|
|
|
|||
|
|
@ -82,14 +82,31 @@ export const ImportCommand = cmd({
|
|||
return
|
||||
}
|
||||
|
||||
db()
|
||||
.insert(SessionTable)
|
||||
.values(Session.toRow({ ...exportData.info, projectID: Instance.project.id }))
|
||||
.onConflictDoUpdate({
|
||||
target: SessionTable.id,
|
||||
set: Session.toRow({ ...exportData.info, projectID: Instance.project.id }),
|
||||
})
|
||||
.run()
|
||||
const info = exportData.info
|
||||
const row = {
|
||||
id: info.id,
|
||||
projectID: Instance.project.id,
|
||||
parentID: info.parentID,
|
||||
slug: info.slug,
|
||||
directory: info.directory,
|
||||
title: info.title,
|
||||
version: info.version,
|
||||
share_url: info.share?.url,
|
||||
summary_additions: info.summary?.additions,
|
||||
summary_deletions: info.summary?.deletions,
|
||||
summary_files: info.summary?.files,
|
||||
summary_diffs: info.summary?.diffs,
|
||||
revert_messageID: info.revert?.messageID,
|
||||
revert_partID: info.revert?.partID,
|
||||
revert_snapshot: info.revert?.snapshot,
|
||||
revert_diff: info.revert?.diff,
|
||||
permission: info.permission,
|
||||
time_created: info.time.created,
|
||||
time_updated: info.time.updated,
|
||||
time_compacting: info.time.compacting,
|
||||
time_archived: info.time.archived,
|
||||
}
|
||||
db().insert(SessionTable).values(row).onConflictDoUpdate({ target: SessionTable.id, set: row }).run()
|
||||
|
||||
for (const msg of exportData.messages) {
|
||||
db()
|
||||
|
|
@ -97,7 +114,6 @@ export const ImportCommand = cmd({
|
|||
.values({
|
||||
id: msg.info.id,
|
||||
sessionID: exportData.info.id,
|
||||
createdAt: msg.info.time?.created ?? Date.now(),
|
||||
data: msg.info,
|
||||
})
|
||||
.onConflictDoUpdate({ target: MessageTable.id, set: { data: msg.info } })
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import { SessionTable } from "../session/session.sql"
|
|||
import { eq } from "drizzle-orm"
|
||||
import { Log } from "../util/log"
|
||||
import { Flag } from "@/flag/flag"
|
||||
import { Session } from "../session"
|
||||
|
||||
import { work } from "../util/queue"
|
||||
import { fn } from "@opencode-ai/util/fn"
|
||||
import { BusEvent } from "@/bus/bus-event"
|
||||
|
|
@ -304,13 +304,10 @@ export namespace Project {
|
|||
log.info("migrating sessions from global", { newProjectID, worktree, count: globalSessions.length })
|
||||
|
||||
await work(10, globalSessions, async (row) => {
|
||||
const session = Session.fromRow(row)
|
||||
if (!session) return
|
||||
if (session.directory && session.directory !== worktree) return
|
||||
if (row.directory && row.directory !== worktree) return
|
||||
|
||||
session.projectID = newProjectID
|
||||
log.info("migrating session", { sessionID: session.id, from: "global", to: newProjectID })
|
||||
db().update(SessionTable).set(Session.toRow(session)).where(eq(SessionTable.id, session.id)).run()
|
||||
log.info("migrating session", { sessionID: row.id, from: "global", to: newProjectID })
|
||||
db().update(SessionTable).set({ projectID: newProjectID }).where(eq(SessionTable.id, row.id)).run()
|
||||
}).catch((error) => {
|
||||
log.error("failed to migrate sessions from global to project", { error, projectId: newProjectID })
|
||||
})
|
||||
|
|
|
|||
|
|
@ -85,32 +85,6 @@ export namespace Session {
|
|||
}
|
||||
}
|
||||
|
||||
export function toRow(info: Info) {
|
||||
return {
|
||||
id: info.id,
|
||||
projectID: info.projectID,
|
||||
parentID: info.parentID,
|
||||
slug: info.slug,
|
||||
directory: info.directory,
|
||||
title: info.title,
|
||||
version: info.version,
|
||||
share_url: info.share?.url,
|
||||
summary_additions: info.summary?.additions,
|
||||
summary_deletions: info.summary?.deletions,
|
||||
summary_files: info.summary?.files,
|
||||
summary_diffs: info.summary?.diffs,
|
||||
revert_messageID: info.revert?.messageID,
|
||||
revert_partID: info.revert?.partID,
|
||||
revert_snapshot: info.revert?.snapshot,
|
||||
revert_diff: info.revert?.diff,
|
||||
permission: info.permission,
|
||||
time_created: info.time.created,
|
||||
time_updated: info.time.updated,
|
||||
time_compacting: info.time.compacting,
|
||||
time_archived: info.time.archived,
|
||||
}
|
||||
}
|
||||
|
||||
export const Info = z
|
||||
.object({
|
||||
id: Identifier.schema("session"),
|
||||
|
|
@ -256,9 +230,10 @@ export namespace Session {
|
|||
)
|
||||
|
||||
export const touch = fn(Identifier.schema("session"), async (sessionID) => {
|
||||
await update(sessionID, (draft) => {
|
||||
draft.time.updated = Date.now()
|
||||
})
|
||||
const now = Date.now()
|
||||
db().update(SessionTable).set({ time_updated: now }).where(eq(SessionTable.id, sessionID)).run()
|
||||
const session = await get(sessionID)
|
||||
Bus.publish(Event.Updated, { info: session })
|
||||
})
|
||||
|
||||
export async function createNext(input: {
|
||||
|
|
@ -283,21 +258,29 @@ export namespace Session {
|
|||
},
|
||||
}
|
||||
log.info("created", result)
|
||||
db().insert(SessionTable).values(toRow(result)).run()
|
||||
db()
|
||||
.insert(SessionTable)
|
||||
.values({
|
||||
id: result.id,
|
||||
projectID: result.projectID,
|
||||
parentID: result.parentID,
|
||||
slug: result.slug,
|
||||
directory: result.directory,
|
||||
title: result.title,
|
||||
version: result.version,
|
||||
permission: result.permission,
|
||||
time_created: result.time.created,
|
||||
time_updated: result.time.updated,
|
||||
})
|
||||
.run()
|
||||
Bus.publish(Event.Created, {
|
||||
info: result,
|
||||
})
|
||||
const cfg = await Config.get()
|
||||
if (!result.parentID && (Flag.OPENCODE_AUTO_SHARE || cfg.share === "auto"))
|
||||
share(result.id)
|
||||
.then((share) => {
|
||||
update(result.id, (draft) => {
|
||||
draft.share = share
|
||||
})
|
||||
})
|
||||
.catch(() => {
|
||||
// Silently ignore sharing errors during session creation
|
||||
})
|
||||
share(result.id).catch(() => {
|
||||
// Silently ignore sharing errors during session creation
|
||||
})
|
||||
Bus.publish(Event.Updated, {
|
||||
info: result,
|
||||
})
|
||||
|
|
@ -404,7 +387,6 @@ export namespace Session {
|
|||
})
|
||||
|
||||
export const remove = fn(Identifier.schema("session"), async (sessionID) => {
|
||||
const project = Instance.project
|
||||
try {
|
||||
const session = await get(sessionID)
|
||||
for (const child of await children(sessionID)) {
|
||||
|
|
@ -422,13 +404,11 @@ export namespace Session {
|
|||
})
|
||||
|
||||
export const updateMessage = fn(MessageV2.Info, async (msg) => {
|
||||
const createdAt = msg.role === "user" ? msg.time.created : msg.time.created
|
||||
db()
|
||||
.insert(MessageTable)
|
||||
.values({
|
||||
id: msg.id,
|
||||
sessionID: msg.sessionID,
|
||||
createdAt,
|
||||
data: msg,
|
||||
})
|
||||
.onConflictDoUpdate({ target: MessageTable.id, set: { data: msg } })
|
||||
|
|
|
|||
|
|
@ -166,10 +166,13 @@ export namespace SessionPrompt {
|
|||
})
|
||||
}
|
||||
if (permissions.length > 0) {
|
||||
session.permission = permissions
|
||||
await Session.update(session.id, (draft) => {
|
||||
draft.permission = permissions
|
||||
})
|
||||
Session.update(
|
||||
session.id,
|
||||
(draft) => {
|
||||
draft.permission = permissions
|
||||
},
|
||||
{ touch: false },
|
||||
)
|
||||
}
|
||||
|
||||
if (input.noReply === true) {
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import { Session } from "."
|
|||
import { Log } from "../util/log"
|
||||
import { splitWhen } from "remeda"
|
||||
import { db } from "../storage/db"
|
||||
import { MessageTable, PartTable } from "./session.sql"
|
||||
import { MessageTable, PartTable, SessionTable, SessionDiffTable } from "./session.sql"
|
||||
import { eq } from "drizzle-orm"
|
||||
import { Bus } from "../bus"
|
||||
import { SessionPrompt } from "./prompt"
|
||||
|
|
@ -56,13 +56,17 @@ export namespace SessionRevert {
|
|||
}
|
||||
|
||||
if (revert) {
|
||||
const session = await Session.get(input.sessionID)
|
||||
revert.snapshot = session.revert?.snapshot ?? (await Snapshot.track())
|
||||
const current = await Session.get(input.sessionID)
|
||||
revert.snapshot = current.revert?.snapshot ?? (await Snapshot.track())
|
||||
await Snapshot.revert(patches)
|
||||
if (revert.snapshot) revert.diff = await Snapshot.diff(revert.snapshot)
|
||||
const rangeMessages = all.filter((msg) => msg.info.id >= revert!.messageID)
|
||||
const diffs = await SessionSummary.computeDiff({ messages: rangeMessages })
|
||||
await Storage.write(["session_diff", input.sessionID], diffs)
|
||||
db()
|
||||
.insert(SessionDiffTable)
|
||||
.values({ sessionID: input.sessionID, data: diffs })
|
||||
.onConflictDoUpdate({ target: SessionDiffTable.sessionID, set: { data: diffs } })
|
||||
.run()
|
||||
Bus.publish(Session.Event.Diff, {
|
||||
sessionID: input.sessionID,
|
||||
diff: diffs,
|
||||
|
|
@ -85,10 +89,21 @@ export namespace SessionRevert {
|
|||
const session = await Session.get(input.sessionID)
|
||||
if (!session.revert) return session
|
||||
if (session.revert.snapshot) await Snapshot.restore(session.revert.snapshot)
|
||||
const next = await Session.update(input.sessionID, (draft) => {
|
||||
draft.revert = undefined
|
||||
})
|
||||
return next
|
||||
const now = Date.now()
|
||||
db()
|
||||
.update(SessionTable)
|
||||
.set({
|
||||
revert_messageID: null,
|
||||
revert_partID: null,
|
||||
revert_snapshot: null,
|
||||
revert_diff: null,
|
||||
time_updated: now,
|
||||
})
|
||||
.where(eq(SessionTable.id, input.sessionID))
|
||||
.run()
|
||||
const updated = await Session.get(input.sessionID)
|
||||
Bus.publish(Session.Event.Updated, { info: updated })
|
||||
return updated
|
||||
}
|
||||
|
||||
export async function cleanup(session: Session.Info) {
|
||||
|
|
@ -116,8 +131,19 @@ export namespace SessionRevert {
|
|||
})
|
||||
}
|
||||
}
|
||||
await Session.update(sessionID, (draft) => {
|
||||
draft.revert = undefined
|
||||
})
|
||||
const now = Date.now()
|
||||
db()
|
||||
.update(SessionTable)
|
||||
.set({
|
||||
revert_messageID: null,
|
||||
revert_partID: null,
|
||||
revert_snapshot: null,
|
||||
revert_diff: null,
|
||||
time_updated: now,
|
||||
})
|
||||
.where(eq(SessionTable.id, sessionID))
|
||||
.run()
|
||||
const updated = await Session.get(sessionID)
|
||||
Bus.publish(Session.Event.Updated, { info: updated })
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ import { Log } from "@/util/log"
|
|||
import path from "path"
|
||||
import { Instance } from "@/project/instance"
|
||||
import { db } from "@/storage/db"
|
||||
import { SessionDiffTable } from "./session.sql"
|
||||
import { SessionDiffTable, SessionTable } from "./session.sql"
|
||||
import { eq } from "drizzle-orm"
|
||||
import { Bus } from "@/bus"
|
||||
|
||||
|
|
@ -49,13 +49,19 @@ export namespace SessionSummary {
|
|||
return files.has(x.file)
|
||||
}),
|
||||
)
|
||||
await Session.update(input.sessionID, (draft) => {
|
||||
draft.summary = {
|
||||
additions: diffs.reduce((sum, x) => sum + x.additions, 0),
|
||||
deletions: diffs.reduce((sum, x) => sum + x.deletions, 0),
|
||||
files: diffs.length,
|
||||
}
|
||||
})
|
||||
const now = Date.now()
|
||||
db()
|
||||
.update(SessionTable)
|
||||
.set({
|
||||
summary_additions: diffs.reduce((sum, x) => sum + x.additions, 0),
|
||||
summary_deletions: diffs.reduce((sum, x) => sum + x.deletions, 0),
|
||||
summary_files: diffs.length,
|
||||
time_updated: now,
|
||||
})
|
||||
.where(eq(SessionTable.id, input.sessionID))
|
||||
.run()
|
||||
const session = await Session.get(input.sessionID)
|
||||
Bus.publish(Session.Event.Updated, { info: session })
|
||||
db()
|
||||
.insert(SessionDiffTable)
|
||||
.values({ sessionID: input.sessionID, data: diffs })
|
||||
|
|
|
|||
|
|
@ -145,7 +145,6 @@ export async function migrateFromJson(sqlite: Database, customStorageDir?: strin
|
|||
.values({
|
||||
id: data.id,
|
||||
sessionID: data.sessionID,
|
||||
createdAt: data.time?.created ?? Date.now(),
|
||||
data,
|
||||
})
|
||||
.onConflictDoNothing()
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
// Auto-generated - do not edit
|
||||
import m0 from "../../migration/0000_easy_albert_cleary.sql" with { type: "text" }
|
||||
import m0 from "../../migration/0000_vengeful_the_watchers.sql" with { type: "text" }
|
||||
|
||||
export const migrations = [{ name: "0000_easy_albert_cleary", sql: m0 }]
|
||||
export const migrations = [
|
||||
{ name: "0000_vengeful_the_watchers", sql: m0 },
|
||||
]
|
||||
|
|
|
|||
|
|
@ -0,0 +1,21 @@
|
|||
import { AsyncLocalStorage } from "node:async_hooks"
|
||||
|
||||
export namespace Context {
|
||||
export class NotFound extends Error {}
|
||||
|
||||
export function create<T>() {
|
||||
const storage = new AsyncLocalStorage<T>()
|
||||
return {
|
||||
use() {
|
||||
const result = storage.getStore()
|
||||
if (!result) {
|
||||
throw new NotFound()
|
||||
}
|
||||
return result
|
||||
},
|
||||
provide<R>(value: T, fn: () => R) {
|
||||
return storage.run(value, fn)
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue