style(storage,workspace): satisfy the repository lint gate
eslint --fix formatting sweep plus the manual residue: sync method bodies drop async behind Promise-returning signatures (the sqlite unit routes primitives through a settle() guard preserving the never-throws- synchronously contract), catch callbacks type their reason as unknown, loadAll's global slot is plain unknown (null semantics stay in JSDoc), a non-null assertion becomes a narrowing, and unsafe any assignments in tests gain explicit types. One justified eslint-disable for prefer-promise-reject-errors follows the core/session precedent — wrapping would discard the original StorageError code.
This commit is contained in:
@@ -62,24 +62,25 @@ export class SqliteKvUnit implements KvUnit {
|
||||
: undefined
|
||||
}
|
||||
|
||||
async loadAll(): Promise<{ tables: Record<string, Record<string, unknown>>; global: unknown | null }> {
|
||||
this.ensureOpen()
|
||||
const tables: Record<string, Record<string, unknown>> = {}
|
||||
for (const [name, statements] of this.tables) {
|
||||
// Null prototype: record keys are arbitrary strings, so '__proto__'
|
||||
// must land as an own property instead of mutating the prototype.
|
||||
const records: Record<string, unknown> = Object.create(null) as Record<string, unknown>
|
||||
for (const row of statements.selectAll.all() as unknown as Array<{ key: string; value: string }>) {
|
||||
records[row.key] = this.parseValue(row.value, `table '${name}' key '${row.key}'`)
|
||||
loadAll(): Promise<{ tables: Record<string, Record<string, unknown>>; global: unknown }> {
|
||||
return this.settle(() => {
|
||||
const tables: Record<string, Record<string, unknown>> = {}
|
||||
for (const [name, statements] of this.tables) {
|
||||
// Null prototype: record keys are arbitrary strings, so '__proto__'
|
||||
// must land as an own property instead of mutating the prototype.
|
||||
const records: Record<string, unknown> = Object.create(null) as Record<string, unknown>
|
||||
for (const row of statements.selectAll.all() as unknown as Array<{ key: string; value: string }>) {
|
||||
records[row.key] = this.parseValue(row.value, `table '${name}' key '${row.key}'`)
|
||||
}
|
||||
tables[name] = records
|
||||
}
|
||||
tables[name] = records
|
||||
}
|
||||
let global: unknown = null
|
||||
if (this.globalSelect !== undefined) {
|
||||
const row = this.globalSelect.get(this.descriptor.name) as { value: string } | undefined
|
||||
if (row !== undefined) global = this.parseValue(row.value, 'global slot')
|
||||
}
|
||||
return { tables, global }
|
||||
let global: unknown = null
|
||||
if (this.globalSelect !== undefined) {
|
||||
const row = this.globalSelect.get(this.descriptor.name) as { value: string } | undefined
|
||||
if (row !== undefined) global = this.parseValue(row.value, 'global slot')
|
||||
}
|
||||
return { tables, global }
|
||||
})
|
||||
}
|
||||
|
||||
/** Parse one stored value column, mapping bad JSON to `malformed-medium`. */
|
||||
@@ -95,28 +96,48 @@ export class SqliteKvUnit implements KvUnit {
|
||||
}
|
||||
}
|
||||
|
||||
async putRecord(table: string, key: string, value: unknown): Promise<void> {
|
||||
this.ensureOpen()
|
||||
this.statementsFor(table).upsert.run(key, JSON.stringify(value))
|
||||
putRecord(table: string, key: string, value: unknown): Promise<void> {
|
||||
return this.settle(() => {
|
||||
this.statementsFor(table).upsert.run(key, JSON.stringify(value))
|
||||
})
|
||||
}
|
||||
|
||||
async deleteRecord(table: string, key: string): Promise<void> {
|
||||
this.ensureOpen()
|
||||
this.statementsFor(table).remove.run(key)
|
||||
deleteRecord(table: string, key: string): Promise<void> {
|
||||
return this.settle(() => {
|
||||
this.statementsFor(table).remove.run(key)
|
||||
})
|
||||
}
|
||||
|
||||
async setGlobal(value: unknown): Promise<void> {
|
||||
this.ensureOpen()
|
||||
if (this.globalUpsert === undefined) {
|
||||
throw new Error(`kv unit '${this.descriptor.name}' declared no global slot`)
|
||||
setGlobal(value: unknown): Promise<void> {
|
||||
return this.settle(() => {
|
||||
if (this.globalUpsert === undefined) {
|
||||
throw new Error(`kv unit '${this.descriptor.name}' declared no global slot`)
|
||||
}
|
||||
this.globalUpsert.run(this.descriptor.name, JSON.stringify(value))
|
||||
})
|
||||
}
|
||||
|
||||
close(): Promise<void> {
|
||||
if (!this.closed) {
|
||||
this.closed = true
|
||||
this.onClose()
|
||||
}
|
||||
this.globalUpsert.run(this.descriptor.name, JSON.stringify(value))
|
||||
return Promise.resolve()
|
||||
}
|
||||
|
||||
async close(): Promise<void> {
|
||||
if (this.closed) return
|
||||
this.closed = true
|
||||
this.onClose()
|
||||
/**
|
||||
* Run one synchronous primitive behind the closed guard, mapping a throw to
|
||||
* a rejection so the Promise-returning contract never throws synchronously.
|
||||
*/
|
||||
private settle<T>(operation: () => T): Promise<T> {
|
||||
try {
|
||||
this.ensureOpen()
|
||||
return Promise.resolve(operation())
|
||||
} catch (error) {
|
||||
// Non-Error throws can only enter through JSON.stringify propagating a
|
||||
// value's own toJSON throw; wrap those, preserve every real Error.
|
||||
return Promise.reject(error instanceof Error ? error : new Error(String(error)))
|
||||
}
|
||||
}
|
||||
|
||||
private ensureOpen(): void {
|
||||
|
||||
@@ -171,6 +171,17 @@ describe('sqlite backend specifics', () => {
|
||||
await reopened.close()
|
||||
})
|
||||
|
||||
it('wraps a non-Error toJSON throw into an Error rejection', async () => {
|
||||
const backend = backendAt(':memory:')
|
||||
const unit = await backend.kv.open(DESCRIPTOR)
|
||||
// JSON.stringify propagates a value's own toJSON throw verbatim; the unit
|
||||
// must still reject with an Error instance.
|
||||
const hostile = { toJSON: () => { throw 'not an error' } }
|
||||
await expect(unit.putRecord('records', 'k', hostile)).rejects.toThrow('not an error')
|
||||
await expect(unit.putRecord('records', 'k', hostile)).rejects.toBeInstanceOf(Error)
|
||||
await backend.close()
|
||||
})
|
||||
|
||||
it('rejects setGlobal on a unit without a global slot and writes to undeclared tables', async () => {
|
||||
const backend = backendAt(':memory:')
|
||||
const unit = await backend.kv.open({ ...DESCRIPTOR, hasGlobal: false })
|
||||
|
||||
Reference in New Issue
Block a user