From 260bd0ed1054f176614999aebfea7d222b90fa10 Mon Sep 17 00:00:00 2001 From: Gen Tamura Date: Fri, 24 Oct 2025 10:28:54 +0900 Subject: [PATCH] chore: point to published @listee packages and add ci --- .github/workflows/ci.yml | 16 ++++++++++++++++ bun.lock | 16 ++++++++++++---- package.json | 4 ++-- src/commands/auth.ts | 24 +++++++++++++++++++----- src/services/auth-service.test.ts | 13 +++++++++++-- 5 files changed, 60 insertions(+), 13 deletions(-) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..30caf39 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,16 @@ +name: CI + +on: + push: + branches: [main] + pull_request: + +jobs: + lint: + uses: listee-dev/listee-ci/.github/workflows/lint.yml@main + + typecheck: + uses: listee-dev/listee-ci/.github/workflows/typecheck.yml@main + + test: + uses: listee-dev/listee-ci/.github/workflows/test.yml@main diff --git a/bun.lock b/bun.lock index cc49161..c190242 100644 --- a/bun.lock +++ b/bun.lock @@ -4,8 +4,8 @@ "": { "name": "listee-cli", "dependencies": { - "@listee/auth": "link:@listee/auth", - "@listee/types": "link:@listee/types", + "@listee/auth": "^0.2.3", + "@listee/types": "^0.2.3", "@napi-rs/keyring": "^1.2.0", "commander": "^12.1.0", "dotenv": "^16.4.5", @@ -37,9 +37,11 @@ "@biomejs/cli-win32-x64": ["@biomejs/cli-win32-x64@2.2.5", "", { "os": "win32", "cpu": "x64" }, "sha512-F/jhuXCssPFAuciMhHKk00xnCAxJRS/pUzVfXYmOMUp//XW7mO6QeCjsjvnm8L4AO/dG2VOB0O+fJPiJ2uXtIw=="], - "@listee/auth": ["@listee/auth@link:@listee/auth", {}], + "@listee/auth": ["@listee/auth@0.2.3", "", { "dependencies": { "@listee/db": "^0.2.3", "@listee/types": "^0.2.3", "jose": "^5.2.3" } }, "sha512-uKWmlxL1wji+/qILopv1iOc8G98fpBhmpTTAc7CgtaNK3bXdrbsKSQCNKUXxu9Q5bPsdWNcbLP1AyYznwEE9wQ=="], - "@listee/types": ["@listee/types@link:@listee/types", {}], + "@listee/db": ["@listee/db@0.2.3", "", { "dependencies": { "drizzle-orm": "^0.44.5", "postgres": "^3.4.7" } }, "sha512-RNijZvSbavrMITk+mKUwPon2XwcbxVYiQSLWPfrv83g+B3JpG1ClYId4qk21cJkUVH6Hn6O97rXN1XOQSKdKNw=="], + + "@listee/types": ["@listee/types@0.2.3", "", { "dependencies": { "@listee/db": "^0.2.3" } }, "sha512-eGOVIn4nCTIWuIC8fu07vjbBf14UU8DQKJQCQOpJjFjW7YP4hCUdjc64DhtYjlsK56xi/TiJprVw7MrpJfx4FA=="], "@napi-rs/keyring": ["@napi-rs/keyring@1.2.0", "", { "optionalDependencies": { "@napi-rs/keyring-darwin-arm64": "1.2.0", "@napi-rs/keyring-darwin-x64": "1.2.0", "@napi-rs/keyring-freebsd-x64": "1.2.0", "@napi-rs/keyring-linux-arm-gnueabihf": "1.2.0", "@napi-rs/keyring-linux-arm64-gnu": "1.2.0", "@napi-rs/keyring-linux-arm64-musl": "1.2.0", "@napi-rs/keyring-linux-riscv64-gnu": "1.2.0", "@napi-rs/keyring-linux-x64-gnu": "1.2.0", "@napi-rs/keyring-linux-x64-musl": "1.2.0", "@napi-rs/keyring-win32-arm64-msvc": "1.2.0", "@napi-rs/keyring-win32-ia32-msvc": "1.2.0", "@napi-rs/keyring-win32-x64-msvc": "1.2.0" } }, "sha512-d0d4Oyxm+v980PEq1ZH2PmS6cvpMIRc17eYpiU47KgW+lzxklMu6+HOEOPmxrpnF/XQZ0+Q78I2mgMhbIIo/dg=="], @@ -81,6 +83,12 @@ "dotenv": ["dotenv@16.6.1", "", {}, "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow=="], + "drizzle-orm": ["drizzle-orm@0.44.6", "", { "peerDependencies": { "@aws-sdk/client-rds-data": ">=3", "@cloudflare/workers-types": ">=4", "@electric-sql/pglite": ">=0.2.0", "@libsql/client": ">=0.10.0", "@libsql/client-wasm": ">=0.10.0", "@neondatabase/serverless": ">=0.10.0", "@op-engineering/op-sqlite": ">=2", "@opentelemetry/api": "^1.4.1", "@planetscale/database": ">=1.13", "@prisma/client": "*", "@tidbcloud/serverless": "*", "@types/better-sqlite3": "*", "@types/pg": "*", "@types/sql.js": "*", "@upstash/redis": ">=1.34.7", "@vercel/postgres": ">=0.8.0", "@xata.io/client": "*", "better-sqlite3": ">=7", "bun-types": "*", "expo-sqlite": ">=14.0.0", "gel": ">=2", "knex": "*", "kysely": "*", "mysql2": ">=2", "pg": ">=8", "postgres": ">=3", "sql.js": ">=1", "sqlite3": ">=5" }, "optionalPeers": ["@aws-sdk/client-rds-data", "@cloudflare/workers-types", "@electric-sql/pglite", "@libsql/client", "@libsql/client-wasm", "@neondatabase/serverless", "@op-engineering/op-sqlite", "@opentelemetry/api", "@planetscale/database", "@prisma/client", "@tidbcloud/serverless", "@types/better-sqlite3", "@types/pg", "@types/sql.js", "@upstash/redis", "@vercel/postgres", "@xata.io/client", "better-sqlite3", "bun-types", "expo-sqlite", "gel", "knex", "kysely", "mysql2", "pg", "postgres", "sql.js", "sqlite3"] }, "sha512-uy6uarrrEOc9K1u5/uhBFJbdF5VJ5xQ/Yzbecw3eAYOunv5FDeYkR2m8iitocdHBOHbvorviKOW5GVw0U1j4LQ=="], + + "jose": ["jose@5.10.0", "", {}, "sha512-s+3Al/p9g32Iq+oqXxkW//7jk2Vig6FF1CFqzVXoTUXt2qz89YWbL+OwS17NFYEvxC35n0FKeGO2LGYSxeM2Gg=="], + + "postgres": ["postgres@3.4.7", "", {}, "sha512-Jtc2612XINuBjIl/QTWsV5UvE8UHuNblcO3vVADSrKsrc6RqGX6lOW1cEo3CM2v0XG4Nat8nI+YM7/f26VxXLw=="], + "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], "undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], diff --git a/package.json b/package.json index 25e2ad6..36ff89b 100644 --- a/package.json +++ b/package.json @@ -14,8 +14,8 @@ "test": "bun test" }, "dependencies": { - "@listee/auth": "link:@listee/auth", - "@listee/types": "link:@listee/types", + "@listee/auth": "^0.2.3", + "@listee/types": "^0.2.3", "@napi-rs/keyring": "^1.2.0", "commander": "^12.1.0", "dotenv": "^16.4.5" diff --git a/src/commands/auth.ts b/src/commands/auth.ts index 1b3fbc2..f61d5c5 100644 --- a/src/commands/auth.ts +++ b/src/commands/auth.ts @@ -103,12 +103,19 @@ const startLoopbackServer = async (): Promise => { let settled = false; const server = createServer((req, res) => { - const finish = (status: number, body: string, contentType = "text/html"): void => { + const finish = ( + status: number, + body: string, + contentType = "text/html", + ): void => { res.writeHead(status, { "Content-Type": contentType }); res.end(body); }; - const respondWithJson = (status: number, payload: { title: string; message: string }): void => { + const respondWithJson = ( + status: number, + payload: { title: string; message: string }, + ): void => { finish(status, JSON.stringify(payload), "application/json"); }; @@ -174,7 +181,11 @@ const startLoopbackServer = async (): Promise => { }); const address = server.address(); - if (address === null || typeof address !== "object" || address.port === undefined) { + if ( + address === null || + typeof address !== "object" || + address.port === undefined + ) { server.close(); throw new Error("Failed to determine loopback server port."); } @@ -200,7 +211,8 @@ const startLoopbackServer = async (): Promise => { return { redirectUrl: `http://${LOOPBACK_HOST}:${address.port}/callback`, - waitForConfirmation: () => waitForConfirmation.finally(() => clearTimeout(timeout)), + waitForConfirmation: () => + waitForConfirmation.finally(() => clearTimeout(timeout)), shutdown, }; }; @@ -344,7 +356,9 @@ const signupAction = async (options: EmailOption): Promise => { try { await signup(email, password, loopback.redirectUrl); - console.log("📩 Confirmation email sent. Keep this terminal open while you click the link."); + console.log( + "📩 Confirmation email sent. Keep this terminal open while you click the link.", + ); const result = await loopback.waitForConfirmation(); console.log(`✅ Signup confirmed for ${result.account}.`); } finally { diff --git a/src/services/auth-service.test.ts b/src/services/auth-service.test.ts index 6b19f64..e7c8e09 100644 --- a/src/services/auth-service.test.ts +++ b/src/services/auth-service.test.ts @@ -67,11 +67,20 @@ describe("parseSignupFragment", () => { }; const header = encodeSegment(JSON.stringify({ alg: "HS256", typ: "JWT" })); - const payload = encodeSegment(JSON.stringify({ email: "user@example.com" })); + const payloadSegment = (): string => { + const currentEpoch = Math.floor(Date.now() / 1000); + const payload = { + sub: "user-id", + email: "user@example.com", + exp: currentEpoch + 3600, + iat: currentEpoch, + }; + return encodeSegment(JSON.stringify(payload)); + }; const signature = encodeSegment("signature"); - const accessToken = `${header}.${payload}.${signature}`; it("parses tokens from a confirmation fragment", () => { + const accessToken = `${header}.${payloadSegment()}.${signature}`; const fragment = `#access_token=${accessToken}&refresh_token=refresh123&expires_in=3600&token_type=bearer&type=signup`; const result = parseSignupFragment(fragment);