diff --git a/.eslintignore b/.eslintignore index 4ebc8ae..618ef2b 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1 +1,3 @@ +test/fixtures coverage +__snapshots__ diff --git a/.eslintrc b/.eslintrc index c799fe5..9bcdb46 100644 --- a/.eslintrc +++ b/.eslintrc @@ -1,3 +1,6 @@ { - "extends": "eslint-config-egg" + "extends": [ + "eslint-config-egg/typescript", + "eslint-config-egg/lib/rules/enforce-node-prefix" + ] } diff --git a/.github/workflows/node.yml b/.github/workflows/nodejs.yml similarity index 81% rename from .github/workflows/node.yml rename to .github/workflows/nodejs.yml index 73ca141..25bb2a4 100644 --- a/.github/workflows/node.yml +++ b/.github/workflows/nodejs.yml @@ -11,7 +11,6 @@ jobs: name: Node.js uses: node-modules/github-actions/.github/workflows/node-test-mysql.yml@master with: - os: 'ubuntu-latest' - version: '14, 16, 18, 20, 22' + version: '18.19.0, 18, 20, 22' secrets: CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} diff --git a/.github/workflows/pkg.pr.new.yml b/.github/workflows/pkg.pr.new.yml new file mode 100644 index 0000000..bac3fac --- /dev/null +++ b/.github/workflows/pkg.pr.new.yml @@ -0,0 +1,23 @@ +name: Publish Any Commit +on: [push, pull_request] + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - run: corepack enable + - uses: actions/setup-node@v4 + with: + node-version: 20 + + - name: Install dependencies + run: npm install + + - name: Build + run: npm run prepublishOnly --if-present + + - run: npx pkg-pr-new publish diff --git a/.gitignore b/.gitignore index 60753d7..c010914 100644 --- a/.gitignore +++ b/.gitignore @@ -3,5 +3,9 @@ npm-debug.log node_modules/ coverage/ test/fixtures/**/run -dump.rdb +.DS_Store +.tshy* +.eslintcache +dist package-lock.json +.package-lock.json diff --git a/History.md b/CHANGELOG.md similarity index 100% rename from History.md rename to CHANGELOG.md diff --git a/README.md b/README.md index 1fd2127..47d47cf 100644 --- a/README.md +++ b/README.md @@ -1,22 +1,29 @@ -# egg-session +# @eggjs/session [![NPM version][npm-image]][npm-url] +[![Node.js CI](https://github.com/eggjs/session/actions/workflows/nodejs.yml/badge.svg)](https://github.com/eggjs/session/actions/workflows/nodejs.yml) [![Test coverage][codecov-image]][codecov-url] +[![Known Vulnerabilities][snyk-image]][snyk-url] [![npm download][download-image]][download-url] - -[npm-image]: https://img.shields.io/npm/v/egg-session.svg?style=flat-square -[npm-url]: https://npmjs.org/package/egg-session -[codecov-image]: https://codecov.io/github/eggjs/egg-session/coverage.svg?branch=master -[codecov-url]: https://codecov.io/github/eggjs/egg-session?branch=master -[download-image]: https://img.shields.io/npm/dm/egg-session.svg?style=flat-square -[download-url]: https://npmjs.org/package/egg-session +[![Node.js Version](https://img.shields.io/node/v/@eggjs/session.svg?style=flat)](https://nodejs.org/en/download/) +[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)](https://makeapullrequest.com) +![CodeRabbit Pull Request Reviews](https://img.shields.io/coderabbit/prs/github/eggjs/session) + +[npm-image]: https://img.shields.io/npm/v/@eggjs/session.svg?style=flat-square +[npm-url]: https://npmjs.org/package/@eggjs/session +[codecov-image]: https://codecov.io/gh/eggjs/session/branch/master/graph/badge.svg +[codecov-url]: https://codecov.io/gh/eggjs/session +[snyk-image]: https://snyk.io/test/npm/@eggjs/session/badge.svg?style=flat-square +[snyk-url]: https://snyk.io/test/npm/@eggjs/session +[download-image]: https://img.shields.io/npm/dm/@eggjs/session.svg?style=flat-square +[download-url]: https://npmjs.org/package/@eggjs/session Session plugin for egg, based on [koa-session](https://github.com/koajs/session). ## Install ```bash -npm i egg-session --save +npm i @eggjs/session ``` ## Usage @@ -118,7 +125,7 @@ Please open an issue [here](https://github.com/eggjs/egg/issues). ## License -[MIT](https://github.com/eggjs/egg-session/blob/master/LICENSE) +[MIT](LICENSE) ## Contributors diff --git a/__snapshots__/session.test.ts.js b/__snapshots__/session.test.ts.js new file mode 100644 index 0000000..3d5d9c0 --- /dev/null +++ b/__snapshots__/session.test.ts.js @@ -0,0 +1,12 @@ +exports['test/app/middlewares/session.test.js sessionStore should keep session config stable 1'] = { + "key": "EGG_SESS", + "maxAge": 86400000, + "autoCommit": true, + "overwrite": true, + "httpOnly": true, + "signed": true, + "rolling": false, + "renew": false, + "logValue": true, + "store": {} +} diff --git a/app.js b/app.js deleted file mode 100644 index d40c00a..0000000 --- a/app.js +++ /dev/null @@ -1,19 +0,0 @@ -'use strict'; - -module.exports = function(app) { - if (!app.config.session.httpOnly) { - app.logger.warn('[egg-session]: please set `config.session.httpOnly` to true. It is very dangerous if session can read by client JavaScript.'); - } - app.config.coreMiddleware.push('session'); - - // listen on session's events - app.on('session:missed', ({ ctx, key }) => { - ctx.coreLogger.warn('[session][missed] key(%s)', key); - }); - app.on('session:expired', ({ ctx, key, value }) => { - ctx.coreLogger.warn('[session][expired] key(%s) value(%j)', key, app.config.session.logValue ? value : ''); - }); - app.on('session:invalid', ({ ctx, key, value }) => { - ctx.coreLogger.warn('[session][invalid] key(%s) value(%j)', key, app.config.session.logValue ? value : ''); - }); -}; diff --git a/app/extend/application.js b/app/extend/application.js deleted file mode 100644 index c647f57..0000000 --- a/app/extend/application.js +++ /dev/null @@ -1,55 +0,0 @@ -'use strict'; - -const assert = require('assert'); - -module.exports = { - /** - * set session external store - * - * ```js - * app.sessionStore = { - * * get() {}, - * * set() {}, - * * destory() {}, - * }; - * - * app.sessionStore = class SessionStore { - * constructor(app) { - * } - * * get() {}, - * * set() {}, - * * destroy() {}, - * } - * ``` - * @param {Class|Object} store session store class or instance - */ - set sessionStore(store) { - if (this.config.session.store && this.config.env !== 'unittest') { - this.logger.warn('[egg-session] sessionStore already exists and will be overwrite'); - } - - // supoprt this.sesionStore = null to disable external store - if (!store) { - this.config.session.store = undefined; - return; - } - - if (typeof store === 'function') store = new store(this); - assert(typeof store.get === 'function', 'store.get must be function'); - assert(typeof store.set === 'function', 'store.set must be function'); - assert(typeof store.destroy === 'function', 'store.destroy must be function'); - store.get = this.toAsyncFunction(store.get); - store.set = this.toAsyncFunction(store.set); - store.destroy = this.toAsyncFunction(store.destroy); - this.config.session.store = store; - }, - - /** - * get sessionStore - * - * @return {Object} session store instance - */ - get sessionStore() { - return this.config.session.store; - }, -}; diff --git a/app/middleware/session.js b/app/middleware/session.js deleted file mode 100644 index b0e1c59..0000000 --- a/app/middleware/session.js +++ /dev/null @@ -1,3 +0,0 @@ -'use strict'; - -module.exports = require('koa-session'); diff --git a/config/config.default.js b/config/config.default.js deleted file mode 100644 index f445487..0000000 --- a/config/config.default.js +++ /dev/null @@ -1,10 +0,0 @@ -'use strict'; - -exports.session = { - maxAge: 24 * 3600 * 1000, // ms - key: 'EGG_SESS', - httpOnly: true, - encrypt: true, - // sameSite: null, - logValue: true, -}; diff --git a/package.json b/package.json index c581527..29aea83 100644 --- a/package.json +++ b/package.json @@ -1,19 +1,23 @@ { - "name": "egg-session", + "name": "@eggjs/session", "version": "3.3.0", + "publishConfig": { + "access": "public" + }, "description": "session plugin for egg", "eggPlugin": { - "name": "session" + "name": "session", + "exports": { + "import": "./dist/esm", + "require": "./dist/commonjs", + "typescript": "./src" + } }, - "files": [ - "config", - "app", - "app.js" - ], "repository": { "type": "git", "url": "git@github.com:eggjs/session.git" }, + "author": "dead_horse", "keywords": [ "egg", "egg-plugin", @@ -21,28 +25,68 @@ "session", "cookie" ], + "engines": { + "node": ">= 18.19.0" + }, "dependencies": { - "koa-session": "^6.0.0" + "@eggjs/core": "^6.2.13", + "koa-session": "^7.0.2", + "zod": "^3.24.1" }, "devDependencies": { - "egg": "^3.17.5", - "egg-bin": "6", - "egg-mock": "5", - "egg-redis": "^1.0.1", + "@arethetypeswrong/cli": "^0.17.1", + "@eggjs/bin": "7", + "@eggjs/mock": "^6.0.5", + "@eggjs/supertest": "^8.2.0", + "@eggjs/tsconfig": "1", + "@types/mocha": "10", + "@types/node": "22", + "egg": "^4.0.1", + "egg-redis": "^2.6.0", "eslint": "8", - "eslint-config-egg": "12", - "mz-modules": "^2.0.0", - "supertest": "^3.0.0" - }, - "engines": { - "node": ">=8.0.0" + "eslint-config-egg": "14", + "rimraf": "6", + "snap-shot-it": "^7.9.10", + "tshy": "3", + "tshy-after": "1", + "typescript": "5" }, "scripts": { - "lint": "eslint .", - "test": "npm run lint -- --fix && npm run test-local", - "test-local": "egg-bin test --ts false", - "cov": "egg-bin cov --ts false", - "ci": "npm run lint && npm run cov" + "lint": "eslint --cache src test --ext .ts", + "pretest": "npm run clean && npm run lint -- --fix", + "test": "egg-bin test", + "test:snapshot:update": "SNAPSHOT_UPDATE=1 egg-bin test", + "preci": "npm run clean && npm run lint", + "ci": "egg-bin cov", + "postci": "npm run prepublishOnly && npm run clean", + "clean": "rimraf dist", + "prepublishOnly": "tshy && tshy-after && attw --pack" + }, + "type": "module", + "tshy": { + "exports": { + ".": "./src/index.ts", + "./package.json": "./package.json" + } + }, + "exports": { + ".": { + "import": { + "types": "./dist/esm/index.d.ts", + "default": "./dist/esm/index.js" + }, + "require": { + "types": "./dist/commonjs/index.d.ts", + "default": "./dist/commonjs/index.js" + } + }, + "./package.json": "./package.json" }, - "author": "dead_horse" + "files": [ + "dist", + "src" + ], + "types": "./dist/commonjs/index.d.ts", + "main": "./dist/commonjs/index.js", + "module": "./dist/esm/index.js" } diff --git a/src/app.ts b/src/app.ts new file mode 100644 index 0000000..0e0f1d7 --- /dev/null +++ b/src/app.ts @@ -0,0 +1,30 @@ +import type { ILifecycleBoot, EggCore } from '@eggjs/core'; +import { SessionConfig } from './config/config.default.js'; + +export default class AppBoot implements ILifecycleBoot { + private readonly app; + + constructor(app: EggCore) { + this.app = app; + } + + configWillLoad() { + const app = this.app; + SessionConfig.parse(app.config.session); + + if (!app.config.session.httpOnly) { + app.coreLogger.warn('[@eggjs/session]: please set `config.session.httpOnly` to true. It is very dangerous if session can read by client JavaScript.'); + } + app.config.coreMiddleware.push('session'); + // listen on session's events + app.on('session:missed', ({ ctx, key }) => { + ctx.coreLogger.warn('[session][missed] key(%s)', key); + }); + app.on('session:expired', ({ ctx, key, value }) => { + ctx.coreLogger.warn('[session][expired] key(%s) value(%j)', key, app.config.session.logValue ? value : ''); + }); + app.on('session:invalid', ({ ctx, key, value }) => { + ctx.coreLogger.warn('[session][invalid] key(%s) value(%j)', key, app.config.session.logValue ? value : ''); + }); + } +} diff --git a/src/app/extend/application.ts b/src/app/extend/application.ts new file mode 100644 index 0000000..6743bfd --- /dev/null +++ b/src/app/extend/application.ts @@ -0,0 +1,59 @@ +import assert from 'node:assert'; +import { EggCore } from '@eggjs/core'; +import type { SessionConfig } from '../../config/config.default.js'; + +export type SessionStore = Required['store']; + +export type SessionStoreOrAppSessionStoreClass = SessionStore | { + new(app: Application): SessionStore; +}; + +export default class Application extends EggCore { + /** + * set session external store + * + * ```js + * app.sessionStore = { + * get(key): Promise, + * set(key, data): Promise, + * destroy(key): Promise, + * }; + * + * app.sessionStore = class SessionStore { + * constructor(app) { + * } + * get(key): Promise, + * set(key, data): Promise, + * destroy(key): Promise, + * } + * ``` + * @param {Class|Object} store session store class or instance + */ + set sessionStore(store: SessionStoreOrAppSessionStoreClass | null | undefined) { + if (this.config.session.store && this.config.env !== 'unittest') { + this.coreLogger.warn('[@eggjs/session] sessionStore already exists and will be overwrite'); + } + + // support this.sessionStore = null to disable external store + if (!store) { + this.config.session.store = undefined; + this.coreLogger.info('[@eggjs/session] sessionStore is disabled'); + return; + } + + if (typeof store === 'function') { + store = new store(this); + } + assert(typeof store.get === 'function', 'store.get must be function'); + assert(typeof store.set === 'function', 'store.set must be function'); + assert(typeof store.destroy === 'function', 'store.destroy must be function'); + this.config.session.store = store; + } + + /** + * get sessionStore instance + */ + get sessionStore(): SessionStore | undefined { + return this.config.session.store; + } +} diff --git a/src/app/middleware/session.ts b/src/app/middleware/session.ts new file mode 100644 index 0000000..6dd302e --- /dev/null +++ b/src/app/middleware/session.ts @@ -0,0 +1,3 @@ +import { createSession } from 'koa-session'; + +export default createSession; diff --git a/src/config/config.default.ts b/src/config/config.default.ts new file mode 100644 index 0000000..bd84877 --- /dev/null +++ b/src/config/config.default.ts @@ -0,0 +1,17 @@ +import z from 'zod'; +import { SessionOptions } from 'koa-session'; + +export const SessionConfig = SessionOptions.extend({ + logValue: z.boolean().default(true), +}); + +export type SessionConfig = z.infer; + +export default { + session: SessionConfig.parse({ + maxAge: 24 * 3600 * 1000, // ms, one day + key: 'EGG_SESS', + httpOnly: true, + encrypt: true, + }), +}; diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..842b344 --- /dev/null +++ b/src/index.ts @@ -0,0 +1,3 @@ +import './types.js'; + +export * from './config/config.default.js'; diff --git a/src/types.ts b/src/types.ts new file mode 100644 index 0000000..21d0f16 --- /dev/null +++ b/src/types.ts @@ -0,0 +1,8 @@ +import type { SessionConfig } from './config/config.default.js'; + +declare module '@eggjs/core' { + // add EggAppConfig overrides types + interface EggAppConfig { + session: SessionConfig; + } +} diff --git a/src/typings/index.d.ts b/src/typings/index.d.ts new file mode 100644 index 0000000..53c65c7 --- /dev/null +++ b/src/typings/index.d.ts @@ -0,0 +1,4 @@ +// make sure to import egg typings and let typescript know about it +// @see https://github.com/whxaxes/blog/issues/11 +// and https://www.typescriptlang.org/docs/handbook/declaration-merging.html +import 'egg'; diff --git a/test/app/middleware/session.test.js b/test/app/middleware/session.test.ts similarity index 74% rename from test/app/middleware/session.test.js rename to test/app/middleware/session.test.ts index 18fb60b..c2a5e96 100644 --- a/test/app/middleware/session.test.js +++ b/test/app/middleware/session.test.ts @@ -1,70 +1,72 @@ -'use strict'; - -const sleep = require('mz-modules/sleep'); -const request = require('supertest'); -const assert = require('assert'); -const mm = require('egg-mock'); +import assert from 'node:assert'; +import { scheduler } from 'node:timers/promises'; +import { request, TestAgent } from '@eggjs/supertest'; +import { mm, MockApplication } from '@eggjs/mock'; +import snapshot from 'snap-shot-it'; describe('test/app/middlewares/session.test.js', () => { - let app; - let agent; + let app: MockApplication; + let agent: TestAgent; afterEach(mm.restore); - describe('sesionStore', () => { - + describe('sessionStore', () => { before(() => { app = mm.app({ baseDir: 'memory-session' }); return app.ready(); }); beforeEach(() => { - agent = request.agent(app.callback()); + agent = new TestAgent(app.callback()); }); after(() => app.close()); - it('should get sessionStore', function* () { + it('should keep session config stable', () => { + snapshot(app.config.session); + }); + + it('should get sessionStore', async () => { mm.empty(app.sessionStore, 'set'); - yield agent + await agent .get('/set?foo=bar') .expect(200) .expect({ foo: 'bar' }) .expect('set-cookie', /EGG_SESS=.*?;/); - yield agent.get('/get') + await agent.get('/get') .expect(200) .expect({}); }); - it('should session store can be change', function* () { + it('should session store can be change', async () => { mm(app.config, 'env', 'local'); - yield agent + await agent .get('/set?foo=bar') .expect(200) .expect({ foo: 'bar' }) .expect(res => { - const cookie = res.headers['set-cookie'].join('|'); + const cookie = res.get('Set-Cookie')!.join('|'); assert(!cookie.includes('; samesite=none;')); }) .expect('set-cookie', /EGG_SESS=.*?;/); - yield agent.get('/get') + await agent.get('/get') .expect(200) .expect({ foo: 'bar' }); app.sessionStore = null; - yield agent.get('/get') + await agent.get('/get') .expect(200) .expect({}); }); }); describe('httpOnly', () => { - it('should warn when httponly false', function* () { + it('should warn when httponly false', async () => { app = mm.app({ baseDir: 'httponly-false-session' }); - yield app.ready(); - app.expectLog('[egg-session]: please set `config.session.httpOnly` to true. It is very dangerous if session can read by client JavaScript.'); - yield app.close(); + await app.ready(); + app.expectLog('[@eggjs/session]: please set `config.session.httpOnly` to true. It is very dangerous if session can read by client JavaScript.', 'coreLogger'); + await app.close(); }); }); @@ -74,7 +76,7 @@ describe('test/app/middlewares/session.test.js', () => { return app.ready(); }); beforeEach(() => { - agent = request.agent(app.callback()); + agent = new TestAgent(app.callback()); }); after(() => app.close()); @@ -86,7 +88,7 @@ describe('test/app/middlewares/session.test.js', () => { .expect(200) .expect({ foo: 'bar' }) .expect(res => { - const cookie = res.headers['set-cookie'].join('|'); + const cookie = res.get('Set-Cookie')!.join('|'); assert(cookie.includes('; samesite=none;')); }); }); @@ -98,7 +100,7 @@ describe('test/app/middlewares/session.test.js', () => { return app.ready(); }); beforeEach(() => { - agent = request.agent(app.callback()); + agent = new TestAgent(app.callback()); }); after(() => app.close()); @@ -122,13 +124,13 @@ describe('test/app/middlewares/session.test.js', () => { return app.ready(); }); beforeEach(() => { - agent = request.agent(app.callback()); + agent = new TestAgent(app.callback()); app.mockLog(); }); after(() => app.close()); it('when logValue is true, should log the session value', async () => { - let cookie; + let cookie = ''; app.mockLog(); mm(app.config.session, 'logValue', true); @@ -136,10 +138,10 @@ describe('test/app/middlewares/session.test.js', () => { .get('/maxAge?maxAge=100') .expect(200) .expect(res => { - cookie = res.headers['set-cookie'].join(';'); + cookie = res.get('Set-Cookie')!.join(';'); }); - await sleep(200); + await scheduler.wait(200); await request(app.callback()) .get('/get') @@ -152,16 +154,16 @@ describe('test/app/middlewares/session.test.js', () => { it('when logValue is false, should not log the session value', async () => { mm(app.config.session, 'logValue', false); app.mockLog(); - let cookie; + let cookie = ''; await agent .get('/maxAge?maxAge=100') .expect(200) .expect(res => { - cookie = res.headers['set-cookie'].join(';'); + cookie = res.get('Set-Cookie')!.join(';'); }); - await sleep(200); + await scheduler.wait(200); await request(app.callback()) .get('/get') @@ -169,7 +171,7 @@ describe('test/app/middlewares/session.test.js', () => { .expect(200) .expect({}); - await sleep(1000); + await scheduler.wait(1000); app.expectLog('[session][expired] key(undefined) value("")', 'coreLogger'); }); @@ -187,7 +189,7 @@ describe('test/app/middlewares/session.test.js', () => { await agent .get('/get'); - await sleep(1000); + await scheduler.wait(1000); app.expectLog('[session][invalid] key(undefined) value("")', 'coreLogger'); }); @@ -199,7 +201,7 @@ describe('test/app/middlewares/session.test.js', () => { return app.ready(); }); beforeEach(() => { - agent = request.agent(app.callback()); + agent = new TestAgent(app.callback()); }); after(() => app.close()); @@ -211,7 +213,7 @@ describe('test/app/middlewares/session.test.js', () => { .expect(200) .expect({ foo: 'bar' }) .expect(res => { - const cookie = res.headers['set-cookie'].join('|'); + const cookie = res.get('Set-Cookie')!.join('|'); assert(!cookie.includes('expires')); assert(!cookie.includes('max-age')); }); @@ -222,7 +224,7 @@ describe('test/app/middlewares/session.test.js', () => { .get('/maxAge?maxAge=session') .expect(200) .expect(res => { - const cookie = res.headers['set-cookie'].join(';'); + const cookie = res.get('Set-Cookie')!.join(';'); assert(cookie.match(/EGG_SESS=.*?;/)); assert(!cookie.includes('expires')); assert(!cookie.includes('max-age')); @@ -245,13 +247,13 @@ describe('test/app/middlewares/session.test.js', () => { return app.ready(); }); beforeEach(() => { - agent = request.agent(app.callback()); + agent = new TestAgent(app.callback()); }); after(() => app.close()); if (name === 'redis-session') { - it('should get with ', function* () { - yield agent + it('should get with ', async () => { + await agent .get('/set?foo=bar') .expect(200) .expect({ foo: 'bar' }) @@ -259,91 +261,91 @@ describe('test/app/middlewares/session.test.js', () => { mm.empty(app.redis, 'get'); - yield agent + await agent .get('/get') .expect(200) .expect({}); }); } - it('should get empty session and do not set cookie when session not populated', function* () { - yield agent + it('should get empty session and do not set cookie when session not populated', async () => { + await agent .get('/get') .expect(200) .expect({}) .expect(res => { - assert(!res.header['set-cookie'].join('').match(/EGG_SESS/)); + assert(!res.get('Set-Cookie')!.join('').match(/EGG_SESS/)); }); }); - it('should ctx.session= change the session', function* () { - yield agent + it('should ctx.session= change the session', async () => { + await agent .get('/set?foo=bar') .expect(200) .expect({ foo: 'bar' }) .expect('set-cookie', /EGG_SESS=.*?;/); }); - it('should ctx.session.key= change the session', function* () { - yield agent + it('should ctx.session.key= change the session', async () => { + await agent .get('/set?key=foo&foo=bar') .expect(200) .expect({ key: 'foo', foo: 'bar' }) .expect('set-cookie', /EGG_SESS=.*?;/); - yield agent + await agent .get('/setKey?key=bar') .expect(200) .expect({ key: 'bar', foo: 'bar' }) .expect('set-cookie', /EGG_SESS=.*?;/); }); - it('should ctx.session=null remove the session', function* () { - yield agent + it('should ctx.session=null remove the session', async () => { + await agent .get('/set?key=foo&foo=bar') .expect(200) .expect({ key: 'foo', foo: 'bar' }) .expect('set-cookie', /EGG_SESS=.*?;/); - yield agent + await agent .get('/remove') .expect(204) .expect('set-cookie', /EGG_SESS=;/); - yield agent + await agent .get('/get') .expect(200) .expect({}); }); - it('should ctx.session.maxAge= change maxAge', function* () { - yield agent + it('should ctx.session.maxAge= change maxAge', async () => { + await agent .get('/set?key=foo&foo=bar') .expect(200) .expect({ key: 'foo', foo: 'bar' }) .expect('set-cookie', /EGG_SESS=.*?;/); - let cookie; + let cookie = ''; - yield agent + await agent .get('/maxAge?maxAge=100') .expect(200) .expect({ key: 'foo', foo: 'bar' }) .expect(res => { - cookie = res.headers['set-cookie'].join(';'); + cookie = res.get('Set-Cookie')!.join(';'); assert(cookie.match(/EGG_SESS=.*?;/)); assert(cookie.match(/expires=/)); assert(cookie.match(/max-age=/)); }); - yield sleep(200); + await scheduler.wait(200); - yield agent + await agent .get('/get') .expect(200) .expect({}); - yield request(app.callback()) + await request(app.callback()) .get('/get') .set('cookie', cookie) .expect(200) diff --git a/test/fixtures/chips/app/controller/home.js b/test/fixtures/chips/app/controller/home.js index 8841cfc..1bf7974 100644 --- a/test/fixtures/chips/app/controller/home.js +++ b/test/fixtures/chips/app/controller/home.js @@ -1,5 +1,3 @@ -'use strict'; - exports.get = async ctx => { ctx.body = ctx.session; }; diff --git a/test/fixtures/chips/app/router.js b/test/fixtures/chips/app/router.js index 7b32aa3..930d446 100644 --- a/test/fixtures/chips/app/router.js +++ b/test/fixtures/chips/app/router.js @@ -1,5 +1,3 @@ -'use strict'; - module.exports = app => { app.get('/get', 'home.get'); app.get('/set', 'home.set'); diff --git a/test/fixtures/cookie-session/app/controller/home.js b/test/fixtures/cookie-session/app/controller/home.js index b122fac..69e8dce 100644 --- a/test/fixtures/cookie-session/app/controller/home.js +++ b/test/fixtures/cookie-session/app/controller/home.js @@ -1,25 +1,23 @@ -'use strict'; - -exports.get = function* (ctx) { +exports.get = async function(ctx) { ctx.body = ctx.session; }; -exports.set = function* (ctx) { +exports.set = async function(ctx) { ctx.session = ctx.query; ctx.body = ctx.session; }; -exports.setKey = function* (ctx) { +exports.setKey = async function(ctx) { ctx.session.key = ctx.query.key; ctx.body = ctx.session; }; -exports.remove = function* (ctx) { +exports.remove = async function(ctx) { ctx.session = null; ctx.body = ctx.session; }; -exports.maxAge = function* (ctx) { - ctx.session.maxAge = Number(this.query.maxAge); +exports.maxAge = async function(ctx) { + ctx.session.maxAge = Number(ctx.query.maxAge); ctx.body = ctx.session; }; diff --git a/test/fixtures/httponly-false-session/app/controller/home.js b/test/fixtures/httponly-false-session/app/controller/home.js index b122fac..69e8dce 100644 --- a/test/fixtures/httponly-false-session/app/controller/home.js +++ b/test/fixtures/httponly-false-session/app/controller/home.js @@ -1,25 +1,23 @@ -'use strict'; - -exports.get = function* (ctx) { +exports.get = async function(ctx) { ctx.body = ctx.session; }; -exports.set = function* (ctx) { +exports.set = async function(ctx) { ctx.session = ctx.query; ctx.body = ctx.session; }; -exports.setKey = function* (ctx) { +exports.setKey = async function(ctx) { ctx.session.key = ctx.query.key; ctx.body = ctx.session; }; -exports.remove = function* (ctx) { +exports.remove = async function(ctx) { ctx.session = null; ctx.body = ctx.session; }; -exports.maxAge = function* (ctx) { - ctx.session.maxAge = Number(this.query.maxAge); +exports.maxAge = async function(ctx) { + ctx.session.maxAge = Number(ctx.query.maxAge); ctx.body = ctx.session; }; diff --git a/test/fixtures/logValue-false-session/app/controller/home.js b/test/fixtures/logValue-false-session/app/controller/home.js index b122fac..69e8dce 100644 --- a/test/fixtures/logValue-false-session/app/controller/home.js +++ b/test/fixtures/logValue-false-session/app/controller/home.js @@ -1,25 +1,23 @@ -'use strict'; - -exports.get = function* (ctx) { +exports.get = async function(ctx) { ctx.body = ctx.session; }; -exports.set = function* (ctx) { +exports.set = async function(ctx) { ctx.session = ctx.query; ctx.body = ctx.session; }; -exports.setKey = function* (ctx) { +exports.setKey = async function(ctx) { ctx.session.key = ctx.query.key; ctx.body = ctx.session; }; -exports.remove = function* (ctx) { +exports.remove = async function(ctx) { ctx.session = null; ctx.body = ctx.session; }; -exports.maxAge = function* (ctx) { - ctx.session.maxAge = Number(this.query.maxAge); +exports.maxAge = async function(ctx) { + ctx.session.maxAge = Number(ctx.query.maxAge); ctx.body = ctx.session; }; diff --git a/test/fixtures/memory-session-generator/app.js b/test/fixtures/memory-session-generator/app.js index 665adf3..c04e37f 100644 --- a/test/fixtures/memory-session-generator/app.js +++ b/test/fixtures/memory-session-generator/app.js @@ -1,18 +1,16 @@ -'use strict'; - const sessions = {}; module.exports = app => { app.sessionStore = { - * get(key) { + async get(key) { return sessions[key]; }, - * set(key, value) { + async set(key, value) { sessions[key] = value; }, - * destroy(key) { + async destroy(key) { sessions[key] = undefined; }, }; diff --git a/test/fixtures/memory-session-generator/app/controller/home.js b/test/fixtures/memory-session-generator/app/controller/home.js index b122fac..69e8dce 100644 --- a/test/fixtures/memory-session-generator/app/controller/home.js +++ b/test/fixtures/memory-session-generator/app/controller/home.js @@ -1,25 +1,23 @@ -'use strict'; - -exports.get = function* (ctx) { +exports.get = async function(ctx) { ctx.body = ctx.session; }; -exports.set = function* (ctx) { +exports.set = async function(ctx) { ctx.session = ctx.query; ctx.body = ctx.session; }; -exports.setKey = function* (ctx) { +exports.setKey = async function(ctx) { ctx.session.key = ctx.query.key; ctx.body = ctx.session; }; -exports.remove = function* (ctx) { +exports.remove = async function(ctx) { ctx.session = null; ctx.body = ctx.session; }; -exports.maxAge = function* (ctx) { - ctx.session.maxAge = Number(this.query.maxAge); +exports.maxAge = async function(ctx) { + ctx.session.maxAge = Number(ctx.query.maxAge); ctx.body = ctx.session; }; diff --git a/test/fixtures/memory-session/app.js b/test/fixtures/memory-session/app.js index 6597abd..e534f46 100644 --- a/test/fixtures/memory-session/app.js +++ b/test/fixtures/memory-session/app.js @@ -1,19 +1,23 @@ -'use strict'; - const sessions = {}; -module.exports = app => { - app.sessionStore = { - async get(key) { - return sessions[key]; - }, - - async set(key, value) { - sessions[key] = value; - }, +module.exports = class AppBootHook { + constructor(app) { + this.app = app; + } - async destroy(key) { - sessions[key] = undefined; - }, - }; -}; + async willReady() { + this.app.sessionStore = { + async get(key) { + return sessions[key]; + }, + + async set(key, value) { + sessions[key] = value; + }, + + async destroy(key) { + sessions[key] = undefined; + }, + }; + } +} diff --git a/test/fixtures/memory-session/app/router.js b/test/fixtures/memory-session/app/router.js index d5938ed..98f885c 100644 --- a/test/fixtures/memory-session/app/router.js +++ b/test/fixtures/memory-session/app/router.js @@ -1,5 +1,3 @@ -'use strict'; - module.exports = function(app) { app.get('/get', 'home.get'); app.get('/set', 'home.set'); diff --git a/test/fixtures/redis-session/app.js b/test/fixtures/redis-session/app.js index a23df9b..2ee9bc4 100644 --- a/test/fixtures/redis-session/app.js +++ b/test/fixtures/redis-session/app.js @@ -1,24 +1,22 @@ -'use strict'; - module.exports = app => { // set redis session store app.sessionStore = class Store { constructor(app) { this.app = app; } - * get(key) { - const res = yield this.app.redis.get(key); + async get(key) { + const res = await this.app.redis.get(key); if (!res) return null; return JSON.parse(res); } - * set(key, value, maxAge) { + async set(key, value, maxAge) { value = JSON.stringify(value); - yield this.app.redis.set(key, value, 'PX', maxAge); + await this.app.redis.set(key, value, 'PX', maxAge); } - * destroy(key) { - yield this.app.redis.del(key); + async destroy(key) { + await this.app.redis.del(key); } }; }; diff --git a/test/fixtures/redis-session/app/controller/home.js b/test/fixtures/redis-session/app/controller/home.js index b122fac..69e8dce 100644 --- a/test/fixtures/redis-session/app/controller/home.js +++ b/test/fixtures/redis-session/app/controller/home.js @@ -1,25 +1,23 @@ -'use strict'; - -exports.get = function* (ctx) { +exports.get = async function(ctx) { ctx.body = ctx.session; }; -exports.set = function* (ctx) { +exports.set = async function(ctx) { ctx.session = ctx.query; ctx.body = ctx.session; }; -exports.setKey = function* (ctx) { +exports.setKey = async function(ctx) { ctx.session.key = ctx.query.key; ctx.body = ctx.session; }; -exports.remove = function* (ctx) { +exports.remove = async function(ctx) { ctx.session = null; ctx.body = ctx.session; }; -exports.maxAge = function* (ctx) { - ctx.session.maxAge = Number(this.query.maxAge); +exports.maxAge = async function(ctx) { + ctx.session.maxAge = Number(ctx.query.maxAge); ctx.body = ctx.session; }; diff --git a/test/fixtures/redis-session/app/router.js b/test/fixtures/redis-session/app/router.js index d5938ed..98f885c 100644 --- a/test/fixtures/redis-session/app/router.js +++ b/test/fixtures/redis-session/app/router.js @@ -1,5 +1,3 @@ -'use strict'; - module.exports = function(app) { app.get('/get', 'home.get'); app.get('/set', 'home.set'); diff --git a/test/fixtures/samesite-none-session/app/controller/home.js b/test/fixtures/samesite-none-session/app/controller/home.js index 3707362..1577a8d 100644 --- a/test/fixtures/samesite-none-session/app/controller/home.js +++ b/test/fixtures/samesite-none-session/app/controller/home.js @@ -1,10 +1,8 @@ -'use strict'; - -exports.get = function* (ctx) { +exports.get = async function(ctx) { ctx.body = ctx.session; }; -exports.set = function* (ctx) { +exports.set = async function(ctx) { ctx.session = ctx.query; ctx.body = ctx.session; }; diff --git a/test/fixtures/session-maxage-session/app/controller/home.js b/test/fixtures/session-maxage-session/app/controller/home.js index 1b97026..5680758 100644 --- a/test/fixtures/session-maxage-session/app/controller/home.js +++ b/test/fixtures/session-maxage-session/app/controller/home.js @@ -1,25 +1,23 @@ -'use strict'; - -exports.get = function* (ctx) { +exports.get = async function(ctx) { ctx.body = ctx.session; }; -exports.set = function* (ctx) { +exports.set = async function(ctx) { ctx.session = ctx.query; ctx.body = ctx.session; }; -exports.setKey = function* (ctx) { +exports.setKey = async function(ctx) { ctx.session.key = ctx.query.key; ctx.body = ctx.session; }; -exports.remove = function* (ctx) { +exports.remove = async function(ctx) { ctx.session = null; ctx.body = ctx.session; }; -exports.maxAge = function* (ctx) { - ctx.session.maxAge = this.query.maxAge; +exports.maxAge = async function(ctx) { + ctx.session.maxAge = ctx.query.maxAge; ctx.body = ctx.session; }; diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..ff41b73 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "@eggjs/tsconfig", + "compilerOptions": { + "strict": true, + "noImplicitAny": true, + "target": "ES2022", + "module": "NodeNext", + "moduleResolution": "NodeNext" + } +}