work-in-progress atproto PDS
typescript atproto pds atcute
4
fork

Configure Feed

Select the types of activity you want to include in your feed.

chore: adopt tsgolint

Mary 9cdb8e56 83972c51

+215 -240
+24
.oxlintrc.json
··· 1 + { 2 + "$schema": "https://unpkg.com/oxlint/configuration_schema.json", 3 + "ignorePatterns": ["packages/lexicons/lib/**"], 4 + "options": { 5 + "typeAware": true, 6 + "typeCheck": true 7 + }, 8 + "categories": { 9 + "correctness": "warn", 10 + "suspicious": "warn", 11 + "perf": "warn" 12 + }, 13 + "rules": { 14 + "typescript/consistent-return": "off" 15 + }, 16 + "overrides": [ 17 + { 18 + "files": ["*.test.ts"], 19 + "rules": { 20 + "typescript/no-floating-promises": "off" 21 + } 22 + } 23 + ] 24 + }
+1 -1
CLAUDE.md
··· 7 7 - Bun and pnpm is managed by mise, to run commands, use `mise exec -- bun ...` 8 8 - install dependencies with `pnpm install` 9 9 - format via `bun run fmt` (prettier, in root directory) 10 - - lint via `bun run lint` (oxlint, in root directory) 10 + - lint and typecheck via `bun run lint` (oxlint, in root directory) 11 11 - run tests via `bun test` (bun, in package) 12 12 - build via `bun run build` (tsgo + assets, in package) 13 13 - check `pnpm view <package>` before adding a new dependency
+2 -1
package.json
··· 11 11 "devDependencies": { 12 12 "@typescript/native-preview": "7.0.0-dev.20260416.1", 13 13 "oxfmt": "^0.45.0", 14 - "oxlint": "^1.60.0" 14 + "oxlint": "^1.60.0", 15 + "oxlint-tsgolint": "^0.21.1" 15 16 } 16 17 }
+1
packages/danaus/src/accounts/legacy-auth.ts
··· 160 160 const rows = this.#db.select().from(t.appPassword).where(eq(t.appPassword.did, did)).all(); 161 161 162 162 for (const row of rows) { 163 + // oxlint-disable-next-line no-await-in-loop -- sequential password check, short-circuits on match 163 164 const valid = await verifyPassword(password, row.password_hash); 164 165 if (valid) { 165 166 return row;
+4
packages/danaus/src/accounts/manager.ts
··· 48 48 .from(t.account) 49 49 .where((f) => { 50 50 return and( 51 + // oxlint-disable-next-line no-unsafe-type-assertion -- branded type from toLowerCase 51 52 isDid(actor) ? eq(f.did, actor) : eq(sql`lower(${f.handle})`, actor.toLowerCase() as Handle), 52 53 !includeDeactivated ? isNull(f.deactivated_at) : undefined, 53 54 !includeTakenDown ? isNull(f.takedown_ref) : undefined, ··· 292 293 const { did } = options; 293 294 294 295 // normalize to lowercase 296 + // oxlint-disable-next-line no-unsafe-type-assertion -- branded type from toLowerCase 295 297 handle = handle.toLowerCase() as Handle; 296 298 297 299 if (!isValidTld(handle)) { ··· 394 396 * @returns created account 395 397 */ 396 398 async createAccount(options: CreateAccountOptions): Promise<Account> { 399 + // oxlint-disable-next-line no-unsafe-type-assertion -- branded type from toLowerCase 397 400 const handle = options.handle.toLowerCase() as Handle; 398 401 const email = options.email.toLowerCase(); 399 402 ··· 457 460 updateAccountHandle(did: Did, handle: Handle): void { 458 461 this.#db 459 462 .update(t.account) 463 + // oxlint-disable-next-line no-unsafe-type-assertion -- branded type from toLowerCase 460 464 .set({ handle: handle.toLowerCase() as Handle }) 461 465 .where(eq(t.account.did, did)) 462 466 .run();
+1
packages/danaus/src/accounts/mfa.ts
··· 161 161 const credentials = this.listTotpCredentials(did); 162 162 163 163 for (const credential of credentials) { 164 + // oxlint-disable-next-line no-await-in-loop -- sequential credential check, short-circuits on match 164 165 const counter = await verifyTotpCode(credential.secret, code, credential.last_used_counter); 165 166 166 167 if (counter !== null) {
+1
packages/danaus/src/accounts/totp.ts
··· 171 171 continue; 172 172 } 173 173 174 + // oxlint-disable-next-line no-await-in-loop -- sequential TOTP verification over time window 174 175 const expectedCode = await generateHotp(secret, counter); 175 176 176 177 if (timingSafeEqual(codeBytes, Buffer.from(expectedCode))) {
+7 -7
packages/danaus/src/actors/blob-store/disk.ts
··· 48 48 const writer = file.writer(); 49 49 50 50 for await (const chunk of stream) { 51 - writer.write(chunk); 51 + void writer.write(chunk); 52 52 } 53 53 54 54 await writer.end(); ··· 63 63 64 64 async makePermanent(tempKey: string, cid: string): Promise<void> { 65 65 const tempPath = this.getTempPath(tempKey); 66 - const path = this.getStoredPath(cid); 66 + const storedPath = this.getStoredPath(cid); 67 67 68 68 const temp = Bun.file(tempPath); 69 - const stored = Bun.file(path); 69 + const stored = Bun.file(storedPath); 70 70 71 71 if (await stored.exists()) { 72 72 try { ··· 86 86 await mkdir(this.directory, { recursive: true }); 87 87 88 88 try { 89 - await rename(tempPath, path); 89 + await rename(tempPath, storedPath); 90 90 } catch (err) { 91 - blobStoreLogger.warn('rename failed, falling back to copy', { err, tempPath, path }); 92 - await copyFile(tempPath, path); 91 + blobStoreLogger.warn('rename failed, falling back to copy', { err, tempPath, storedPath }); 92 + await copyFile(tempPath, storedPath); 93 93 await rm(tempPath, { force: true }); 94 94 } 95 95 } ··· 119 119 async deleteMany(cid: string[]): Promise<void> { 120 120 const set = new Set(cid); 121 121 122 - await Promise.all(Array.from(set, (cid) => this.delete(cid))); 122 + await Promise.all(Array.from(set, (c) => this.delete(c))); 123 123 } 124 124 125 125 async deleteAll(): Promise<void> {
+2 -2
packages/danaus/src/actors/blob-store/s3.ts
··· 46 46 const writer = temp.writer(); 47 47 48 48 for await (const chunk of stream) { 49 - writer.write(chunk); 49 + void writer.write(chunk); 50 50 } 51 51 52 52 await writer.end(); ··· 99 99 async deleteMany(cid: string[]): Promise<void> { 100 100 const set = new Set(cid); 101 101 102 - await Promise.all(Array.from(set, (cid) => this.delete(cid))); 102 + await Promise.all(Array.from(set, (c) => this.delete(c))); 103 103 } 104 104 }
+1 -11
packages/danaus/src/actors/blobs/transactor.ts
··· 5 5 6 6 import { chunked } from '#app/utils/misc.ts'; 7 7 8 - import type { ActorDbConnection } from '../actor-store-types'; 9 - import type { BlobStore } from '../blob-store/types'; 10 8 import { t } from '../db'; 11 9 import type { RepoBlobHandler, RepoBlobWriteOptions, RepoRecordWrite } from '../repo/side-effects'; 12 10 ··· 17 15 * blob metadata writer. 18 16 */ 19 17 export class BlobTransactor extends BlobReader implements RepoBlobHandler { 20 - /** 21 - * create a blob writer. 22 - * @param db actor database handle 23 - * @param blobStore actor blob store 24 - */ 25 - constructor(db: ActorDbConnection, blobStore: BlobStore) { 26 - super(db, blobStore); 27 - } 28 - 29 18 /** 30 19 * apply blob updates after persisting the commit. 31 20 * @param options blob update options ··· 154 143 continue; 155 144 } 156 145 146 + // oxlint-disable-next-line no-await-in-loop -- sequential blob promotion 157 147 await this.blobStore.makePermanent(row.tempKey, row.cid); 158 148 this.db.update(t.blob).set({ temp_key: null }).where(eq(t.blob.cid, row.cid)).run(); 159 149 }
+1
packages/danaus/src/actors/blobs/utils.ts
··· 38 38 } 39 39 40 40 for (const key in record) { 41 + // oxlint-disable-next-line no-unsafe-type-assertion -- recursive record traversal 41 42 const value = (record as any)[key]; 42 43 findBlobReferences(value, map, layer + 1); 43 44 }
+1
packages/danaus/src/actors/manager.ts
··· 138 138 const result = store.blob.listBlobs({ limit: 250, cursor }); 139 139 cursor = result.at(-1); 140 140 141 + // oxlint-disable-next-line no-await-in-loop -- paginated deletion 141 142 await blobStore.deleteMany(result); 142 143 } while (cursor !== undefined); 143 144 });
-9
packages/danaus/src/actors/preferences/transactor.ts
··· 1 - import type { ActorDbConnection } from '../actor-store-types'; 2 1 import { t } from '../db'; 3 2 4 3 import { PreferenceReader, type LegacyPreferences } from './reader'; ··· 7 6 * preference writer. 8 7 */ 9 8 export class PreferenceTransactor extends PreferenceReader { 10 - /** 11 - * create a preference writer. 12 - * @param db actor database handle 13 - */ 14 - constructor(db: ActorDbConnection) { 15 - super(db); 16 - } 17 - 18 9 /** 19 10 * replace legacy preferences. 20 11 * @param preferences legacy preferences
-9
packages/danaus/src/actors/record/transactor.ts
··· 2 2 3 3 import { eq, inArray, sql } from 'drizzle-orm'; 4 4 5 - import type { ActorDbConnection } from '../actor-store-types'; 6 5 import { t } from '../db'; 7 6 import type { RepoRecordIndexer } from '../repo/side-effects'; 8 7 ··· 13 12 * record index writer. 14 13 */ 15 14 export class RecordTransactor extends RecordReader implements RepoRecordIndexer { 16 - /** 17 - * create a record writer. 18 - * @param db actor database handle 19 - */ 20 - constructor(db: ActorDbConnection) { 21 - super(db); 22 - } 23 - 24 15 /** 25 16 * upsert record index entries. 26 17 * @param records record index entries
+9 -3
packages/danaus/src/actors/repo/transactor.ts
··· 191 191 const commitOps: RepoCommitOp[] = []; 192 192 193 193 for (const write of writes) { 194 - const rkey = (write.rkey ?? (write.action === 'create' ? TID.now() : undefined)) as 195 - | RecordKey 196 - | undefined; 194 + const rkey = write.rkey ?? (write.action === 'create' ? TID.now() : undefined); 197 195 if (!rkey) { 198 196 throw new InvalidRequestError({ 199 197 error: 'InvalidRequest', ··· 202 200 } 203 201 204 202 const path: `${Nsid}/${RecordKey}` = `${write.collection}/${rkey}`; 203 + // oxlint-disable-next-line no-await-in-loop -- sequential repo writes, each depends on prior rootCid 205 204 const walker = await NodeWalker.create(nodeStore, rootCid); 205 + // oxlint-disable-next-line no-await-in-loop 206 206 const prevValue = await walker.findRpath(path); 207 207 const prevCid = prevValue ? prevValue.$link : null; 208 208 ··· 228 228 } 229 229 230 230 if (write.action === 'delete') { 231 + // oxlint-disable-next-line no-await-in-loop 231 232 rootCid = await wrangler.deleteRecord(rootCid, path); 232 233 results.push({ 233 234 action: 'delete', ··· 249 250 } 250 251 251 252 const recordBytes = CBOR.encode(write.record); 253 + // oxlint-disable-next-line no-await-in-loop 252 254 const recordCid = await CID.create(0x71, recordBytes); 253 255 const recordLink = CID.toCidLink(recordCid); 254 256 257 + // oxlint-disable-next-line no-await-in-loop 255 258 rootCid = await wrangler.putRecord(rootCid, path, recordLink); 256 259 257 260 const recordCidStr = CID.toString(recordCid); ··· 450 453 continue; 451 454 } 452 455 456 + // oxlint-disable-next-line no-await-in-loop -- sequential node fetching 453 457 const node = await nodeStore.get(cid); 458 + // oxlint-disable-next-line no-await-in-loop 454 459 blocks.set(cid, await node.serialize()); 455 460 } 456 461 ··· 466 471 467 472 for (const record of recordDeletes) { 468 473 const path: `${Nsid}/${RecordKey}` = `${record.collection}/${record.rkey}`; 474 + // oxlint-disable-next-line no-await-in-loop -- sequential proof building 469 475 const proof = await buildExclusionProof(nodeStore, rootCid, path); 470 476 for (const cid of proof) { 471 477 proofCids.add(cid);
+1
packages/danaus/src/api/app.bsky/actor.getPreferences.ts
··· 18 18 const auth = await authVerifier.authorization(request); 19 19 20 20 let preferences = await actorManager.read(auth.did, (store) => { 21 + // oxlint-disable-next-line no-unsafe-type-assertion -- legacy pref shape matches schema 21 22 return store.pref.getLegacyPreferences() as v.InferOutput<AppBskyActorDefs.preferencesSchema>; 22 23 }); 23 24
+2
packages/danaus/src/api/com.atproto/repo.describeRepo.ts
··· 43 43 44 44 let didDoc: unknown; 45 45 try { 46 + // oxlint-disable-next-line no-unsafe-type-assertion -- DID is known to be atproto 46 47 didDoc = await didDocumentResolver.resolve(account.did as AtprotoDid); 47 48 } catch { 48 49 throw new InvalidRequestError({ ··· 67 68 return json({ 68 69 handle: handle, 69 70 did: account.did, 71 + // oxlint-disable-next-line no-unsafe-type-assertion -- DID document from resolver 70 72 didDoc: didDoc as Record<string, unknown>, 71 73 collections: collections, 72 74 handleIsCorrect: handleIsCorrect,
+1
packages/danaus/src/api/com.atproto/repo.getRecord.ts
··· 34 34 return json({ 35 35 uri: record.uri, 36 36 cid: record.cid, 37 + // oxlint-disable-next-line no-unsafe-type-assertion -- CBOR-decoded record 37 38 value: record.record as Record<string, unknown>, 38 39 }); 39 40 },
+3
packages/danaus/src/api/com.atproto/repo.listRecords.ts
··· 31 31 }); 32 32 33 33 const output = records.map((record) => { 34 + // oxlint-disable-next-line no-unsafe-type-assertion -- includeRecords guarantees RepoRecordEntry 34 35 const entry = record as RepoRecordEntry; 35 36 return { 36 37 uri: entry.uri, 37 38 cid: entry.cid, 39 + // oxlint-disable-next-line no-unsafe-type-assertion -- CBOR-decoded record 38 40 value: entry.record as Record<string, unknown>, 39 41 }; 40 42 }); 41 43 44 + // oxlint-disable-next-line no-unsafe-type-assertion -- same as above 42 45 const last = records.at(-1) as RepoRecordEntry | undefined; 43 46 const nextCursor = last ? last.uri.split('/').pop() : undefined; 44 47
+4 -3
packages/danaus/src/api/com.atproto/repo.uploadBlob.ts
··· 28 28 29 29 const blobStore = actorManager.resources.createBlobStore(auth.did); 30 30 31 - const { stream, result } = hashingStream(request.body!, config.service.blobs.maxUploadSize); 31 + const { stream, result } = hashingStream(request.body, config.service.blobs.maxUploadSize); 32 32 33 33 const tempKey = await blobStore.putTemp(stream); 34 34 ··· 105 105 106 106 const stream = new ReadableStream<Uint8Array>({ 107 107 start() { 108 + // oxlint-disable-next-line no-unsafe-type-assertion -- ReadableStream reader type mismatch 108 109 reader = input.getReader() as any; 109 110 }, 110 111 async pull(controller) { ··· 126 127 }); 127 128 reject(err); 128 129 controller.error(new Error('blob too large')); 129 - reader.cancel(); 130 + void reader.cancel(); 130 131 return; 131 132 } 132 133 ··· 138 139 } 139 140 }, 140 141 cancel() { 141 - reader.cancel(); 142 + void reader.cancel(); 142 143 }, 143 144 }); 144 145
+1 -2
packages/danaus/src/api/com.atproto/server.createSession.ts
··· 1 1 import { ComAtprotoServerCreateSession } from '@atcute/atproto'; 2 - import type { Handle } from '@atcute/lexicons'; 3 2 import { AuthRequiredError, InvalidRequestError, json, type XRPCRouter } from '@atcute/xrpc-server'; 4 3 5 4 import { AccountStatus, formatAccountStatus } from '#app/accounts/types.ts'; ··· 45 44 46 45 return json({ 47 46 did: account.did, 48 - handle: handle as Handle, 47 + handle: handle, 49 48 email: account.email ?? undefined, 50 49 emailConfirmed: account.email_confirmed_at !== null, 51 50 accessJwt: accessJwt,
+2
packages/danaus/src/api/com.atproto/server.describeServer.ts
··· 17 17 email: config.service.branding.contactEmailAddress ?? undefined, 18 18 }, 19 19 links: { 20 + // oxlint-disable-next-line no-unsafe-type-assertion -- config URL string to branded URI 20 21 privacyPolicy: (config.service.branding.privacyPolicyUrl as GenericUri | null) ?? undefined, 22 + // oxlint-disable-next-line no-unsafe-type-assertion 21 23 termsOfService: (config.service.branding.termsOfServiceUrl as GenericUri | null) ?? undefined, 22 24 }, 23 25 });
+2
packages/danaus/src/api/com.atproto/sync.getRecord.ts
··· 45 45 continue; 46 46 } 47 47 48 + // oxlint-disable-next-line no-await-in-loop -- sequential node fetching 48 49 const node = await nodeStore.get(cid); 50 + // oxlint-disable-next-line no-await-in-loop 49 51 blocks.set(cid, await node.serialize()); 50 52 } 51 53
+1
packages/danaus/src/api/com.atproto/sync.listRepos.ts
··· 33 33 }> = []; 34 34 35 35 for (const account of accounts) { 36 + // oxlint-disable-next-line no-await-in-loop -- sequential per-account reads 36 37 const root = await actorManager.read(account.did, (store) => store.repo.getRoot()); 37 38 if (!root) { 38 39 continue;
+1
packages/danaus/src/api/local.danaus/account.createAccount.ts
··· 192 192 } 193 193 194 194 if (params.recoveryKey !== undefined) { 195 + // oxlint-disable-next-line no-unsafe-type-assertion -- branded DID type 195 196 rotationKeys.unshift(params.recoveryKey as Did<'key'>); 196 197 } 197 198
+1 -2
packages/danaus/src/api/local.danaus/admin.getSubjectStatus.ts
··· 45 45 throw new InvalidRequestError({ error: 'InvalidRequest', description: parsed.error }); 46 46 } 47 47 48 - const recordUri = 49 - `at://${parsed.value.repo}/${parsed.value.collection}/${parsed.value.rkey}` as CanonicalResourceUri; 48 + const recordUri: CanonicalResourceUri = `at://${parsed.value.repo}/${parsed.value.collection}/${parsed.value.rkey}`; 50 49 const { takedown, cid } = await actorManager.read(parsed.value.repo, (store) => { 51 50 return { 52 51 takedown: store.record.getRecordTakedownStatus(recordUri),
+1 -2
packages/danaus/src/api/local.danaus/admin.updateSubjectStatus.ts
··· 28 28 throw new InvalidRequestError({ error: 'InvalidRequest', description: parsed.error }); 29 29 } 30 30 31 - const recordUri = 32 - `at://${parsed.value.repo}/${parsed.value.collection}/${parsed.value.rkey}` as CanonicalResourceUri; 31 + const recordUri: CanonicalResourceUri = `at://${parsed.value.repo}/${parsed.value.collection}/${parsed.value.rkey}`; 33 32 await actorManager.transact(parsed.value.repo, async (store) => { 34 33 const existing = store.record.getRecord(recordUri); 35 34 if (!existing) {
+1
packages/danaus/src/auth/scopes.ts
··· 17 17 const authScopesValues = new Set(Object.values(AuthScope)); 18 18 19 19 export const isAuthScope = (value: unknown): value is AuthScope => { 20 + // oxlint-disable-next-line no-unsafe-type-assertion -- type guard implementation 20 21 return authScopesValues.has(value as AuthScope); 21 22 };
+1
packages/danaus/src/auth/verifier.ts
··· 478 478 return null; 479 479 } 480 480 481 + // oxlint-disable-next-line no-unsafe-type-assertion -- branded type from URL path 481 482 return url.pathname.slice('/xrpc/'.length) as Nsid; 482 483 }; 483 484
+2
packages/danaus/src/auth/web.ts
··· 12 12 * @returns token or null 13 13 */ 14 14 export const readWebSessionToken = (request: Request): string | null => { 15 + // oxlint-disable-next-line no-unsafe-type-assertion -- Bun-specific API access 15 16 const cookies = (request as BunRequest).cookies; 16 17 17 18 return cookies.get(WEB_SESSION_COOKIE) ?? null; ··· 24 25 * @param options cookie options 25 26 */ 26 27 export const setWebSessionToken = (request: Request, token: string, options: CookieInit = {}): void => { 28 + // oxlint-disable-next-line no-unsafe-type-assertion -- Bun-specific API access 27 29 const cookies = (request as BunRequest).cookies; 28 30 29 31 cookies.set(WEB_SESSION_COOKIE, token, options);
+1
packages/danaus/src/lexicon/cache.ts
··· 446 446 447 447 // parse the reference 448 448 const hashIndex = ref.indexOf('#'); 449 + // oxlint-disable-next-line no-unsafe-type-assertion -- branded type from parsed reference 449 450 const nsid = ref.slice(0, hashIndex) as Nsid; 450 451 const defId = ref.slice(hashIndex + 1); 451 452
+4 -2
packages/danaus/src/pds-server.ts
··· 56 56 disposables.defer(() => context.identityCache.dispose()); 57 57 disposables.defer(() => context.accountDb.$client.close()); 58 58 59 + // oxlint-disable-next-line unbound-method -- factory return, not a class method 59 60 const { wrap, adapter } = createBunWebSocket(); 60 61 const router = new XRPCRouter({ 61 62 websocket: adapter, ··· 124 125 { version: `danaus-${context.config.service.version}` }, 125 126 { headers: corsHeaders }, 126 127 ), 128 + // oxlint-disable-next-line unbound-method -- factory return, not a class method 127 129 '/xrpc/*': wrapped.fetch, 128 130 129 - '/*': (request, server) => runWithServer(server, () => web.fetch(request)), 131 + '/*': (request, bunServer) => runWithServer(bunServer, () => web.fetch(request)), 130 132 }, 131 133 }); 132 134 disposables.defer(() => { 133 135 httpLogger.info('server stopping'); 134 - server.stop(); 136 + void server.stop(); 135 137 }); 136 138 137 139 httpLogger.info('server started', { port: server.port, hostname: serviceHostname });
+1 -1
packages/danaus/src/proxy/index.ts
··· 36 36 targets: Map<string, ProxyTargetConfig>; 37 37 authVerifier: AuthVerifier; 38 38 actorManager: ActorManager; 39 - didDocumentResolver: DidDocumentResolver<string>; 39 + didDocumentResolver: DidDocumentResolver; 40 40 } 41 41 42 42 /**
+3 -1
packages/danaus/src/proxy/utils.ts
··· 21 21 */ 22 22 export const parseProxyHeader = async ( 23 23 targets: Map<string, ProxyTargetConfig>, 24 - didDocumentResolver: DidDocumentResolver<string>, 24 + didDocumentResolver: DidDocumentResolver, 25 25 header: string, 26 26 nsid: Nsid, 27 27 ): Promise<ProxyTarget | null> => { ··· 40 40 const audience = targetConfig?.to ?? header; 41 41 42 42 const hashIndex = audience.indexOf('#'); 43 + // oxlint-disable-next-line no-unsafe-type-assertion -- branded type from validated proxy header 43 44 const did = audience.slice(0, hashIndex) as AtprotoDid; 45 + // oxlint-disable-next-line no-unsafe-type-assertion 44 46 const serviceId = audience.slice(hashIndex) as `#${string}`; 45 47 46 48 const didDoc = await didDocumentResolver.resolve(did);
+1
packages/danaus/src/sequencer/async-buffer.ts
··· 49 49 50 50 async *events(): AsyncGenerator<T> { 51 51 while (true) { 52 + // oxlint-disable-next-line no-await-in-loop -- event loop by design 52 53 await this.deferred.promise; 53 54 54 55 if (this.queue.size > this.maxSize) {
+4
packages/danaus/src/sequencer/events.ts
··· 179 179 const time = toIsoTime(sequencedAt); 180 180 181 181 if (eventType === 'commit') { 182 + // oxlint-disable-next-line no-unsafe-type-assertion -- discriminated by eventType 182 183 const payload = event as StoredCommit; 183 184 return { 184 185 type: 'commit', ··· 192 193 } 193 194 194 195 if (eventType === 'sync') { 196 + // oxlint-disable-next-line no-unsafe-type-assertion -- discriminated by eventType 195 197 const payload = event as StoredSync; 196 198 return { 197 199 type: 'sync', ··· 209 211 type: 'identity', 210 212 seq: seq, 211 213 time: time, 214 + // oxlint-disable-next-line no-unsafe-type-assertion -- discriminated by eventType 212 215 evt: event as IdentityEvt, 213 216 }; 214 217 } ··· 217 220 type: 'account', 218 221 seq: seq, 219 222 time: time, 223 + // oxlint-disable-next-line no-unsafe-type-assertion -- discriminated by eventType 220 224 evt: event as AccountEvt, 221 225 }; 222 226 };
+2 -2
packages/danaus/src/sequencer/sequencer.ts
··· 17 17 toSeqEvt, 18 18 type SequencerInsert, 19 19 } from './events'; 20 - import type { RepoSeqEventType, SeqEvt } from './types'; 20 + import type { SeqEvt } from './types'; 21 21 22 22 type RepoSeqRow = typeof t.repoSeq.$inferSelect; 23 23 ··· 270 270 271 271 const rowToSeqEvt = (row: RepoSeqRow): SeqEvt => { 272 272 return toSeqEvt( 273 - row.event_type as RepoSeqEventType, 273 + row.event_type, 274 274 row.seq, 275 275 row.sequenced_at, 276 276 row.event,
+6 -3
packages/danaus/src/test/seed-client.ts
··· 236 236 }), 237 237 ); 238 238 239 - const ref: RecordRef = { uri: data.uri as CanonicalResourceUri, cid: data.cid as Cid }; 239 + // oxlint-disable-next-line no-unsafe-type-assertion -- API response URI 240 + const ref: RecordRef = { uri: data.uri as CanonicalResourceUri, cid: data.cid }; 240 241 241 242 this.profiles[by] = { ref, record }; 242 243 ··· 498 499 async createStarterPack(by: Did, name: string, actors: Did[], feeds?: ResourceUri[]): Promise<RecordRef> { 499 500 const list = await this.createList(by, 'n/a', 'reference'); 500 501 for (const did of actors) { 502 + // oxlint-disable-next-line no-await-in-loop -- sequential list mutations 501 503 await this.addToList(by, did, list); 502 504 } 503 505 ··· 556 558 * @param record record value 557 559 * @param rkey optional record key 558 560 */ 559 - async createRecord<TCollection extends keyof Records & string>( 561 + async createRecord<TCollection extends keyof Records>( 560 562 did: Did, 561 563 collection: TCollection, 562 564 record: InferInput<Records[TCollection]>, ··· 575 577 ); 576 578 577 579 return { 580 + // oxlint-disable-next-line no-unsafe-type-assertion -- API response URI 578 581 uri: data.uri as CanonicalResourceUri, 579 - cid: data.cid as Cid, 582 + cid: data.cid, 580 583 }; 581 584 } 582 585
+1
packages/danaus/src/utils/keyset.ts
··· 12 12 private readonly validateKey: (key: string) => key is K; 13 13 14 14 constructor(validateKey?: (key: string) => key is K) { 15 + // oxlint-disable-next-line no-unsafe-type-assertion -- generic default fallback 15 16 this.validateKey = validateKey ?? (defaultValidator as (key: string) => key is K); 16 17 } 17 18
+1
packages/danaus/src/web/account/forms.ts
··· 131 131 // update PLC document for did:plc accounts 132 132 if (did.startsWith('did:plc:')) { 133 133 try { 134 + // oxlint-disable-next-line no-unsafe-type-assertion -- narrowed by startsWith check 134 135 await updatePlcHandle(ctx, did as Did<'plc'>, handle); 135 136 } catch (err) { 136 137 if (err instanceof PlcClientError) {
+1
packages/danaus/src/web/admin/forms.ts
··· 37 37 invalid(issue.domain(`Invalid domain`)); 38 38 } 39 39 40 + // oxlint-disable-next-line no-unsafe-type-assertion -- branded type from validated form input 40 41 const handle = `${data.handle}${data.domain}` as Handle; 41 42 42 43 try {
+1 -2
packages/danaus/src/web/controllers/account.tsx
··· 1 - import type { Did } from '@atcute/lexicons'; 2 1 import type { Controller } from '@oomfware/fetch-router'; 3 2 import { forms } from '@oomfware/forms'; 4 3 import { render } from '@oomfware/jsx'; ··· 342 341 appPasswords() { 343 342 const { legacyAuthManager } = getAppContext(); 344 343 const session = getSession(); 345 - const did = session.did as Did; 344 + const did = session.did; 346 345 347 346 const passwords = legacyAuthManager.listAppPasswords(did); 348 347
+2 -2
packages/danaus/src/web/controllers/admin.tsx
··· 77 77 limit: 50, 78 78 }); 79 79 80 - const buildHref = (nextCursor: string) => { 80 + const buildHref = (pageCursor: string) => { 81 81 return routes.admin.accounts.index.href(undefined, { 82 82 q: query || undefined, 83 - cursor: nextCursor, 83 + cursor: pageCursor, 84 84 }); 85 85 }; 86 86
+1
packages/danaus/src/web/controllers/login.tsx
··· 121 121 } 122 122 123 123 // clear the session cookie 124 + // oxlint-disable-next-line no-unsafe-type-assertion -- Bun-specific API access 124 125 (request as BunRequest).cookies.delete(WEB_SESSION_COOKIE, { path: '/' }); 125 126 126 127 redirect(routes.login.index.href());
-166
packages/danaus/src/web/lib/cache.ts
··· 1 - import { createInjectionKey, type Middleware } from '@oomfware/fetch-router'; 2 - import { getContext } from '@oomfware/fetch-router/middlewares/async-context'; 3 - 4 - // #region cache node types 5 - 6 - const enum CacheStatus { 7 - UNTERMINATED = 0, 8 - TERMINATED = 1, 9 - ERRORED = 2, 10 - } 11 - 12 - type Primitive = string | number | null | undefined | symbol | boolean; 13 - 14 - type UnterminatedCacheNode<T> = { 15 - s: CacheStatus.UNTERMINATED; 16 - v: undefined; 17 - o: WeakMap<WeakKey, CacheNode<T>> | null; 18 - p: Map<Primitive, CacheNode<T>> | null; 19 - }; 20 - 21 - type TerminatedCacheNode<T> = { 22 - s: CacheStatus.TERMINATED; 23 - v: T; 24 - o: WeakMap<WeakKey, CacheNode<T>> | null; 25 - p: Map<Primitive, CacheNode<T>> | null; 26 - }; 27 - 28 - type ErroredCacheNode<T> = { 29 - s: CacheStatus.ERRORED; 30 - v: unknown; 31 - o: WeakMap<WeakKey, CacheNode<T>> | null; 32 - p: Map<Primitive, CacheNode<T>> | null; 33 - }; 34 - 35 - type CacheNode<T> = UnterminatedCacheNode<T> | TerminatedCacheNode<T> | ErroredCacheNode<T>; 36 - 37 - const createCacheNode = <T>(): CacheNode<T> => { 38 - return { 39 - s: CacheStatus.UNTERMINATED, 40 - v: undefined, 41 - o: null, 42 - p: null, 43 - }; 44 - }; 45 - 46 - // #endregion 47 - 48 - // #region cache store 49 - 50 - type CacheStore = WeakMap<WeakKey, CacheNode<unknown>>; 51 - 52 - const cacheStoreKey = createInjectionKey<CacheStore>(); 53 - 54 - /** 55 - * middleware that provides a cache store scoped to the current request. 56 - */ 57 - export const provideCache = (): Middleware => { 58 - return async ({ store }, next) => { 59 - store.provide(cacheStoreKey, new WeakMap()); 60 - return next(); 61 - }; 62 - }; 63 - 64 - const getCacheStore = (): CacheStore | undefined => { 65 - try { 66 - return getContext().store.inject(cacheStoreKey); 67 - } catch { 68 - return undefined; 69 - } 70 - }; 71 - 72 - // #endregion 73 - 74 - // #region cache function 75 - 76 - /** 77 - * wraps a function to memoize its results for the duration of the current request. 78 - * arguments are compared by identity (===) with special handling for objects via WeakMap. 79 - * @param fn the function to memoize 80 - * @returns a memoized version of the function 81 - */ 82 - export const cache = <A extends unknown[], T>(fn: (...args: A) => T): ((...args: A) => T) => { 83 - return function (this: unknown, ...args: A): T { 84 - const store = getCacheStore(); 85 - if (!store) { 86 - return fn.apply(this, args); 87 - } 88 - 89 - let cacheNode: CacheNode<T>; 90 - const fnNode = store.get(fn) as CacheNode<T> | undefined; 91 - if (fnNode === undefined) { 92 - cacheNode = createCacheNode(); 93 - store.set(fn, cacheNode); 94 - } else { 95 - cacheNode = fnNode; 96 - } 97 - 98 - // walk through arguments to find/create the cache node 99 - for (let i = 0; i < args.length; i++) { 100 - const arg = args[i]; 101 - 102 - if (typeof arg === 'function' || (typeof arg === 'object' && arg !== null)) { 103 - // objects go into a WeakMap 104 - let objectCache = cacheNode.o; 105 - if (objectCache === null) { 106 - cacheNode.o = objectCache = new WeakMap(); 107 - } 108 - 109 - const objectNode = objectCache.get(arg); 110 - if (objectNode === undefined) { 111 - cacheNode = createCacheNode(); 112 - objectCache.set(arg, cacheNode); 113 - } else { 114 - cacheNode = objectNode; 115 - } 116 - } else { 117 - // primitives go into a regular Map 118 - let primitiveCache = cacheNode.p; 119 - if (primitiveCache === null) { 120 - cacheNode.p = primitiveCache = new Map(); 121 - } 122 - 123 - const primitiveNode = primitiveCache.get(arg as Primitive); 124 - if (primitiveNode === undefined) { 125 - cacheNode = createCacheNode(); 126 - primitiveCache.set(arg as Primitive, cacheNode); 127 - } else { 128 - cacheNode = primitiveNode; 129 - } 130 - } 131 - } 132 - 133 - // return cached value or compute 134 - if (cacheNode.s === CacheStatus.TERMINATED) { 135 - return cacheNode.v; 136 - } 137 - if (cacheNode.s === CacheStatus.ERRORED) { 138 - throw cacheNode.v; 139 - } 140 - 141 - try { 142 - const result = fn.apply(this, args); 143 - 144 - const terminatedNode = cacheNode as unknown as TerminatedCacheNode<T>; 145 - terminatedNode.s = CacheStatus.TERMINATED; 146 - terminatedNode.v = result; 147 - 148 - return result; 149 - } catch (error) { 150 - const erroredNode = cacheNode as unknown as ErroredCacheNode<T>; 151 - erroredNode.s = CacheStatus.ERRORED; 152 - erroredNode.v = error; 153 - 154 - throw error; 155 - } 156 - }; 157 - }; 158 - 159 - cache.decorate = <This, A extends unknown[], T>( 160 - target: (this: This, ...args: A) => T, 161 - _context: ClassMethodDecoratorContext, 162 - ) => { 163 - return cache(target); 164 - }; 165 - 166 - // #endregion
+2
packages/danaus/src/web/primitives/menu/trigger.tsx
··· 16 16 const { children } = props; 17 17 const { menuId } = useMenuContext(); 18 18 19 + // oxlint-disable-next-line no-unsafe-type-assertion -- JSX element props access 19 20 const childProps = children.props as Record<string, unknown>; 20 21 21 22 return cloneElement(children, { 22 23 commandfor: menuId, 23 24 command: 'toggle-popover', 25 + // oxlint-disable-next-line no-unsafe-type-assertion 24 26 class: cx('anchor', childProps?.class as string | undefined), 25 27 }); 26 28 };
+5 -1
packages/danaus/src/web/scripts/webauthn-authenticate.ts
··· 30 30 return; 31 31 } 32 32 33 + // oxlint-disable-next-line no-unsafe-type-assertion -- trusted dataset JSON 33 34 this.#options = JSON.parse(optionsJson) as PublicKeyCredentialRequestOptionsJson; 34 35 35 36 const startButton = this.startButton; ··· 38 39 startButton.disabled = false; 39 40 startButton.addEventListener('click', (event) => { 40 41 event.preventDefault(); 41 - this.#handleAuthentication(); 42 + void this.#handleAuthentication(); 42 43 }); 43 44 } 44 45 } ··· 63 64 const publicKeyOptions: PublicKeyCredentialRequestOptions = { 64 65 ...options, 65 66 challenge: fromBase64Url(options.challenge), 67 + // oxlint-disable-next-line no-map-spread -- immutable credential transform 66 68 allowCredentials: options.allowCredentials?.map((credential) => ({ 67 69 ...credential, 68 70 id: fromBase64Url(credential.id), 69 71 })), 70 72 }; 71 73 74 + // oxlint-disable-next-line no-unsafe-type-assertion -- WebAuthn API returns PublicKeyCredential 72 75 const credential = (await navigator.credentials.get({ 73 76 publicKey: publicKeyOptions, 74 77 })) as PublicKeyCredential | null; ··· 81 84 return; 82 85 } 83 86 87 + // oxlint-disable-next-line no-unsafe-type-assertion -- WebAuthn get response type 84 88 const response = credential.response as AuthenticatorAssertionResponse; 85 89 86 90 const serialized = JSON.stringify({
+4 -1
packages/danaus/src/web/scripts/webauthn-passkey-login.ts
··· 34 34 startButton.disabled = false; 35 35 startButton.addEventListener('click', (event) => { 36 36 event.preventDefault(); 37 - this.#handlePasskeyLogin(challengeUrl); 37 + void this.#handlePasskeyLogin(challengeUrl); 38 38 }); 39 39 } 40 40 } ··· 59 59 throw new Error('Failed to fetch challenge'); 60 60 } 61 61 62 + // oxlint-disable-next-line no-unsafe-type-assertion -- trusted API JSON response 62 63 const options = (await challengeResponse.json()) as PublicKeyCredentialRequestOptionsJson; 63 64 64 65 status.textContent = ''; ··· 69 70 allowCredentials: undefined, 70 71 }; 71 72 73 + // oxlint-disable-next-line no-unsafe-type-assertion -- WebAuthn API returns PublicKeyCredential 72 74 const credential = (await navigator.credentials.get({ 73 75 publicKey: publicKeyOptions, 74 76 })) as PublicKeyCredential | null; ··· 81 83 return; 82 84 } 83 85 86 + // oxlint-disable-next-line no-unsafe-type-assertion -- WebAuthn get response type 84 87 const response = credential.response as AuthenticatorAssertionResponse; 85 88 86 89 const serialized = JSON.stringify({
+5 -1
packages/danaus/src/web/scripts/webauthn-register.ts
··· 30 30 return; 31 31 } 32 32 33 + // oxlint-disable-next-line no-unsafe-type-assertion -- trusted dataset JSON 33 34 this.#options = JSON.parse(optionsJson) as PublicKeyCredentialCreationOptionsJson; 34 35 35 36 const startButton = this.startButton; ··· 37 38 startButton.disabled = false; 38 39 startButton.addEventListener('click', (event) => { 39 40 event.preventDefault(); 40 - this.#handleRegistration(); 41 + void this.#handleRegistration(); 41 42 }); 42 43 } 43 44 } ··· 66 67 ...options.user, 67 68 id: fromBase64Url(options.user.id), 68 69 }, 70 + // oxlint-disable-next-line no-map-spread -- immutable credential transform 69 71 excludeCredentials: options.excludeCredentials?.map((credential) => ({ 70 72 ...credential, 71 73 id: fromBase64Url(credential.id), 72 74 })), 73 75 }; 74 76 77 + // oxlint-disable-next-line no-unsafe-type-assertion -- WebAuthn API returns PublicKeyCredential 75 78 const credential = (await navigator.credentials.create({ 76 79 publicKey: publicKeyOptions, 77 80 })) as PublicKeyCredential | null; ··· 84 87 return; 85 88 } 86 89 90 + // oxlint-disable-next-line no-unsafe-type-assertion -- WebAuthn create response type 87 91 const response = credential.response as AuthenticatorAttestationResponse; 88 92 89 93 const serialized = JSON.stringify({
+3
packages/danaus/tests/lexicon-validation.test.ts
··· 512 512 const lexiconCache = network.pds.ctx.lexiconCache; 513 513 514 514 // first lookup - should trigger DNS resolution and cache negative result 515 + // oxlint-disable-next-line no-unsafe-type-assertion -- test-only: forcing invalid NSID 515 516 const result1 = await lexiconCache.resolveAuthority('com.notfound.test' as never); 516 517 expect(result1).toBe(null); 517 518 518 519 // second lookup - should hit cache, not trigger another DNS lookup 520 + // oxlint-disable-next-line no-unsafe-type-assertion -- test-only: forcing invalid NSID 519 521 const result2 = await lexiconCache.resolveAuthority('com.notfound.other' as never); 520 522 expect(result2).toBe(null); 521 523 ··· 523 525 // if the authority was cached, repeated calls should be instant 524 526 const start = performance.now(); 525 527 for (let i = 0; i < 100; i++) { 528 + // oxlint-disable-next-line no-await-in-loop, no-unsafe-type-assertion -- intentional sequential cache check 526 529 await lexiconCache.resolveAuthority(`com.notfound.item${i}` as never); 527 530 } 528 531 const elapsed = performance.now() - start;
+7 -2
packages/danaus/tests/sequencer.test.ts
··· 46 46 47 47 const toStoredMessage = (evt: SeqEvt): SeqEvt['evt'] | (SeqEvt['evt'] & { blocks: null }) => { 48 48 if (evt.type === 'commit' || evt.type === 'sync') { 49 + // oxlint-disable-next-line no-unsafe-type-assertion -- discriminated union narrowing 49 50 return { ...evt.evt, blocks: null } as SeqEvt['evt'] & { blocks: null }; 50 51 } 51 52 ··· 54 55 55 56 const evtToDbRow = (evt: SeqEvt) => { 56 57 const did = evt.type === 'commit' ? evt.evt.repo : evt.evt.did; 58 + // oxlint-disable-next-line no-unsafe-type-assertion -- JSON round-trip preserves shape 57 59 const event = JSON.parse(JSON.stringify(toStoredMessage(evt))) as typeof evt.evt; 58 60 return { 59 61 seq: evt.seq, ··· 120 122 try { 121 123 const iterator = generator[Symbol.asyncIterator](); 122 124 while (events.length < limit) { 125 + // oxlint-disable-next-line no-await-in-loop -- sequential event consumption 123 126 const maybeEvent = await Promise.race([iterator.next(), breakOn]); 124 127 if (!maybeEvent) { 125 128 break; 126 129 } 127 130 128 - const event = maybeEvent as IteratorResult<T>; 131 + const event = maybeEvent; 129 132 if (event.done) { 130 133 break; 131 134 } ··· 176 179 const createPosts = async (count: number): Promise<void> => { 177 180 for (let i = 0; i < count; i++) { 178 181 if (i % 2 === 0) { 182 + // oxlint-disable-next-line no-await-in-loop -- sequential post creation 179 183 await randomPost(alice); 180 184 } else { 185 + // oxlint-disable-next-line no-await-in-loop 181 186 await randomPost(bob); 182 187 } 183 188 } ··· 309 314 await readFromGenerator(generator, caughtUp(), createPromise); 310 315 }; 311 316 312 - await expect(overloadBuffer()).rejects.toThrow('stream consumer too slow'); 317 + expect(overloadBuffer()).rejects.toThrow('stream consumer too slow'); 313 318 314 319 await createPromise; 315 320 controller.abort();
+8 -1
packages/danaus/tests/sync-repo.test.ts
··· 82 82 ); 83 83 84 84 const { record: found, cid } = await verifyRecord({ 85 + // oxlint-disable-next-line no-unsafe-type-assertion -- branded DID type 85 86 did: did as AtprotoDid, 86 87 collection: recordCollection, 87 88 rkey: recordRkey, ··· 89 90 }); 90 91 91 92 expect(cid).toBe(recordCid); 93 + // oxlint-disable-next-line no-unsafe-type-assertion -- narrowing verified record 92 94 expect((found as AppBskyFeedPost.Main).text).toBe(record.text); 93 95 }); 94 96 ··· 103 105 ); 104 106 105 107 const { record: found, cid } = await verifyRecord({ 108 + // oxlint-disable-next-line no-unsafe-type-assertion -- branded DID type 106 109 did: did as AtprotoDid, 107 110 collection: recordCollection, 108 111 rkey: recordRkey, ··· 110 113 }); 111 114 112 115 expect(cid).toBe(recordCid); 116 + // oxlint-disable-next-line no-unsafe-type-assertion -- narrowing verified record 113 117 expect((found as AppBskyFeedPost.Main).text).toBe(record.text); 114 118 }); 115 119 ··· 193 197 ); 194 198 195 199 const { record: found, cid } = await verifyRecord({ 200 + // oxlint-disable-next-line no-unsafe-type-assertion -- branded DID type 196 201 did: did as AtprotoDid, 197 202 collection: recordCollection, 198 203 rkey: recordRkey, ··· 200 205 }); 201 206 202 207 expect(cid).toBe(putRes.cid); 208 + // oxlint-disable-next-line no-unsafe-type-assertion -- narrowing verified record 203 209 expect((found as AppBskyFeedPost.Main).text).toBe(updated.text); 204 210 205 211 recordCid = putRes.cid; ··· 229 235 }), 230 236 ); 231 237 232 - await expect(attempt).rejects.toThrow('RecordNotFound'); 238 + expect(attempt).rejects.toThrow('RecordNotFound'); 233 239 }); 234 240 235 241 it('repo.uploadBlob stores data for sync.getBlob', async () => { ··· 243 249 as: 'bytes', 244 250 params: { 245 251 did, 252 + // oxlint-disable-next-line no-unsafe-type-assertion -- narrowing upload response 246 253 cid: (blob as Blob).ref.$link, 247 254 }, 248 255 }),
+5
packages/dev-env/src/bin.ts
··· 19 19 throw new ReferenceError('AsyncDisposableStack already disposed'); 20 20 } 21 21 if (value != null) { 22 + // oxlint-disable-next-line no-unsafe-type-assertion -- null-checked above 22 23 if (Symbol.asyncDispose in (value as object)) { 23 24 this.#stack.push(async () => { 25 + // oxlint-disable-next-line no-unsafe-type-assertion -- guarded by Symbol.asyncDispose in check 24 26 await (value as AsyncDisposable)[Symbol.asyncDispose](); 25 27 }); 28 + // oxlint-disable-next-line no-unsafe-type-assertion -- null-checked above 26 29 } else if (Symbol.dispose in (value as object)) { 30 + // oxlint-disable-next-line no-unsafe-type-assertion -- guarded by Symbol.dispose in check 27 31 this.#stack.push(async () => (value as Disposable)[Symbol.dispose]()); 28 32 } 29 33 } ··· 69 73 while (this.#stack.length > 0) { 70 74 const dispose = this.#stack.pop()!; 71 75 try { 76 + // oxlint-disable-next-line no-await-in-loop -- sequential disposal 72 77 await dispose(); 73 78 } catch (e) { 74 79 error ??= e;
+68 -3
pnpm-lock.yaml
··· 14 14 .: 15 15 devDependencies: 16 16 '@typescript/native-preview': 17 - specifier: latest 17 + specifier: 7.0.0-dev.20260416.1 18 18 version: 7.0.0-dev.20260416.1 19 19 oxfmt: 20 20 specifier: ^0.45.0 21 21 version: 0.45.0 22 22 oxlint: 23 23 specifier: ^1.60.0 24 - version: 1.60.0 24 + version: 1.60.0(oxlint-tsgolint@0.21.1) 25 + oxlint-tsgolint: 26 + specifier: ^0.21.1 27 + version: 0.21.1 25 28 26 29 packages/danaus: 27 30 dependencies: ··· 1173 1176 cpu: [x64] 1174 1177 os: [win32] 1175 1178 1179 + '@oxlint-tsgolint/darwin-arm64@0.21.1': 1180 + resolution: {integrity: sha512-7TLjyWe4wG9saJc992VWmaHq2hwKfOEEVTjheReXJXaDhavMZI4X9a6nKhbEng4IVkYtzjD2jw16vw2WFXLYLw==} 1181 + cpu: [arm64] 1182 + os: [darwin] 1183 + 1184 + '@oxlint-tsgolint/darwin-x64@0.21.1': 1185 + resolution: {integrity: sha512-7wf9Wf75nTzA7zpL9myhFe2RKvfuqGUOADNvUooCjEWvh7hmPz3lSEqTMh5Z/VQhzsG04mM9ACyghxhRzq7zFw==} 1186 + cpu: [x64] 1187 + os: [darwin] 1188 + 1189 + '@oxlint-tsgolint/linux-arm64@0.21.1': 1190 + resolution: {integrity: sha512-IPuQN/Vd0Rjklg/cCGBbQyUuRBp2f6LQXpZYwk5ivOR6V/+CgiYsv8pn/PVY7gjeyoNvPQrXB7xMjHUO2YZbdw==} 1191 + cpu: [arm64] 1192 + os: [linux] 1193 + 1194 + '@oxlint-tsgolint/linux-x64@0.21.1': 1195 + resolution: {integrity: sha512-d1niGuTbh2qiv7dR7tqkbOcM5cIR63of0lMBFdEQavL1KrJV8zuRdwdi68K7MNGdgoR+J5A9ajpGGvsHwp1bPg==} 1196 + cpu: [x64] 1197 + os: [linux] 1198 + 1199 + '@oxlint-tsgolint/win32-arm64@0.21.1': 1200 + resolution: {integrity: sha512-ICu9y2JLnFPvFqstnWPPNqBM8LK8BWw2OTeaR0UgEMm4hOSbrZAKv1/hwZYyiLqnCNjBL87AGSQIgTHCYlsipw==} 1201 + cpu: [arm64] 1202 + os: [win32] 1203 + 1204 + '@oxlint-tsgolint/win32-x64@0.21.1': 1205 + resolution: {integrity: sha512-cTEFCFjCj6iXfrSHcvajSPNqhEA4TxSzU3gFxbdGSAUTNXGToU99IbdhWAPSbhcucoym0XE4Zl7E41NiSkNTug==} 1206 + cpu: [x64] 1207 + os: [win32] 1208 + 1176 1209 '@oxlint/binding-android-arm-eabi@1.60.0': 1177 1210 resolution: {integrity: sha512-YdeJKaZckDQL1qa62a1aKq/goyq48aX3yOxaaWqWb4sau4Ee4IiLbamftNLU3zbePky6QsDj6thnSSzHRBjDfA==} 1178 1211 engines: {node: ^20.19.0 || >=22.12.0} ··· 2785 2818 oxfmt@0.45.0: 2786 2819 resolution: {integrity: sha512-0o/COoN9fY50bjVeM7PQsNgbhndKurBIeTIcspW033OumksjJJmIVDKjAk5HMwU/GHTxSOdGDdhJ6BRzGPmsHg==} 2787 2820 engines: {node: ^20.19.0 || >=22.12.0} 2821 + hasBin: true 2822 + 2823 + oxlint-tsgolint@0.21.1: 2824 + resolution: {integrity: sha512-O2hxiT14C2HJkwzBU6CQBFPoagSd/IcV+Tt3e3UUaXFwbW4BO5DSDPSSboc3UM5MIDY+MLyepvtQwBQafNxWdw==} 2788 2825 hasBin: true 2789 2826 2790 2827 oxlint@1.60.0: ··· 4420 4457 '@oxfmt/binding-win32-x64-msvc@0.45.0': 4421 4458 optional: true 4422 4459 4460 + '@oxlint-tsgolint/darwin-arm64@0.21.1': 4461 + optional: true 4462 + 4463 + '@oxlint-tsgolint/darwin-x64@0.21.1': 4464 + optional: true 4465 + 4466 + '@oxlint-tsgolint/linux-arm64@0.21.1': 4467 + optional: true 4468 + 4469 + '@oxlint-tsgolint/linux-x64@0.21.1': 4470 + optional: true 4471 + 4472 + '@oxlint-tsgolint/win32-arm64@0.21.1': 4473 + optional: true 4474 + 4475 + '@oxlint-tsgolint/win32-x64@0.21.1': 4476 + optional: true 4477 + 4423 4478 '@oxlint/binding-android-arm-eabi@1.60.0': 4424 4479 optional: true 4425 4480 ··· 5848 5903 '@oxfmt/binding-win32-ia32-msvc': 0.45.0 5849 5904 '@oxfmt/binding-win32-x64-msvc': 0.45.0 5850 5905 5851 - oxlint@1.60.0: 5906 + oxlint-tsgolint@0.21.1: 5907 + optionalDependencies: 5908 + '@oxlint-tsgolint/darwin-arm64': 0.21.1 5909 + '@oxlint-tsgolint/darwin-x64': 0.21.1 5910 + '@oxlint-tsgolint/linux-arm64': 0.21.1 5911 + '@oxlint-tsgolint/linux-x64': 0.21.1 5912 + '@oxlint-tsgolint/win32-arm64': 0.21.1 5913 + '@oxlint-tsgolint/win32-x64': 0.21.1 5914 + 5915 + oxlint@1.60.0(oxlint-tsgolint@0.21.1): 5852 5916 optionalDependencies: 5853 5917 '@oxlint/binding-android-arm-eabi': 1.60.0 5854 5918 '@oxlint/binding-android-arm64': 1.60.0 ··· 5869 5933 '@oxlint/binding-win32-arm64-msvc': 1.60.0 5870 5934 '@oxlint/binding-win32-ia32-msvc': 1.60.0 5871 5935 '@oxlint/binding-win32-x64-msvc': 1.60.0 5936 + oxlint-tsgolint: 0.21.1 5872 5937 5873 5938 p-finally@1.0.0: {} 5874 5939