+4
.oxfmtrc.json
+4
.oxfmtrc.json
+4
-4
PLAN.md
+4
-4
PLAN.md
···
1
1
# rough notes on how I think this should work
2
2
3
3
- we start of with no accounts
4
-
- ``/teal auth`` sends user a link to log in with atproto account
4
+
- `/teal auth` sends user a link to log in with atproto account
5
5
- after auth success, we take did and send http request to tap instance to start backfilling for repo
6
6
- user can now use bot commands
7
7
8
8
## planned commands
9
9
10
-
- ``teal auth`` sends user a link to log in with atproto account
11
-
- ``top <artist>``: top 10 listeners for artist (total amount of plays across all songs / albums)
12
-
- ``recent``: most recent play
10
+
- `teal auth` sends user a link to log in with atproto account
11
+
- `top <artist>`: top 10 listeners for artist (total amount of plays across all songs / albums)
12
+
- `recent`: most recent play
13
13
14
14
## web interface for account management...maybe..?
15
15
+6
-4
apps/bot/commands/auth.ts
+6
-4
apps/bot/commands/auth.ts
···
2
2
import { logger } from "@tealfmbot/common/logger.ts";
3
3
4
4
export default {
5
-
data: new SlashCommandBuilder().setName("auth").setDescription(
6
-
"Authenticate your account with the teal.fm bot to start tracking your listens",
7
-
),
5
+
data: new SlashCommandBuilder()
6
+
.setName("auth")
7
+
.setDescription(
8
+
"Authenticate your account with the teal.fm bot to start tracking your listens",
9
+
),
8
10
async execute(interaction: CommandInteraction) {
9
11
await interaction.reply("placeholder");
10
-
logger.info("auth command sent")
12
+
logger.info("auth command sent");
11
13
},
12
14
};
+2
-4
apps/bot/commands/ping.ts
+2
-4
apps/bot/commands/ping.ts
···
2
2
import { logger } from "@tealfmbot/common/logger.ts";
3
3
4
4
export default {
5
-
data: new SlashCommandBuilder().setName("ping").setDescription(
6
-
"replies with pong",
7
-
),
5
+
data: new SlashCommandBuilder().setName("ping").setDescription("replies with pong"),
8
6
async execute(interaction: CommandInteraction) {
9
7
await interaction.reply("pong lol");
10
-
logger.info("ping command sent")
8
+
logger.info("ping command sent");
11
9
},
12
10
};
+4
-4
apps/bot/commands/top.ts
+4
-4
apps/bot/commands/top.ts
···
2
2
import { logger } from "@tealfmbot/common/logger.ts";
3
3
4
4
export default {
5
-
data: new SlashCommandBuilder().setName("top").setDescription(
6
-
"Find the top listeners for the specified artist(s)",
7
-
),
5
+
data: new SlashCommandBuilder()
6
+
.setName("top")
7
+
.setDescription("Find the top listeners for the specified artist(s)"),
8
8
async execute(interaction: CommandInteraction) {
9
9
await interaction.reply("placeholder");
10
-
logger.info("top command sent")
10
+
logger.info("top command sent");
11
11
},
12
12
};
+6
-10
apps/bot/deploy-commands.ts
+6
-10
apps/bot/deploy-commands.ts
···
11
11
const commandPaths = fs.globSync("commands/**/*.ts");
12
12
13
13
for await (const cmdPath of commandPaths) {
14
-
const absoluteCommandPath = path.join(import.meta.dirname, cmdPath)
15
-
const command = await import(absoluteCommandPath)
14
+
const absoluteCommandPath = path.join(import.meta.dirname, cmdPath);
15
+
const command = await import(absoluteCommandPath);
16
16
if ("data" in command.default && "execute" in command.default) {
17
17
commands.push(command.default.data);
18
18
} else {
···
26
26
27
27
(async () => {
28
28
try {
29
-
console.log(
30
-
`Started refreshing ${commands.length} application (/) commands.`,
31
-
);
29
+
console.log(`Started refreshing ${commands.length} application (/) commands.`);
32
30
33
-
const data = await rest.put(
31
+
const data = (await rest.put(
34
32
Routes.applicationGuildCommands(DISCORD_APPLICATION_ID, DISCORD_GUILD_ID),
35
33
{ body: commands },
36
-
) as unknown[];
34
+
)) as unknown[];
37
35
38
-
console.log(
39
-
`Successfully reloaded ${data.length} application (/) commands.`,
40
-
);
36
+
console.log(`Successfully reloaded ${data.length} application (/) commands.`);
41
37
} catch (error) {
42
38
console.error(error);
43
39
}
+1
-1
apps/bot/discord.d.ts
+1
-1
apps/bot/discord.d.ts
+1
-7
apps/bot/main.ts
+1
-7
apps/bot/main.ts
···
1
-
import {
2
-
Client,
3
-
Collection,
4
-
Events,
5
-
GatewayIntentBits,
6
-
MessageFlags,
7
-
} from "discord.js";
1
+
import { Client, Collection, Events, GatewayIntentBits, MessageFlags } from "discord.js";
8
2
import fs from "node:fs";
9
3
import path from "node:path";
10
4
import { DISCORD_BOT_TOKEN } from "@tealfmbot/common/constants.ts";
+1
-1
apps/bot/package.json
+1
-1
apps/bot/package.json
+2
-4
apps/bot/tsconfig.json
+2
-4
apps/bot/tsconfig.json
+1
-2
apps/tapper/index.ts
+1
-2
apps/tapper/index.ts
···
17
17
// track_name: evt?.record?.trackName,
18
18
// user_id: 4
19
19
// }).execute()
20
-
console.log(evt.record)
20
+
console.log(evt.record);
21
21
} else {
22
22
console.log(`deleted: ${uri}`);
23
23
}
24
-
25
24
26
25
if (process.env.NODE_ENV === "development") {
27
26
// we don't want to ack in development
+1
-1
apps/tapper/package.json
+1
-1
apps/tapper/package.json
+1
-3
apps/tapper/tsconfig.json
+1
-3
apps/tapper/tsconfig.json
+6
lefthook.yml
+6
lefthook.yml
+14
-11
package.json
+14
-11
package.json
···
3
3
"version": "0.0.1",
4
4
"private": true,
5
5
"description": "A discord bot for teal.fm",
6
+
"keywords": [
7
+
"atprotocol",
8
+
"music",
9
+
"teal.fm"
10
+
],
11
+
"license": "MIT",
12
+
"author": "Dane Miller <me@dane.computer>",
13
+
"repository": {},
6
14
"scripts": {
7
15
"bot": "pnpm --filter bot dev",
8
16
"tap": "pnpm --filter tapper dev",
9
17
"dev": "pnpm --filter './apps/**' dev",
10
18
"typecheck": "pnpm --filter './apps/**' typecheck",
11
-
"lint": "oxlint"
19
+
"lint": "oxlint",
20
+
"format": "oxfmt"
12
21
},
13
-
"keywords": [
14
-
"teal.fm",
15
-
"atprotocol",
16
-
"music"
17
-
],
18
-
"author": "Dane Miller <me@dane.computer>",
19
-
"license": "MIT",
20
-
"packageManager": "pnpm@10.15.0",
21
-
"repository": {},
22
22
"devDependencies": {
23
+
"lefthook": "^2.0.13",
24
+
"oxfmt": "^0.20.0",
23
25
"oxlint": "^1.35.0",
24
26
"typescript": "^5.9.3"
25
-
}
27
+
},
28
+
"packageManager": "pnpm@10.15.0"
26
29
}
+3
-3
packages/common/constants.ts
+3
-3
packages/common/constants.ts
···
1
-
import { loadEnvFile } from "node:process"
2
-
import path from "node:path"
1
+
import { loadEnvFile } from "node:process";
2
+
import path from "node:path";
3
3
4
-
loadEnvFile(path.join(import.meta.dirname, "../../.env"))
4
+
loadEnvFile(path.join(import.meta.dirname, "../../.env"));
5
5
6
6
export const DISCORD_BOT_TOKEN = process.env.DISCORD_BOT_TOKEN as string;
7
7
export const DISCORD_APPLICATION_ID = process.env.DISCORD_APPLICATION_ID as string;
+5
-5
packages/common/logger.ts
+5
-5
packages/common/logger.ts
+6
-6
packages/common/package.json
+6
-6
packages/common/package.json
···
1
1
{
2
2
"name": "@tealfmbot/common",
3
3
"version": "0.0.0",
4
-
"type": "module",
5
4
"private": true,
5
+
"type": "module",
6
6
"exports": {
7
7
"./*": "./*"
8
8
},
9
9
"scripts": {
10
10
"typecheck": "tsc --noEmit"
11
11
},
12
+
"dependencies": {
13
+
"pino": "^10.1.0"
14
+
},
12
15
"devDependencies": {
13
16
"@tealfmbot/tsconfig": "workspace:*",
14
17
"@types/node": "^22.15.3",
15
-
"typescript": "5.9.2",
16
-
"pino-pretty": "^13.1.3"
17
-
},
18
-
"dependencies": {
19
-
"pino": "^10.1.0"
18
+
"pino-pretty": "^13.1.3",
19
+
"typescript": "5.9.2"
20
20
}
21
21
}
+1
-1
packages/common/tsconfig.json
+1
-1
packages/common/tsconfig.json
+9
-9
packages/database/db.ts
+9
-9
packages/database/db.ts
···
1
-
import type { DB } from "kysely-codegen"
2
-
import { Pool } from "pg"
3
-
import { Kysely, PostgresDialect } from "kysely"
4
-
import { DATABASE_URL } from "@tealfmbot/common/constants.ts"
1
+
import type { DB } from "kysely-codegen";
2
+
import { Pool } from "pg";
3
+
import { Kysely, PostgresDialect } from "kysely";
4
+
import { DATABASE_URL } from "@tealfmbot/common/constants.ts";
5
5
6
6
const dialect = new PostgresDialect({
7
7
pool: new Pool({
8
-
connectionString: DATABASE_URL
9
-
})
10
-
})
8
+
connectionString: DATABASE_URL,
9
+
}),
10
+
});
11
11
12
12
export const db = new Kysely<DB>({
13
-
dialect
14
-
})
13
+
dialect,
14
+
});
+21
-16
packages/database/migrations/schema.ts
+21
-16
packages/database/migrations/schema.ts
···
1
-
import { Kysely, sql } from "kysely"
1
+
import { Kysely, sql } from "kysely";
2
2
3
3
export async function up(db: Kysely<any>): Promise<void> {
4
4
await db.schema
5
-
.createTable("users").ifNotExists()
5
+
.createTable("users")
6
+
.ifNotExists()
6
7
.addColumn("id", "serial", (col) => col.primaryKey())
7
8
.addColumn("did", "varchar", (col) => col.notNull().unique())
8
-
.addColumn('created_at', 'timestamp', (col) =>
9
-
col.defaultTo(sql`now()`).notNull(),
10
-
)
11
-
.execute()
9
+
.addColumn("created_at", "timestamp", (col) => col.defaultTo(sql`now()`).notNull())
10
+
.execute();
12
11
13
12
await db.schema
14
-
.createTable("plays").ifNotExists()
13
+
.createTable("plays")
14
+
.ifNotExists()
15
15
.addColumn("id", "serial", (col) => col.primaryKey())
16
16
.addColumn("played_time", "timestamptz", (col) => col.notNull())
17
17
.addColumn("release_name", "varchar", (col) => col.notNull())
18
-
.addColumn("track_name", 'varchar', (col) => col.notNull())
18
+
.addColumn("track_name", "varchar", (col) => col.notNull())
19
19
.addColumn("indexed_at", "timestamp", (col) => col.defaultTo(sql`now()`).notNull())
20
-
.addColumn("user_id", "integer", (col) => col.references("users.id").onDelete("cascade").notNull())
21
-
.execute()
20
+
.addColumn("user_id", "integer", (col) =>
21
+
col.references("users.id").onDelete("cascade").notNull(),
22
+
)
23
+
.execute();
22
24
23
25
await db.schema
24
-
.createTable("artists").ifNotExists()
26
+
.createTable("artists")
27
+
.ifNotExists()
25
28
.addColumn("artist_name", "varchar", (col) => col.notNull())
26
-
.addColumn("play_id", "integer", (col) => col.references("plays.id").onDelete("cascade").notNull())
27
-
.execute()
29
+
.addColumn("play_id", "integer", (col) =>
30
+
col.references("plays.id").onDelete("cascade").notNull(),
31
+
)
32
+
.execute();
28
33
}
29
34
30
35
export async function down(db: Kysely<any>): Promise<void> {
31
-
await db.schema.dropTable("users").execute()
32
-
await db.schema.dropTable("plays").execute()
33
-
await db.schema.dropTable("artists").execute()
36
+
await db.schema.dropTable("users").execute();
37
+
await db.schema.dropTable("plays").execute();
38
+
await db.schema.dropTable("artists").execute();
34
39
}
+23
-24
packages/database/migrator.ts
+23
-24
packages/database/migrator.ts
···
1
-
import path from "node:path"
2
-
import { Pool } from "pg"
3
-
import fs from "node:fs/promises"
4
-
import type { DB } from "kysely-codegen"
5
-
import { Kysely, Migrator, PostgresDialect, FileMigrationProvider } from "kysely"
6
-
import { DATABASE_URL } from "@tealfmbot/common/constants.ts"
1
+
import path from "node:path";
2
+
import { Pool } from "pg";
3
+
import fs from "node:fs/promises";
4
+
import type { DB } from "kysely-codegen";
5
+
import { Kysely, Migrator, PostgresDialect, FileMigrationProvider } from "kysely";
6
+
import { DATABASE_URL } from "@tealfmbot/common/constants.ts";
7
7
8
8
async function migrateToLatest() {
9
9
const db = new Kysely<DB>({
10
10
dialect: new PostgresDialect({
11
11
pool: new Pool({
12
-
connectionString: DATABASE_URL
13
-
})
14
-
})
15
-
})
12
+
connectionString: DATABASE_URL,
13
+
}),
14
+
}),
15
+
});
16
16
17
17
const migrator = new Migrator({
18
18
db,
19
19
provider: new FileMigrationProvider({
20
20
fs,
21
21
path,
22
-
migrationFolder: path.join(import.meta.dirname, "../migrations")
23
-
})
24
-
})
25
-
22
+
migrationFolder: path.join(import.meta.dirname, "../migrations"),
23
+
}),
24
+
});
26
25
27
-
const { error, results } = await migrator.migrateToLatest()
26
+
const { error, results } = await migrator.migrateToLatest();
28
27
29
-
results?.forEach(result => {
28
+
results?.forEach((result) => {
30
29
if (result.status === "Success") {
31
-
console.log(`migration ${result.migrationName} was executed successfully`)
30
+
console.log(`migration ${result.migrationName} was executed successfully`);
32
31
} else if (result.status === "Error") {
33
-
console.error(`failed to execute migration: "${result.migrationName}" `)
32
+
console.error(`failed to execute migration: "${result.migrationName}" `);
34
33
}
35
-
})
34
+
});
36
35
37
36
if (error) {
38
-
console.error("failed to migrate")
39
-
console.error(error)
40
-
process.exit(1)
37
+
console.error("failed to migrate");
38
+
console.error(error);
39
+
process.exit(1);
41
40
}
42
41
43
-
await db.destroy()
42
+
await db.destroy();
44
43
}
45
44
46
-
migrateToLatest().catch(err => console.error(err))
45
+
migrateToLatest().catch((err) => console.error(err));
+7
-7
packages/database/package.json
+7
-7
packages/database/package.json
···
1
1
{
2
2
"name": "@tealfmbot/database",
3
3
"version": "0.0.0",
4
-
"type": "module",
5
4
"private": true,
5
+
"type": "module",
6
6
"exports": {
7
7
"./db.ts": "./db.ts"
8
8
},
···
12
12
"seed": "tsx seed.ts",
13
13
"typecheck": "tsc --noEmit"
14
14
},
15
+
"dependencies": {
16
+
"@tealfmbot/common": "workspace:*",
17
+
"@tealfmbot/tsconfig": "workspace:*",
18
+
"kysely": "^0.28.9",
19
+
"pg": "^8.16.3"
20
+
},
15
21
"devDependencies": {
16
22
"@types/node": "^22.15.3",
17
23
"@types/pg": "^8.16.0",
18
24
"kysely-codegen": "^0.19.0",
19
25
"tsx": "^4.21.0",
20
26
"typescript": "5.9.2"
21
-
},
22
-
"dependencies": {
23
-
"@tealfmbot/common": "workspace:*",
24
-
"@tealfmbot/tsconfig": "workspace:*",
25
-
"kysely": "^0.28.9",
26
-
"pg": "^8.16.3"
27
27
}
28
28
}
+1
-2
packages/database/seed.ts
+1
-2
packages/database/seed.ts
···
6
6
// }).returning(['did', 'created_at'])
7
7
// .executeTakeFirst()
8
8
// console.log({ result })
9
-
10
9
// const users = await db.selectFrom("users").select(["id", "did"]).execute()
11
10
// console.log({ users })
12
11
// await db.deleteFrom("users").where("did", "=", "did:plc:qttsv4e7pu2jl3ilanfgc3zn").execute()
···
14
13
// console.log({ plays })
15
14
}
16
15
17
-
main().catch(error => console.error(error))
16
+
main().catch((error) => console.error(error));
+1
-3
packages/database/tsconfig.json
+1
-3
packages/database/tsconfig.json
+1
-1
packages/database/types.ts
+1
-1
packages/database/types.ts
+1
-1
packages/tsconfig/tsconfig.base.json
+1
-1
packages/tsconfig/tsconfig.base.json
+2
-6
packages/tsconfig/tsconfig.node.json
+2
-6
packages/tsconfig/tsconfig.node.json
+191
pnpm-lock.yaml
+191
pnpm-lock.yaml
···
8
8
9
9
.:
10
10
devDependencies:
11
+
lefthook:
12
+
specifier: ^2.0.13
13
+
version: 2.0.13
14
+
oxfmt:
15
+
specifier: ^0.20.0
16
+
version: 0.20.0
11
17
oxlint:
12
18
specifier: ^1.35.0
13
19
version: 1.35.0
···
332
338
cpu: [x64]
333
339
os: [win32]
334
340
341
+
'@oxfmt/darwin-arm64@0.20.0':
342
+
resolution: {integrity: sha512-bjR5dqvrd9gxKYfYR0ljUu3/T3+TuDVWcwA7d+tsfmx9lqidlw3zhgBTblnjF1mrd1zkPMoc5zzq86GeSEt1cA==}
343
+
cpu: [arm64]
344
+
os: [darwin]
345
+
346
+
'@oxfmt/darwin-x64@0.20.0':
347
+
resolution: {integrity: sha512-esUDes8FlJX3IY4TVjFLgZrnZlIIyPDlhkCaHgGR3+z2eHFZOvQu68kTSpZLCEJmGXdSpU5rlveycQ6n8tk9ew==}
348
+
cpu: [x64]
349
+
os: [darwin]
350
+
351
+
'@oxfmt/linux-arm64-gnu@0.20.0':
352
+
resolution: {integrity: sha512-irE0RO9B0R6ziQE6kUVZtZ6IuTdRyuumn1cPWhDfpa0XUa5sE0ly8pjVsvJbj/J9qerVtidU05txeXBB5CirQg==}
353
+
cpu: [arm64]
354
+
os: [linux]
355
+
356
+
'@oxfmt/linux-arm64-musl@0.20.0':
357
+
resolution: {integrity: sha512-eXPBLwYJm26DCmwMwhelEwQMRwuGNaYhYZOhd+CYYsmVoF+h6L6dtjwj0Ovuu0Gqh18EL8vfsaoUvb+jr3vEBg==}
358
+
cpu: [arm64]
359
+
os: [linux]
360
+
361
+
'@oxfmt/linux-x64-gnu@0.20.0':
362
+
resolution: {integrity: sha512-dTPW38Hjgb7LoD2mNgyQGBaJ1hu5YgPrxImhl5Eb04eiws+ETCM0wrb2TWGduA+Nv3rHKn3vZEkMTEjklZXgRw==}
363
+
cpu: [x64]
364
+
os: [linux]
365
+
366
+
'@oxfmt/linux-x64-musl@0.20.0':
367
+
resolution: {integrity: sha512-b4duw9JGDK/kZoqrPNU9tBOOZQdUW8KJPZ7gW7z54X1eGSqCJ1PT0XLNmZ7SOA1BzQwQ0a3qmQWfFVOsH3a5bw==}
368
+
cpu: [x64]
369
+
os: [linux]
370
+
371
+
'@oxfmt/win32-arm64@0.20.0':
372
+
resolution: {integrity: sha512-XAzvBhw4K+Fe16dBaFgYAdob9WaM8RYEXl0ibbm5NlNaQEq+5bH9xwc0oaYlHFnLfcgXWmn9ceTAYqNlONQRNA==}
373
+
cpu: [arm64]
374
+
os: [win32]
375
+
376
+
'@oxfmt/win32-x64@0.20.0':
377
+
resolution: {integrity: sha512-fkJqHbJaoOMRmrjHSljyb4/7BgXO3xPLBsJSFGtm3mpfW0HHFbAKvd4/6njhqJz9KY+b3RWP1WssjFshcqQQ4w==}
378
+
cpu: [x64]
379
+
os: [win32]
380
+
335
381
'@oxlint/darwin-arm64@1.35.0':
336
382
resolution: {integrity: sha512-ieiYVHkNZPo77Hgrxav595wGS4rRNKuDNrljf+4xhwpJsddrxMpM64IQUf2IvR3MhK4FxdGzhhB6OVmGVHY5/w==}
337
383
cpu: [arm64]
···
670
716
resolution: {integrity: sha512-3BeXMoiOhpOwu62CiVpO6lxfq4eS6KMYfQdMsN/2kUCRNuF2YiEr7u0HLHaQU+O4Xu8YXE3bHVkwaQ85i72EuA==}
671
717
engines: {node: '>=20.0.0'}
672
718
719
+
lefthook-darwin-arm64@2.0.13:
720
+
resolution: {integrity: sha512-KbQqpNSNTugjtPzt97CNcy/XZy5asJ0+uSLoHc4ML8UCJdsXKYJGozJHNwAd0Xfci/rQlj82A7rPOuTdh0jY0Q==}
721
+
cpu: [arm64]
722
+
os: [darwin]
723
+
724
+
lefthook-darwin-x64@2.0.13:
725
+
resolution: {integrity: sha512-s/vI6sEE8/+rE6CONZzs59LxyuSc/KdU+/3adkNx+Q13R1+p/AvQNeszg3LAHzXmF3NqlxYf8jbj/z5vBzEpRw==}
726
+
cpu: [x64]
727
+
os: [darwin]
728
+
729
+
lefthook-freebsd-arm64@2.0.13:
730
+
resolution: {integrity: sha512-iQeJTU7Zl8EJlCMQxNZQpJFAQ9xl40pydUIv5SYnbJ4nqIr9ONuvrioNv6N2LtKP5aBl1nIWQQ9vMjgVyb3k+A==}
731
+
cpu: [arm64]
732
+
os: [freebsd]
733
+
734
+
lefthook-freebsd-x64@2.0.13:
735
+
resolution: {integrity: sha512-99cAXKRIzpq/u3obUXbOQJCHP+0ZkJbN3TF+1ZQZlRo3Y6+mPSCg9fh/oi6dgbtu4gTI5Ifz3o5p2KZzAIF9ZQ==}
736
+
cpu: [x64]
737
+
os: [freebsd]
738
+
739
+
lefthook-linux-arm64@2.0.13:
740
+
resolution: {integrity: sha512-RWarenY3kLy/DT4/8dY2bwDlYwlELRq9MIFq+FiMYmoBHES3ckWcLX2JMMlM49Y672paQc7MbneSrNUn/FQWhg==}
741
+
cpu: [arm64]
742
+
os: [linux]
743
+
744
+
lefthook-linux-x64@2.0.13:
745
+
resolution: {integrity: sha512-QZRcxXGf8Uj/75ITBqoBh0zWhJE7+uFoRxEHwBq0Qjv55Q4KcFm7FBN/IFQCSd14reY5pmY3kDaWVVy60cAGJA==}
746
+
cpu: [x64]
747
+
os: [linux]
748
+
749
+
lefthook-openbsd-arm64@2.0.13:
750
+
resolution: {integrity: sha512-LAuOWwnNmOlRE0RxKMOhIz5Kr9tXi0rCjzXtDARW9lvfAV6Br2wP+47q0rqQ8m/nVwBYoxfJ/RDunLbb86O1nA==}
751
+
cpu: [arm64]
752
+
os: [openbsd]
753
+
754
+
lefthook-openbsd-x64@2.0.13:
755
+
resolution: {integrity: sha512-n9TIN3QLncyxOHomiKKwzDFHKOCm5H28CVNAZFouKqDwEaUGCs5TJI88V85j4/CgmLVUU8uUn4ClVCxIWYG59w==}
756
+
cpu: [x64]
757
+
os: [openbsd]
758
+
759
+
lefthook-windows-arm64@2.0.13:
760
+
resolution: {integrity: sha512-sdSC4F9Di7y0t43Of9MOA5g/0CmvkM4juQ3sKfUhRcoygetLJn4PR2/pvuDOIaGf4mNMXBP5IrcKaeDON9HrcA==}
761
+
cpu: [arm64]
762
+
os: [win32]
763
+
764
+
lefthook-windows-x64@2.0.13:
765
+
resolution: {integrity: sha512-ccl1v7Fl10qYoghEtjXN+JC1x/y/zLM/NSHf3NFGeKEGBNd1P5d/j6w8zVmhfzi+ekS8whXrcNbRAkLdAqUrSw==}
766
+
cpu: [x64]
767
+
os: [win32]
768
+
769
+
lefthook@2.0.13:
770
+
resolution: {integrity: sha512-D39rCVl7/GpqakvhQvqz07SBpzUWTvWjXKnBZyIy8O6D+Lf9xD6tnbHtG5nWXd9iPvv1AKGQwL9R/e5rNtV6SQ==}
771
+
hasBin: true
772
+
673
773
lines-and-columns@1.2.4:
674
774
resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==}
675
775
···
705
805
706
806
once@1.4.0:
707
807
resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==}
808
+
809
+
oxfmt@0.20.0:
810
+
resolution: {integrity: sha512-+7f8eV8iaK3tENN/FUVxZM1g78HjPehybN8/+/dvEA1O893Dcvk6O7/Q1wTQOHMD7wvdwWdujKl+Uo8QMiKDrQ==}
811
+
engines: {node: ^20.19.0 || >=22.12.0}
812
+
hasBin: true
708
813
709
814
oxlint@1.35.0:
710
815
resolution: {integrity: sha512-QDX1aUgaiqznkGfTM2qHwva2wtKqhVoqPSVXrnPz+yLUhlNadikD3QRuRtppHl7WGuy3wG6nKAuR8lash3aWSg==}
···
913
1018
thread-stream@3.1.0:
914
1019
resolution: {integrity: sha512-OqyPZ9u96VohAyMfJykzmivOrY2wfMSf3C5TtFJVgN+Hm6aj+voFhlK+kZEIv2FBh1X6Xp3DlnCOfEQ3B2J86A==}
915
1020
1021
+
tinypool@2.0.0:
1022
+
resolution: {integrity: sha512-/RX9RzeH2xU5ADE7n2Ykvmi9ED3FBGPAjw9u3zucrNNaEBIO0HPSYgL0NT7+3p147ojeSdaVu08F6hjpv31HJg==}
1023
+
engines: {node: ^20.0.0 || >=22.0.0}
1024
+
916
1025
to-regex-range@5.0.1:
917
1026
resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
918
1027
engines: {node: '>=8.0'}
···
1171
1280
'@esbuild/win32-x64@0.27.2':
1172
1281
optional: true
1173
1282
1283
+
'@oxfmt/darwin-arm64@0.20.0':
1284
+
optional: true
1285
+
1286
+
'@oxfmt/darwin-x64@0.20.0':
1287
+
optional: true
1288
+
1289
+
'@oxfmt/linux-arm64-gnu@0.20.0':
1290
+
optional: true
1291
+
1292
+
'@oxfmt/linux-arm64-musl@0.20.0':
1293
+
optional: true
1294
+
1295
+
'@oxfmt/linux-x64-gnu@0.20.0':
1296
+
optional: true
1297
+
1298
+
'@oxfmt/linux-x64-musl@0.20.0':
1299
+
optional: true
1300
+
1301
+
'@oxfmt/win32-arm64@0.20.0':
1302
+
optional: true
1303
+
1304
+
'@oxfmt/win32-x64@0.20.0':
1305
+
optional: true
1306
+
1174
1307
'@oxlint/darwin-arm64@1.35.0':
1175
1308
optional: true
1176
1309
···
1481
1614
1482
1615
kysely@0.28.9: {}
1483
1616
1617
+
lefthook-darwin-arm64@2.0.13:
1618
+
optional: true
1619
+
1620
+
lefthook-darwin-x64@2.0.13:
1621
+
optional: true
1622
+
1623
+
lefthook-freebsd-arm64@2.0.13:
1624
+
optional: true
1625
+
1626
+
lefthook-freebsd-x64@2.0.13:
1627
+
optional: true
1628
+
1629
+
lefthook-linux-arm64@2.0.13:
1630
+
optional: true
1631
+
1632
+
lefthook-linux-x64@2.0.13:
1633
+
optional: true
1634
+
1635
+
lefthook-openbsd-arm64@2.0.13:
1636
+
optional: true
1637
+
1638
+
lefthook-openbsd-x64@2.0.13:
1639
+
optional: true
1640
+
1641
+
lefthook-windows-arm64@2.0.13:
1642
+
optional: true
1643
+
1644
+
lefthook-windows-x64@2.0.13:
1645
+
optional: true
1646
+
1647
+
lefthook@2.0.13:
1648
+
optionalDependencies:
1649
+
lefthook-darwin-arm64: 2.0.13
1650
+
lefthook-darwin-x64: 2.0.13
1651
+
lefthook-freebsd-arm64: 2.0.13
1652
+
lefthook-freebsd-x64: 2.0.13
1653
+
lefthook-linux-arm64: 2.0.13
1654
+
lefthook-linux-x64: 2.0.13
1655
+
lefthook-openbsd-arm64: 2.0.13
1656
+
lefthook-openbsd-x64: 2.0.13
1657
+
lefthook-windows-arm64: 2.0.13
1658
+
lefthook-windows-x64: 2.0.13
1659
+
1484
1660
lines-and-columns@1.2.4: {}
1485
1661
1486
1662
lodash.snakecase@4.1.1: {}
···
1509
1685
once@1.4.0:
1510
1686
dependencies:
1511
1687
wrappy: 1.0.2
1688
+
1689
+
oxfmt@0.20.0:
1690
+
dependencies:
1691
+
tinypool: 2.0.0
1692
+
optionalDependencies:
1693
+
'@oxfmt/darwin-arm64': 0.20.0
1694
+
'@oxfmt/darwin-x64': 0.20.0
1695
+
'@oxfmt/linux-arm64-gnu': 0.20.0
1696
+
'@oxfmt/linux-arm64-musl': 0.20.0
1697
+
'@oxfmt/linux-x64-gnu': 0.20.0
1698
+
'@oxfmt/linux-x64-musl': 0.20.0
1699
+
'@oxfmt/win32-arm64': 0.20.0
1700
+
'@oxfmt/win32-x64': 0.20.0
1512
1701
1513
1702
oxlint@1.35.0:
1514
1703
optionalDependencies:
···
1732
1921
thread-stream@3.1.0:
1733
1922
dependencies:
1734
1923
real-require: 0.2.0
1924
+
1925
+
tinypool@2.0.0: {}
1735
1926
1736
1927
to-regex-range@5.0.1:
1737
1928
dependencies: