mirror of
https://github.com/toeverything/AFFiNE.git
synced 2024-11-27 06:33:32 +03:00
refactor(server): use ava (#4120)
This commit is contained in:
parent
8845bb9b4b
commit
eb1a21265f
@ -11,11 +11,8 @@
|
||||
"build": "tsc",
|
||||
"start": "node --loader ts-node/esm.mjs --es-module-specifier-resolution node ./src/index.ts",
|
||||
"dev": "nodemon ./src/index.ts",
|
||||
"test": "yarn exec ts-node-esm ./scripts/run-test.ts all",
|
||||
"test:select": "yarn exec ts-node-esm ./scripts/run-test.ts",
|
||||
"test:watch": "yarn exec ts-node-esm ./scripts/run-test.ts all --watch",
|
||||
"test:select:watch": "yarn exec ts-node-esm ./scripts/run-test.ts --watch",
|
||||
"test:coverage": "c8 yarn exec ts-node-esm ./scripts/run-test.ts all",
|
||||
"test": "ava --concurrency 1 --serial",
|
||||
"test:coverage": "c8 ava --concurrency 1 --serial",
|
||||
"postinstall": "prisma generate"
|
||||
},
|
||||
"dependencies": {
|
||||
@ -90,6 +87,7 @@
|
||||
"@types/sinon": "^10.0.16",
|
||||
"@types/supertest": "^2.0.12",
|
||||
"@types/ws": "^8.5.5",
|
||||
"ava": "^5.3.1",
|
||||
"c8": "^8.0.1",
|
||||
"nodemon": "^3.0.1",
|
||||
"sinon": "^15.2.0",
|
||||
@ -97,6 +95,27 @@
|
||||
"ts-node": "^10.9.1",
|
||||
"typescript": "^5.2.2"
|
||||
},
|
||||
"ava": {
|
||||
"extensions": {
|
||||
"ts": "module"
|
||||
},
|
||||
"nodeArguments": [
|
||||
"--loader",
|
||||
"ts-node/esm.mjs",
|
||||
"--es-module-specifier-resolution",
|
||||
"node"
|
||||
],
|
||||
"files": [
|
||||
"src/**/*.spec.ts"
|
||||
],
|
||||
"require": [
|
||||
"./src/prelude.ts"
|
||||
],
|
||||
"environmentVariables": {
|
||||
"TS_NODE_PROJECT": "./tsconfig.json",
|
||||
"NODE_ENV": "test"
|
||||
}
|
||||
},
|
||||
"nodemonConfig": {
|
||||
"exec": "node",
|
||||
"script": "./src/index.ts",
|
||||
|
@ -1,73 +0,0 @@
|
||||
#!/usr/bin/env ts-node-esm
|
||||
import { resolve } from 'node:path';
|
||||
|
||||
import * as p from '@clack/prompts';
|
||||
import { spawn, spawnSync } from 'child_process';
|
||||
import { readdir } from 'fs/promises';
|
||||
import * as process from 'process';
|
||||
import { fileURLToPath } from 'url';
|
||||
|
||||
import pkg from '../package.json' assert { type: 'json' };
|
||||
|
||||
const root = fileURLToPath(new URL('..', import.meta.url));
|
||||
const testDir = resolve(root, 'src', 'tests');
|
||||
const files = await readdir(testDir);
|
||||
|
||||
const watchMode = process.argv.includes('--watch');
|
||||
|
||||
const sharedArgs = [
|
||||
...pkg.nodemonConfig.nodeArgs,
|
||||
'--test',
|
||||
watchMode ? '--watch' : '',
|
||||
];
|
||||
|
||||
const env = {
|
||||
PATH: process.env.PATH,
|
||||
NODE_ENV: 'test',
|
||||
DATABASE_URL: process.env.DATABASE_URL,
|
||||
NODE_NO_WARNINGS: '1',
|
||||
};
|
||||
|
||||
if (process.argv[2] === 'all') {
|
||||
files.forEach(file => {
|
||||
const path = resolve(testDir, file);
|
||||
spawnSync('node', [...sharedArgs, path], {
|
||||
cwd: root,
|
||||
env,
|
||||
stdio: 'inherit',
|
||||
shell: true,
|
||||
});
|
||||
});
|
||||
} else {
|
||||
const result = await p.group({
|
||||
file: () =>
|
||||
p.select({
|
||||
message: 'Select a file to run',
|
||||
options: files.map(file => ({
|
||||
label: file,
|
||||
value: file as any,
|
||||
})),
|
||||
}),
|
||||
});
|
||||
|
||||
const target = resolve(testDir, result.file);
|
||||
|
||||
const cp = spawn(
|
||||
'node',
|
||||
[
|
||||
...sharedArgs,
|
||||
'--test-reporter=spec',
|
||||
'--test-reporter-destination=stdout',
|
||||
target,
|
||||
],
|
||||
{
|
||||
cwd: root,
|
||||
env,
|
||||
stdio: 'inherit',
|
||||
shell: true,
|
||||
}
|
||||
);
|
||||
cp.on('exit', code => {
|
||||
process.exit(code ?? 0);
|
||||
});
|
||||
}
|
@ -3,7 +3,7 @@ import { Module } from '@nestjs/common';
|
||||
import { AppController } from './app.controller';
|
||||
import { ConfigModule } from './config';
|
||||
import { MetricsModule } from './metrics';
|
||||
import { BusinessModules, Providers } from './modules';
|
||||
import { BusinessModules } from './modules';
|
||||
import { PrismaModule } from './prisma';
|
||||
import { SessionModule } from './session';
|
||||
import { StorageModule } from './storage';
|
||||
@ -19,7 +19,6 @@ import { RateLimiterModule } from './throttler';
|
||||
RateLimiterModule,
|
||||
...BusinessModules,
|
||||
],
|
||||
providers: Providers,
|
||||
controllers: [AppController],
|
||||
})
|
||||
export class AppModule {}
|
||||
|
@ -24,6 +24,7 @@ import graphqlUploadExpress from 'graphql-upload/graphqlUploadExpress.mjs';
|
||||
|
||||
import { AppModule } from './app';
|
||||
import { Config } from './config';
|
||||
import { ExceptionLogger } from './middleware/exception-logger';
|
||||
import { serverTimingAndCache } from './middleware/timing';
|
||||
import { RedisIoAdapter } from './modules/sync/redis-adapter';
|
||||
|
||||
@ -75,6 +76,7 @@ app.use(
|
||||
})
|
||||
);
|
||||
|
||||
app.useGlobalFilters(new ExceptionLogger());
|
||||
app.use(cookieParser());
|
||||
|
||||
const config = app.get(Config);
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { Global, Module } from '@nestjs/common';
|
||||
|
||||
import { SessionService } from '../../session';
|
||||
import { MAILER, MailService } from './mailer';
|
||||
import { NextAuthController } from './next-auth.controller';
|
||||
import { NextAuthOptionsProvider } from './next-auth-options';
|
||||
@ -10,6 +11,7 @@ import { AuthService } from './service';
|
||||
@Module({
|
||||
providers: [
|
||||
AuthService,
|
||||
SessionService,
|
||||
AuthResolver,
|
||||
NextAuthOptionsProvider,
|
||||
MAILER,
|
||||
|
@ -43,7 +43,7 @@ export class TokenType {
|
||||
export class AuthResolver {
|
||||
constructor(
|
||||
private readonly config: Config,
|
||||
private auth: AuthService,
|
||||
private readonly auth: AuthService,
|
||||
private readonly session: SessionService
|
||||
) {}
|
||||
|
||||
|
@ -1,8 +1,6 @@
|
||||
import { DynamicModule, Provider, Type } from '@nestjs/common';
|
||||
import { APP_FILTER } from '@nestjs/core';
|
||||
import { DynamicModule, Type } from '@nestjs/common';
|
||||
|
||||
import { GqlModule } from '../graphql.module';
|
||||
import { ExceptionLogger } from '../middleware/exception-logger';
|
||||
import { AuthModule } from './auth';
|
||||
import { DocModule } from './doc';
|
||||
import { SyncModule } from './sync';
|
||||
@ -39,11 +37,4 @@ switch (SERVER_FLAVOR) {
|
||||
break;
|
||||
}
|
||||
|
||||
const Providers: Provider[] = [
|
||||
{
|
||||
provide: APP_FILTER,
|
||||
useClass: ExceptionLogger,
|
||||
},
|
||||
];
|
||||
|
||||
export { BusinessModules, Providers };
|
||||
export { BusinessModules };
|
||||
|
@ -1,11 +1,11 @@
|
||||
import { equal, ok } from 'node:assert';
|
||||
import { afterEach, beforeEach, describe, test } from 'node:test';
|
||||
|
||||
import { Transformer } from '@napi-rs/image';
|
||||
import type { INestApplication } from '@nestjs/common';
|
||||
import { Test } from '@nestjs/testing';
|
||||
import { hash } from '@node-rs/argon2';
|
||||
import { PrismaClient } from '@prisma/client';
|
||||
import test from 'ava';
|
||||
import { Express } from 'express';
|
||||
// @ts-expect-error graphql-upload is not typed
|
||||
import graphqlUploadExpress from 'graphql-upload/graphqlUploadExpress.mjs';
|
||||
@ -15,83 +15,82 @@ import { AppModule } from '../app';
|
||||
|
||||
const gql = '/graphql';
|
||||
|
||||
describe('AppModule', async () => {
|
||||
let app: INestApplication;
|
||||
let app: INestApplication;
|
||||
|
||||
// cleanup database before each test
|
||||
beforeEach(async () => {
|
||||
const client = new PrismaClient();
|
||||
await client.$connect();
|
||||
await client.user.deleteMany({});
|
||||
await client.user.create({
|
||||
data: {
|
||||
name: 'Alex Yang',
|
||||
email: 'alex.yang@example.org',
|
||||
password: await hash('123456'),
|
||||
},
|
||||
});
|
||||
await client.$disconnect();
|
||||
// cleanup database before each test
|
||||
test.beforeEach(async () => {
|
||||
const client = new PrismaClient();
|
||||
await client.$connect();
|
||||
await client.user.deleteMany({});
|
||||
await client.user.create({
|
||||
data: {
|
||||
name: 'Alex Yang',
|
||||
email: 'alex.yang@example.org',
|
||||
password: await hash('123456'),
|
||||
},
|
||||
});
|
||||
await client.$disconnect();
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
const module = await Test.createTestingModule({
|
||||
imports: [AppModule],
|
||||
}).compile();
|
||||
app = module.createNestApplication({
|
||||
cors: true,
|
||||
bodyParser: true,
|
||||
});
|
||||
app.use(
|
||||
graphqlUploadExpress({
|
||||
maxFileSize: 10 * 1024 * 1024,
|
||||
maxFiles: 5,
|
||||
})
|
||||
);
|
||||
await app.init();
|
||||
test.beforeEach(async () => {
|
||||
const module = await Test.createTestingModule({
|
||||
imports: [AppModule],
|
||||
}).compile();
|
||||
app = module.createNestApplication({
|
||||
cors: true,
|
||||
bodyParser: true,
|
||||
});
|
||||
app.use(
|
||||
graphqlUploadExpress({
|
||||
maxFileSize: 10 * 1024 * 1024,
|
||||
maxFiles: 5,
|
||||
})
|
||||
);
|
||||
await app.init();
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await app.close();
|
||||
});
|
||||
test.afterEach(async () => {
|
||||
await app.close();
|
||||
});
|
||||
|
||||
await test('should init app', async () => {
|
||||
ok(typeof app === 'object');
|
||||
await request(app.getHttpServer())
|
||||
.post(gql)
|
||||
.send({
|
||||
query: `
|
||||
test('should init app', async () => {
|
||||
ok(typeof app === 'object');
|
||||
await request(app.getHttpServer())
|
||||
.post(gql)
|
||||
.send({
|
||||
query: `
|
||||
query {
|
||||
error
|
||||
}
|
||||
`,
|
||||
})
|
||||
.expect(400);
|
||||
})
|
||||
.expect(400);
|
||||
|
||||
const { token } = await createToken(app);
|
||||
const { token } = await createToken(app);
|
||||
|
||||
await request(app.getHttpServer())
|
||||
.post(gql)
|
||||
.auth(token, { type: 'bearer' })
|
||||
.send({
|
||||
query: `
|
||||
await request(app.getHttpServer())
|
||||
.post(gql)
|
||||
.auth(token, { type: 'bearer' })
|
||||
.send({
|
||||
query: `
|
||||
query {
|
||||
__typename
|
||||
}
|
||||
`,
|
||||
})
|
||||
.expect(200)
|
||||
.expect(res => {
|
||||
ok(res.body.data.__typename === 'Query');
|
||||
});
|
||||
});
|
||||
})
|
||||
.expect(200)
|
||||
.expect(res => {
|
||||
ok(res.body.data.__typename === 'Query');
|
||||
});
|
||||
});
|
||||
|
||||
await test('should find default user', async () => {
|
||||
const { token } = await createToken(app);
|
||||
await request(app.getHttpServer())
|
||||
.post(gql)
|
||||
.auth(token, { type: 'bearer' })
|
||||
.send({
|
||||
query: `
|
||||
test('should find default user', async () => {
|
||||
const { token } = await createToken(app);
|
||||
await request(app.getHttpServer())
|
||||
.post(gql)
|
||||
.auth(token, { type: 'bearer' })
|
||||
.send({
|
||||
query: `
|
||||
query {
|
||||
user(email: "alex.yang@example.org") {
|
||||
email
|
||||
@ -99,29 +98,29 @@ describe('AppModule', async () => {
|
||||
}
|
||||
}
|
||||
`,
|
||||
})
|
||||
.expect(200)
|
||||
.expect(res => {
|
||||
equal(res.body.data.user.email, 'alex.yang@example.org');
|
||||
});
|
||||
});
|
||||
})
|
||||
.expect(200)
|
||||
.expect(res => {
|
||||
equal(res.body.data.user.email, 'alex.yang@example.org');
|
||||
});
|
||||
});
|
||||
|
||||
await test('should be able to upload avatar', async () => {
|
||||
const { token, id } = await createToken(app);
|
||||
const png = await Transformer.fromRgbaPixels(
|
||||
Buffer.alloc(400 * 400 * 4).fill(255),
|
||||
400,
|
||||
400
|
||||
).png();
|
||||
test('should be able to upload avatar', async () => {
|
||||
const { token, id } = await createToken(app);
|
||||
const png = await Transformer.fromRgbaPixels(
|
||||
Buffer.alloc(400 * 400 * 4).fill(255),
|
||||
400,
|
||||
400
|
||||
).png();
|
||||
|
||||
await request(app.getHttpServer())
|
||||
.post(gql)
|
||||
.auth(token, { type: 'bearer' })
|
||||
.field(
|
||||
'operations',
|
||||
JSON.stringify({
|
||||
name: 'uploadAvatar',
|
||||
query: `mutation uploadAvatar($id: String!, $avatar: Upload!) {
|
||||
await request(app.getHttpServer())
|
||||
.post(gql)
|
||||
.auth(token, { type: 'bearer' })
|
||||
.field(
|
||||
'operations',
|
||||
JSON.stringify({
|
||||
name: 'uploadAvatar',
|
||||
query: `mutation uploadAvatar($id: String!, $avatar: Upload!) {
|
||||
uploadAvatar(id: $id, avatar: $avatar) {
|
||||
id
|
||||
name
|
||||
@ -130,17 +129,15 @@ describe('AppModule', async () => {
|
||||
}
|
||||
}
|
||||
`,
|
||||
variables: { id, avatar: null },
|
||||
})
|
||||
)
|
||||
|
||||
.field('map', JSON.stringify({ '0': ['variables.avatar'] }))
|
||||
.attach('0', png, 'avatar.png')
|
||||
.expect(200)
|
||||
.expect(res => {
|
||||
equal(res.body.data.uploadAvatar.id, id);
|
||||
});
|
||||
});
|
||||
variables: { id, avatar: null },
|
||||
})
|
||||
)
|
||||
.field('map', JSON.stringify({ '0': ['variables.avatar'] }))
|
||||
.attach('0', png, 'avatar.png')
|
||||
.expect(200)
|
||||
.expect(res => {
|
||||
equal(res.body.data.uploadAvatar.id, id);
|
||||
});
|
||||
});
|
||||
|
||||
async function createToken(app: INestApplication<Express>): Promise<{
|
||||
|
@ -1,9 +1,9 @@
|
||||
/// <reference types="../global.d.ts" />
|
||||
import { equal } from 'node:assert';
|
||||
import { afterEach, beforeEach, test } from 'node:test';
|
||||
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { PrismaClient } from '@prisma/client';
|
||||
import test from 'ava';
|
||||
|
||||
import { ConfigModule } from '../config';
|
||||
import { GqlModule } from '../graphql.module';
|
||||
@ -17,13 +17,13 @@ let auth: AuthService;
|
||||
let module: TestingModule;
|
||||
|
||||
// cleanup database before each test
|
||||
beforeEach(async () => {
|
||||
test.beforeEach(async () => {
|
||||
const client = new PrismaClient();
|
||||
await client.$connect();
|
||||
await client.user.deleteMany({});
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
test.beforeEach(async () => {
|
||||
module = await Test.createTestingModule({
|
||||
imports: [
|
||||
ConfigModule.forRoot({
|
||||
@ -43,16 +43,17 @@ beforeEach(async () => {
|
||||
auth = module.get(AuthService);
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
test.afterEach(async () => {
|
||||
await module.close();
|
||||
});
|
||||
|
||||
test('should be able to register and signIn', async () => {
|
||||
test('should be able to register and signIn', async t => {
|
||||
await auth.signUp('Alex Yang', 'alexyang@example.org', '123456');
|
||||
await auth.signIn('alexyang@example.org', '123456');
|
||||
t.pass();
|
||||
});
|
||||
|
||||
test('should be able to verify', async () => {
|
||||
test('should be able to verify', async t => {
|
||||
await auth.signUp('Alex Yang', 'alexyang@example.org', '123456');
|
||||
await auth.signIn('alexyang@example.org', '123456');
|
||||
const date = new Date();
|
||||
@ -83,4 +84,5 @@ test('should be able to verify', async () => {
|
||||
equal(claim.emailVerified?.toISOString(), date.toISOString());
|
||||
equal(claim.createdAt.toISOString(), date.toISOString());
|
||||
}
|
||||
t.pass();
|
||||
});
|
||||
|
@ -1,24 +1,24 @@
|
||||
import { equal, ok } from 'node:assert';
|
||||
import { beforeEach, test } from 'node:test';
|
||||
import { ok } from 'node:assert';
|
||||
|
||||
import { Test } from '@nestjs/testing';
|
||||
import test from 'ava';
|
||||
|
||||
import { Config, ConfigModule } from '../config';
|
||||
|
||||
let config: Config;
|
||||
beforeEach(async () => {
|
||||
test.beforeEach(async () => {
|
||||
const module = await Test.createTestingModule({
|
||||
imports: [ConfigModule.forRoot()],
|
||||
}).compile();
|
||||
config = module.get(Config);
|
||||
});
|
||||
|
||||
test('should be able to get config', () => {
|
||||
ok(typeof config.host === 'string');
|
||||
equal(config.env, 'test');
|
||||
test('should be able to get config', t => {
|
||||
t.true(typeof config.host === 'string');
|
||||
t.is(config.env, 'test');
|
||||
});
|
||||
|
||||
test('should be able to override config', async () => {
|
||||
test('should be able to override config', async t => {
|
||||
const module = await Test.createTestingModule({
|
||||
imports: [
|
||||
ConfigModule.forRoot({
|
||||
@ -29,4 +29,5 @@ test('should be able to override config', async () => {
|
||||
const config = module.get(Config);
|
||||
|
||||
ok(config.host, 'testing');
|
||||
t.pass();
|
||||
});
|
||||
|
@ -1,8 +1,9 @@
|
||||
import { deepEqual, equal, ok } from 'node:assert';
|
||||
import { afterEach, beforeEach, mock, test } from 'node:test';
|
||||
import { mock } from 'node:test';
|
||||
|
||||
import { INestApplication } from '@nestjs/common';
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import test from 'ava';
|
||||
import { register } from 'prom-client';
|
||||
import * as Sinon from 'sinon';
|
||||
import { Doc as YDoc, encodeStateAsUpdate } from 'yjs';
|
||||
@ -24,135 +25,137 @@ const createModule = () => {
|
||||
}).compile();
|
||||
};
|
||||
|
||||
test('Doc Module', async t => {
|
||||
let app: INestApplication;
|
||||
let m: TestingModule;
|
||||
let timer: Sinon.SinonFakeTimers;
|
||||
let app: INestApplication;
|
||||
let m: TestingModule;
|
||||
let timer: Sinon.SinonFakeTimers;
|
||||
|
||||
// cleanup database before each test
|
||||
beforeEach(async () => {
|
||||
timer = Sinon.useFakeTimers({
|
||||
toFake: ['setInterval'],
|
||||
});
|
||||
await flushDB();
|
||||
m = await createModule();
|
||||
app = m.createNestApplication();
|
||||
app.enableShutdownHooks();
|
||||
await app.init();
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await app.close();
|
||||
timer.restore();
|
||||
});
|
||||
|
||||
await t.test('should setup update poll interval', async () => {
|
||||
register.clear();
|
||||
const m = await createModule();
|
||||
const manager = m.get(DocManager);
|
||||
const fake = mock.method(manager, 'setup');
|
||||
|
||||
await m.createNestApplication().init();
|
||||
|
||||
equal(fake.mock.callCount(), 1);
|
||||
// @ts-expect-error private member
|
||||
ok(manager.job);
|
||||
});
|
||||
|
||||
await t.test('should be able to stop poll', async () => {
|
||||
const manager = m.get(DocManager);
|
||||
const fake = mock.method(manager, 'destroy');
|
||||
|
||||
await app.close();
|
||||
|
||||
equal(fake.mock.callCount(), 1);
|
||||
// @ts-expect-error private member
|
||||
equal(manager.job, null);
|
||||
});
|
||||
|
||||
await t.test('should poll when intervel due', async () => {
|
||||
const manager = m.get(DocManager);
|
||||
const interval = m.get(Config).doc.manager.updatePollInterval;
|
||||
|
||||
let resolve: any;
|
||||
const fake = mock.method(manager, 'apply', () => {
|
||||
return new Promise(_resolve => {
|
||||
resolve = _resolve;
|
||||
});
|
||||
});
|
||||
|
||||
timer.tick(interval);
|
||||
equal(fake.mock.callCount(), 1);
|
||||
|
||||
// busy
|
||||
timer.tick(interval);
|
||||
// @ts-expect-error private member
|
||||
equal(manager.busy, true);
|
||||
equal(fake.mock.callCount(), 1);
|
||||
|
||||
resolve();
|
||||
await timer.tickAsync(1);
|
||||
|
||||
// @ts-expect-error private member
|
||||
equal(manager.busy, false);
|
||||
timer.tick(interval);
|
||||
equal(fake.mock.callCount(), 2);
|
||||
});
|
||||
|
||||
await t.test('should merge update when intervel due', async () => {
|
||||
const db = m.get(PrismaService);
|
||||
const manager = m.get(DocManager);
|
||||
|
||||
const doc = new YDoc();
|
||||
const text = doc.getText('content');
|
||||
text.insert(0, 'hello');
|
||||
const update = encodeStateAsUpdate(doc);
|
||||
|
||||
const ws = await db.workspace.create({
|
||||
data: {
|
||||
id: '1',
|
||||
public: false,
|
||||
},
|
||||
});
|
||||
|
||||
await db.update.createMany({
|
||||
data: [
|
||||
{
|
||||
id: '1',
|
||||
workspaceId: '1',
|
||||
blob: Buffer.from([0, 0]),
|
||||
},
|
||||
{
|
||||
id: '1',
|
||||
workspaceId: '1',
|
||||
blob: Buffer.from(update),
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
await manager.apply();
|
||||
|
||||
deepEqual(await manager.getLatestUpdate(ws.id, '1'), update);
|
||||
|
||||
let appendUpdate = Buffer.from([]);
|
||||
doc.on('update', update => {
|
||||
appendUpdate = Buffer.from(update);
|
||||
});
|
||||
text.insert(5, 'world');
|
||||
|
||||
await db.update.create({
|
||||
data: {
|
||||
workspaceId: ws.id,
|
||||
id: '1',
|
||||
blob: appendUpdate,
|
||||
},
|
||||
});
|
||||
|
||||
await manager.apply();
|
||||
|
||||
deepEqual(
|
||||
await manager.getLatestUpdate(ws.id, '1'),
|
||||
encodeStateAsUpdate(doc)
|
||||
);
|
||||
// cleanup database before each test
|
||||
test.beforeEach(async () => {
|
||||
timer = Sinon.useFakeTimers({
|
||||
toFake: ['setInterval'],
|
||||
});
|
||||
await flushDB();
|
||||
m = await createModule();
|
||||
app = m.createNestApplication();
|
||||
app.enableShutdownHooks();
|
||||
await app.init();
|
||||
});
|
||||
|
||||
test.afterEach(async () => {
|
||||
await app.close();
|
||||
timer.restore();
|
||||
});
|
||||
|
||||
test('should setup update poll interval', async t => {
|
||||
register.clear();
|
||||
const m = await createModule();
|
||||
const manager = m.get(DocManager);
|
||||
const fake = mock.method(manager, 'setup');
|
||||
|
||||
await m.createNestApplication().init();
|
||||
|
||||
equal(fake.mock.callCount(), 1);
|
||||
// @ts-expect-error private member
|
||||
ok(manager.job);
|
||||
t.pass();
|
||||
});
|
||||
|
||||
test('should be able to stop poll', async t => {
|
||||
const manager = m.get(DocManager);
|
||||
const fake = mock.method(manager, 'destroy');
|
||||
|
||||
await app.close();
|
||||
|
||||
equal(fake.mock.callCount(), 1);
|
||||
// @ts-expect-error private member
|
||||
equal(manager.job, null);
|
||||
t.pass();
|
||||
});
|
||||
|
||||
test('should poll when intervel due', async t => {
|
||||
const manager = m.get(DocManager);
|
||||
const interval = m.get(Config).doc.manager.updatePollInterval;
|
||||
|
||||
let resolve: any;
|
||||
const fake = mock.method(manager, 'apply', () => {
|
||||
return new Promise(_resolve => {
|
||||
resolve = _resolve;
|
||||
});
|
||||
});
|
||||
|
||||
timer.tick(interval);
|
||||
equal(fake.mock.callCount(), 1);
|
||||
|
||||
// busy
|
||||
timer.tick(interval);
|
||||
// @ts-expect-error private member
|
||||
equal(manager.busy, true);
|
||||
equal(fake.mock.callCount(), 1);
|
||||
|
||||
resolve();
|
||||
await timer.tickAsync(1);
|
||||
|
||||
// @ts-expect-error private member
|
||||
equal(manager.busy, false);
|
||||
timer.tick(interval);
|
||||
equal(fake.mock.callCount(), 2);
|
||||
t.pass();
|
||||
});
|
||||
|
||||
test('should merge update when intervel due', async t => {
|
||||
const db = m.get(PrismaService);
|
||||
const manager = m.get(DocManager);
|
||||
|
||||
const doc = new YDoc();
|
||||
const text = doc.getText('content');
|
||||
text.insert(0, 'hello');
|
||||
const update = encodeStateAsUpdate(doc);
|
||||
|
||||
const ws = await db.workspace.create({
|
||||
data: {
|
||||
id: '1',
|
||||
public: false,
|
||||
},
|
||||
});
|
||||
|
||||
await db.update.createMany({
|
||||
data: [
|
||||
{
|
||||
id: '1',
|
||||
workspaceId: '1',
|
||||
blob: Buffer.from([0, 0]),
|
||||
},
|
||||
{
|
||||
id: '1',
|
||||
workspaceId: '1',
|
||||
blob: Buffer.from(update),
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
await manager.apply();
|
||||
|
||||
deepEqual(await manager.getLatestUpdate(ws.id, '1'), update);
|
||||
|
||||
let appendUpdate = Buffer.from([]);
|
||||
doc.on('update', update => {
|
||||
appendUpdate = Buffer.from(update);
|
||||
});
|
||||
text.insert(5, 'world');
|
||||
|
||||
await db.update.create({
|
||||
data: {
|
||||
workspaceId: ws.id,
|
||||
id: '1',
|
||||
blob: appendUpdate,
|
||||
},
|
||||
});
|
||||
|
||||
await manager.apply();
|
||||
|
||||
deepEqual(
|
||||
await manager.getLatestUpdate(ws.id, '1'),
|
||||
encodeStateAsUpdate(doc)
|
||||
);
|
||||
t.pass();
|
||||
});
|
||||
|
@ -1,9 +1,9 @@
|
||||
import { ok } from 'node:assert';
|
||||
import { afterEach, beforeEach, describe, it } from 'node:test';
|
||||
|
||||
import type { INestApplication } from '@nestjs/common';
|
||||
import { Test } from '@nestjs/testing';
|
||||
import { PrismaClient } from '@prisma/client';
|
||||
import test from 'ava';
|
||||
// @ts-expect-error graphql-upload is not typed
|
||||
import graphqlUploadExpress from 'graphql-upload/graphqlUploadExpress.mjs';
|
||||
|
||||
@ -11,76 +11,75 @@ import { AppModule } from '../app';
|
||||
import { MailService } from '../modules/auth/mailer';
|
||||
import { createWorkspace, getInviteInfo, inviteUser, signUp } from './utils';
|
||||
|
||||
describe('Mail Module', () => {
|
||||
let app: INestApplication;
|
||||
let app: INestApplication;
|
||||
|
||||
const client = new PrismaClient();
|
||||
const client = new PrismaClient();
|
||||
|
||||
let mail: MailService;
|
||||
let mail: MailService;
|
||||
|
||||
// cleanup database before each test
|
||||
beforeEach(async () => {
|
||||
await client.$connect();
|
||||
await client.user.deleteMany({});
|
||||
await client.snapshot.deleteMany({});
|
||||
await client.update.deleteMany({});
|
||||
await client.workspace.deleteMany({});
|
||||
await client.$disconnect();
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
const module = await Test.createTestingModule({
|
||||
imports: [AppModule],
|
||||
}).compile();
|
||||
app = module.createNestApplication();
|
||||
app.use(
|
||||
graphqlUploadExpress({
|
||||
maxFileSize: 10 * 1024 * 1024,
|
||||
maxFiles: 5,
|
||||
})
|
||||
);
|
||||
await app.init();
|
||||
|
||||
mail = module.get(MailService);
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await app.close();
|
||||
});
|
||||
|
||||
it('should send invite email', async () => {
|
||||
if (mail.hasConfigured()) {
|
||||
const u1 = await signUp(app, 'u1', 'u1@affine.pro', '1');
|
||||
const u2 = await signUp(app, 'u2', 'u2@affine.pro', '1');
|
||||
|
||||
const workspace = await createWorkspace(app, u1.token.token);
|
||||
const inviteId = await inviteUser(
|
||||
app,
|
||||
u1.token.token,
|
||||
workspace.id,
|
||||
u2.email,
|
||||
'Admin'
|
||||
);
|
||||
|
||||
const inviteInfo = await getInviteInfo(app, u1.token.token, inviteId);
|
||||
|
||||
const resp = await mail.sendInviteEmail(
|
||||
'production@toeverything.info',
|
||||
inviteId,
|
||||
{
|
||||
workspace: {
|
||||
id: inviteInfo.workspace.id,
|
||||
name: inviteInfo.workspace.name,
|
||||
avatar: '',
|
||||
},
|
||||
user: {
|
||||
avatar: inviteInfo.user?.avatarUrl || '',
|
||||
name: inviteInfo.user?.name || '',
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
ok(resp.accepted.length === 1, 'failed to send invite email');
|
||||
}
|
||||
});
|
||||
// cleanup database before each test
|
||||
test.beforeEach(async () => {
|
||||
await client.$connect();
|
||||
await client.user.deleteMany({});
|
||||
await client.snapshot.deleteMany({});
|
||||
await client.update.deleteMany({});
|
||||
await client.workspace.deleteMany({});
|
||||
await client.$disconnect();
|
||||
});
|
||||
|
||||
test.beforeEach(async () => {
|
||||
const module = await Test.createTestingModule({
|
||||
imports: [AppModule],
|
||||
}).compile();
|
||||
app = module.createNestApplication();
|
||||
app.use(
|
||||
graphqlUploadExpress({
|
||||
maxFileSize: 10 * 1024 * 1024,
|
||||
maxFiles: 5,
|
||||
})
|
||||
);
|
||||
await app.init();
|
||||
|
||||
mail = module.get(MailService);
|
||||
});
|
||||
|
||||
test.afterEach(async () => {
|
||||
await app.close();
|
||||
});
|
||||
|
||||
test('should send invite email', async t => {
|
||||
if (mail.hasConfigured()) {
|
||||
const u1 = await signUp(app, 'u1', 'u1@affine.pro', '1');
|
||||
const u2 = await signUp(app, 'u2', 'u2@affine.pro', '1');
|
||||
|
||||
const workspace = await createWorkspace(app, u1.token.token);
|
||||
const inviteId = await inviteUser(
|
||||
app,
|
||||
u1.token.token,
|
||||
workspace.id,
|
||||
u2.email,
|
||||
'Admin'
|
||||
);
|
||||
|
||||
const inviteInfo = await getInviteInfo(app, u1.token.token, inviteId);
|
||||
|
||||
const resp = await mail.sendInviteEmail(
|
||||
'production@toeverything.info',
|
||||
inviteId,
|
||||
{
|
||||
workspace: {
|
||||
id: inviteInfo.workspace.id,
|
||||
name: inviteInfo.workspace.name,
|
||||
avatar: '',
|
||||
},
|
||||
user: {
|
||||
avatar: inviteInfo.user?.avatarUrl || '',
|
||||
name: inviteInfo.user?.name || '',
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
ok(resp.accepted.length === 1, 'failed to send invite email');
|
||||
}
|
||||
t.pass();
|
||||
});
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { ok } from 'node:assert';
|
||||
import { afterEach, beforeEach, test } from 'node:test';
|
||||
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import test from 'ava';
|
||||
import { register } from 'prom-client';
|
||||
|
||||
import { MetricsModule } from '../metrics';
|
||||
@ -11,7 +11,7 @@ import { PrismaModule } from '../prisma';
|
||||
let metrics: Metrics;
|
||||
let module: TestingModule;
|
||||
|
||||
beforeEach(async () => {
|
||||
test.beforeEach(async () => {
|
||||
module = await Test.createTestingModule({
|
||||
imports: [MetricsModule, PrismaModule],
|
||||
}).compile();
|
||||
@ -19,11 +19,11 @@ beforeEach(async () => {
|
||||
metrics = module.get(Metrics);
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
test.afterEach(async () => {
|
||||
await module.close();
|
||||
});
|
||||
|
||||
test('should be able to increment counter', async () => {
|
||||
test('should be able to increment counter', async t => {
|
||||
metrics.socketIOEventCounter(1, { event: 'client-handshake' });
|
||||
const socketIOCounterMetric =
|
||||
await register.getSingleMetric('socket_io_counter');
|
||||
@ -33,9 +33,10 @@ test('should be able to increment counter', async () => {
|
||||
JSON.stringify((await socketIOCounterMetric.get()).values) ===
|
||||
'[{"value":1,"labels":{"event":"client-handshake"}}]'
|
||||
);
|
||||
t.pass();
|
||||
});
|
||||
|
||||
test('should be able to timer', async () => {
|
||||
test('should be able to timer', async t => {
|
||||
let minimum: number;
|
||||
{
|
||||
const endTimer = metrics.socketIOEventTimer({ event: 'client-handshake' });
|
||||
@ -76,4 +77,5 @@ test('should be able to timer', async () => {
|
||||
);
|
||||
}
|
||||
}
|
||||
t.pass();
|
||||
});
|
||||
|
@ -1,9 +1,9 @@
|
||||
/// <reference types="../global.d.ts" />
|
||||
import { equal } from 'node:assert';
|
||||
import { afterEach, beforeEach, test } from 'node:test';
|
||||
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { PrismaClient } from '@prisma/client';
|
||||
import test from 'ava';
|
||||
|
||||
import { ConfigModule } from '../config';
|
||||
import { SessionModule, SessionService } from '../session';
|
||||
@ -12,31 +12,33 @@ let session: SessionService;
|
||||
let module: TestingModule;
|
||||
|
||||
// cleanup database before each test
|
||||
beforeEach(async () => {
|
||||
test.beforeEach(async () => {
|
||||
const client = new PrismaClient();
|
||||
await client.$connect();
|
||||
await client.user.deleteMany({});
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
test.beforeEach(async () => {
|
||||
module = await Test.createTestingModule({
|
||||
imports: [ConfigModule.forRoot(), SessionModule],
|
||||
}).compile();
|
||||
session = module.get(SessionService);
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
test.afterEach(async () => {
|
||||
await module.close();
|
||||
});
|
||||
|
||||
test('should be able to set session', async () => {
|
||||
test('should be able to set session', async t => {
|
||||
await session.set('test', 'value');
|
||||
equal(await session.get('test'), 'value');
|
||||
t.pass();
|
||||
});
|
||||
|
||||
test('should be expired by ttl', async () => {
|
||||
test('should be expired by ttl', async t => {
|
||||
await session.set('test', 'value', 100);
|
||||
equal(await session.get('test'), 'value');
|
||||
await new Promise(resolve => setTimeout(resolve, 500));
|
||||
equal(await session.get('test'), undefined);
|
||||
t.pass();
|
||||
});
|
||||
|
@ -1,9 +1,9 @@
|
||||
import { ok, rejects } from 'node:assert';
|
||||
import { afterEach, beforeEach, describe, it } from 'node:test';
|
||||
|
||||
import type { INestApplication } from '@nestjs/common';
|
||||
import { Test } from '@nestjs/testing';
|
||||
import { PrismaClient } from '@prisma/client';
|
||||
import test from 'ava';
|
||||
// @ts-expect-error graphql-upload is not typed
|
||||
import graphqlUploadExpress from 'graphql-upload/graphqlUploadExpress.mjs';
|
||||
import request from 'supertest';
|
||||
@ -11,66 +11,67 @@ import request from 'supertest';
|
||||
import { AppModule } from '../app';
|
||||
import { currentUser, signUp } from './utils';
|
||||
|
||||
describe('User Module', () => {
|
||||
let app: INestApplication;
|
||||
let app: INestApplication;
|
||||
|
||||
// cleanup database before each test
|
||||
beforeEach(async () => {
|
||||
const client = new PrismaClient();
|
||||
await client.$connect();
|
||||
await client.user.deleteMany({});
|
||||
await client.$disconnect();
|
||||
});
|
||||
// cleanup database before each test
|
||||
test.beforeEach(async () => {
|
||||
const client = new PrismaClient();
|
||||
await client.$connect();
|
||||
await client.user.deleteMany({});
|
||||
await client.$disconnect();
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
const module = await Test.createTestingModule({
|
||||
imports: [AppModule],
|
||||
}).compile();
|
||||
app = module.createNestApplication();
|
||||
app.use(
|
||||
graphqlUploadExpress({
|
||||
maxFileSize: 10 * 1024 * 1024,
|
||||
maxFiles: 5,
|
||||
})
|
||||
);
|
||||
await app.init();
|
||||
});
|
||||
test.beforeEach(async () => {
|
||||
const module = await Test.createTestingModule({
|
||||
imports: [AppModule],
|
||||
}).compile();
|
||||
app = module.createNestApplication();
|
||||
app.use(
|
||||
graphqlUploadExpress({
|
||||
maxFileSize: 10 * 1024 * 1024,
|
||||
maxFiles: 5,
|
||||
})
|
||||
);
|
||||
await app.init();
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await app.close();
|
||||
});
|
||||
test.afterEach(async () => {
|
||||
await app.close();
|
||||
});
|
||||
|
||||
it('should register a user', async () => {
|
||||
const user = await signUp(app, 'u1', 'u1@affine.pro', '123456');
|
||||
ok(typeof user.id === 'string', 'user.id is not a string');
|
||||
ok(user.name === 'u1', 'user.name is not valid');
|
||||
ok(user.email === 'u1@affine.pro', 'user.email is not valid');
|
||||
});
|
||||
test('should register a user', async t => {
|
||||
const user = await signUp(app, 'u1', 'u1@affine.pro', '123456');
|
||||
ok(typeof user.id === 'string', 'user.id is not a string');
|
||||
ok(user.name === 'u1', 'user.name is not valid');
|
||||
ok(user.email === 'u1@affine.pro', 'user.email is not valid');
|
||||
t.pass();
|
||||
});
|
||||
|
||||
it('should get current user', async () => {
|
||||
const user = await signUp(app, 'u1', 'u1@affine.pro', '123456');
|
||||
const currUser = await currentUser(app, user.token.token);
|
||||
ok(currUser.id === user.id, 'user.id is not valid');
|
||||
ok(currUser.name === user.name, 'user.name is not valid');
|
||||
ok(currUser.email === user.email, 'user.email is not valid');
|
||||
ok(currUser.hasPassword, 'currUser.hasPassword is not valid');
|
||||
});
|
||||
test('should get current user', async t => {
|
||||
const user = await signUp(app, 'u1', 'u1@affine.pro', '123456');
|
||||
const currUser = await currentUser(app, user.token.token);
|
||||
ok(currUser.id === user.id, 'user.id is not valid');
|
||||
ok(currUser.name === user.name, 'user.name is not valid');
|
||||
ok(currUser.email === user.email, 'user.email is not valid');
|
||||
ok(currUser.hasPassword, 'currUser.hasPassword is not valid');
|
||||
t.pass();
|
||||
});
|
||||
|
||||
it('should be able to delete user', async () => {
|
||||
const user = await signUp(app, 'u1', 'u1@affine.pro', '123456');
|
||||
await request(app.getHttpServer())
|
||||
.post('/graphql')
|
||||
.auth(user.token.token, { type: 'bearer' })
|
||||
.send({
|
||||
query: `
|
||||
test('should be able to delete user', async t => {
|
||||
const user = await signUp(app, 'u1', 'u1@affine.pro', '123456');
|
||||
await request(app.getHttpServer())
|
||||
.post('/graphql')
|
||||
.auth(user.token.token, { type: 'bearer' })
|
||||
.send({
|
||||
query: `
|
||||
mutation {
|
||||
deleteAccount {
|
||||
success
|
||||
}
|
||||
}
|
||||
`,
|
||||
})
|
||||
.expect(200);
|
||||
rejects(currentUser(app, user.token.token));
|
||||
});
|
||||
})
|
||||
.expect(200);
|
||||
await rejects(currentUser(app, user.token.token));
|
||||
t.pass();
|
||||
});
|
||||
|
@ -1,9 +1,9 @@
|
||||
import { deepEqual, ok } from 'node:assert';
|
||||
import { afterEach, beforeEach, describe, it } from 'node:test';
|
||||
|
||||
import type { INestApplication } from '@nestjs/common';
|
||||
import { Test } from '@nestjs/testing';
|
||||
import { PrismaClient } from '@prisma/client';
|
||||
import test from 'ava';
|
||||
// @ts-expect-error graphql-upload is not typed
|
||||
import graphqlUploadExpress from 'graphql-upload/graphqlUploadExpress.mjs';
|
||||
import request from 'supertest';
|
||||
@ -18,116 +18,118 @@ import {
|
||||
signUp,
|
||||
} from './utils';
|
||||
|
||||
describe('Workspace Module - Blobs', () => {
|
||||
let app: INestApplication;
|
||||
let app: INestApplication;
|
||||
|
||||
const client = new PrismaClient();
|
||||
const client = new PrismaClient();
|
||||
|
||||
// cleanup database before each test
|
||||
beforeEach(async () => {
|
||||
await client.$connect();
|
||||
await client.user.deleteMany({});
|
||||
await client.snapshot.deleteMany({});
|
||||
await client.update.deleteMany({});
|
||||
await client.workspace.deleteMany({});
|
||||
await client.$disconnect();
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
const module = await Test.createTestingModule({
|
||||
imports: [AppModule],
|
||||
}).compile();
|
||||
app = module.createNestApplication();
|
||||
app.use(
|
||||
graphqlUploadExpress({
|
||||
maxFileSize: 10 * 1024 * 1024,
|
||||
maxFiles: 5,
|
||||
})
|
||||
);
|
||||
await app.init();
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await app.close();
|
||||
});
|
||||
|
||||
it('should set blobs', async () => {
|
||||
const u1 = await signUp(app, 'u1', 'u1@affine.pro', '1');
|
||||
|
||||
const workspace = await createWorkspace(app, u1.token.token);
|
||||
|
||||
const buffer1 = Buffer.from([0, 0]);
|
||||
const hash1 = await setBlob(app, u1.token.token, workspace.id, buffer1);
|
||||
const buffer2 = Buffer.from([0, 1]);
|
||||
const hash2 = await setBlob(app, u1.token.token, workspace.id, buffer2);
|
||||
|
||||
const server = app.getHttpServer();
|
||||
|
||||
const response1 = await request(server)
|
||||
.get(`/api/workspaces/${workspace.id}/blobs/${hash1}`)
|
||||
.auth(u1.token.token, { type: 'bearer' })
|
||||
.buffer();
|
||||
|
||||
deepEqual(response1.body, buffer1, 'failed to get blob');
|
||||
|
||||
const response2 = await request(server)
|
||||
.get(`/api/workspaces/${workspace.id}/blobs/${hash2}`)
|
||||
.auth(u1.token.token, { type: 'bearer' })
|
||||
.buffer();
|
||||
|
||||
deepEqual(response2.body, buffer2, 'failed to get blob');
|
||||
});
|
||||
|
||||
it('should list blobs', async () => {
|
||||
const u1 = await signUp(app, 'u1', 'u1@affine.pro', '1');
|
||||
|
||||
const workspace = await createWorkspace(app, u1.token.token);
|
||||
const blobs = await listBlobs(app, u1.token.token, workspace.id);
|
||||
ok(blobs.length === 0, 'failed to list blobs');
|
||||
|
||||
const buffer1 = Buffer.from([0, 0]);
|
||||
const hash1 = await setBlob(app, u1.token.token, workspace.id, buffer1);
|
||||
const buffer2 = Buffer.from([0, 1]);
|
||||
const hash2 = await setBlob(app, u1.token.token, workspace.id, buffer2);
|
||||
|
||||
const ret = await listBlobs(app, u1.token.token, workspace.id);
|
||||
ok(ret.length === 2, 'failed to list blobs');
|
||||
ok(ret[0] === hash1, 'failed to list blobs');
|
||||
ok(ret[1] === hash2, 'failed to list blobs');
|
||||
});
|
||||
|
||||
it('should calc blobs size', async () => {
|
||||
const u1 = await signUp(app, 'u1', 'u1@affine.pro', '1');
|
||||
|
||||
const workspace = await createWorkspace(app, u1.token.token);
|
||||
|
||||
const buffer1 = Buffer.from([0, 0]);
|
||||
await setBlob(app, u1.token.token, workspace.id, buffer1);
|
||||
const buffer2 = Buffer.from([0, 1]);
|
||||
await setBlob(app, u1.token.token, workspace.id, buffer2);
|
||||
|
||||
const size = await collectBlobSizes(app, u1.token.token, workspace.id);
|
||||
ok(size === 4, 'failed to collect blob sizes');
|
||||
});
|
||||
|
||||
it('should calc all blobs size', async () => {
|
||||
const u1 = await signUp(app, 'u1', 'u1@affine.pro', '1');
|
||||
|
||||
const workspace1 = await createWorkspace(app, u1.token.token);
|
||||
|
||||
const buffer1 = Buffer.from([0, 0]);
|
||||
await setBlob(app, u1.token.token, workspace1.id, buffer1);
|
||||
const buffer2 = Buffer.from([0, 1]);
|
||||
await setBlob(app, u1.token.token, workspace1.id, buffer2);
|
||||
|
||||
const workspace2 = await createWorkspace(app, u1.token.token);
|
||||
|
||||
const buffer3 = Buffer.from([0, 0]);
|
||||
await setBlob(app, u1.token.token, workspace2.id, buffer3);
|
||||
const buffer4 = Buffer.from([0, 1]);
|
||||
await setBlob(app, u1.token.token, workspace2.id, buffer4);
|
||||
|
||||
const size = await collectAllBlobSizes(app, u1.token.token);
|
||||
ok(size === 8, 'failed to collect all blob sizes');
|
||||
});
|
||||
// cleanup database before each test
|
||||
test.beforeEach(async () => {
|
||||
await client.$connect();
|
||||
await client.user.deleteMany({});
|
||||
await client.snapshot.deleteMany({});
|
||||
await client.update.deleteMany({});
|
||||
await client.workspace.deleteMany({});
|
||||
await client.$disconnect();
|
||||
});
|
||||
|
||||
test.beforeEach(async () => {
|
||||
const module = await Test.createTestingModule({
|
||||
imports: [AppModule],
|
||||
}).compile();
|
||||
app = module.createNestApplication();
|
||||
app.use(
|
||||
graphqlUploadExpress({
|
||||
maxFileSize: 10 * 1024 * 1024,
|
||||
maxFiles: 5,
|
||||
})
|
||||
);
|
||||
await app.init();
|
||||
});
|
||||
|
||||
test.afterEach(async () => {
|
||||
await app.close();
|
||||
});
|
||||
|
||||
test('should set blobs', async t => {
|
||||
const u1 = await signUp(app, 'u1', 'u1@affine.pro', '1');
|
||||
|
||||
const workspace = await createWorkspace(app, u1.token.token);
|
||||
|
||||
const buffer1 = Buffer.from([0, 0]);
|
||||
const hash1 = await setBlob(app, u1.token.token, workspace.id, buffer1);
|
||||
const buffer2 = Buffer.from([0, 1]);
|
||||
const hash2 = await setBlob(app, u1.token.token, workspace.id, buffer2);
|
||||
|
||||
const server = app.getHttpServer();
|
||||
|
||||
const response1 = await request(server)
|
||||
.get(`/api/workspaces/${workspace.id}/blobs/${hash1}`)
|
||||
.auth(u1.token.token, { type: 'bearer' })
|
||||
.buffer();
|
||||
|
||||
deepEqual(response1.body, buffer1, 'failed to get blob');
|
||||
|
||||
const response2 = await request(server)
|
||||
.get(`/api/workspaces/${workspace.id}/blobs/${hash2}`)
|
||||
.auth(u1.token.token, { type: 'bearer' })
|
||||
.buffer();
|
||||
|
||||
deepEqual(response2.body, buffer2, 'failed to get blob');
|
||||
t.pass();
|
||||
});
|
||||
|
||||
test('should list blobs', async t => {
|
||||
const u1 = await signUp(app, 'u1', 'u1@affine.pro', '1');
|
||||
|
||||
const workspace = await createWorkspace(app, u1.token.token);
|
||||
const blobs = await listBlobs(app, u1.token.token, workspace.id);
|
||||
ok(blobs.length === 0, 'failed to list blobs');
|
||||
|
||||
const buffer1 = Buffer.from([0, 0]);
|
||||
const hash1 = await setBlob(app, u1.token.token, workspace.id, buffer1);
|
||||
const buffer2 = Buffer.from([0, 1]);
|
||||
const hash2 = await setBlob(app, u1.token.token, workspace.id, buffer2);
|
||||
|
||||
const ret = await listBlobs(app, u1.token.token, workspace.id);
|
||||
ok(ret.length === 2, 'failed to list blobs');
|
||||
ok(ret[0] === hash1, 'failed to list blobs');
|
||||
ok(ret[1] === hash2, 'failed to list blobs');
|
||||
t.pass();
|
||||
});
|
||||
|
||||
test('should calc blobs size', async t => {
|
||||
const u1 = await signUp(app, 'u1', 'u1@affine.pro', '1');
|
||||
|
||||
const workspace = await createWorkspace(app, u1.token.token);
|
||||
|
||||
const buffer1 = Buffer.from([0, 0]);
|
||||
await setBlob(app, u1.token.token, workspace.id, buffer1);
|
||||
const buffer2 = Buffer.from([0, 1]);
|
||||
await setBlob(app, u1.token.token, workspace.id, buffer2);
|
||||
|
||||
const size = await collectBlobSizes(app, u1.token.token, workspace.id);
|
||||
ok(size === 4, 'failed to collect blob sizes');
|
||||
t.pass();
|
||||
});
|
||||
|
||||
test('should calc all blobs size', async t => {
|
||||
const u1 = await signUp(app, 'u1', 'u1@affine.pro', '1');
|
||||
|
||||
const workspace1 = await createWorkspace(app, u1.token.token);
|
||||
|
||||
const buffer1 = Buffer.from([0, 0]);
|
||||
await setBlob(app, u1.token.token, workspace1.id, buffer1);
|
||||
const buffer2 = Buffer.from([0, 1]);
|
||||
await setBlob(app, u1.token.token, workspace1.id, buffer2);
|
||||
|
||||
const workspace2 = await createWorkspace(app, u1.token.token);
|
||||
|
||||
const buffer3 = Buffer.from([0, 0]);
|
||||
await setBlob(app, u1.token.token, workspace2.id, buffer3);
|
||||
const buffer4 = Buffer.from([0, 1]);
|
||||
await setBlob(app, u1.token.token, workspace2.id, buffer4);
|
||||
|
||||
const size = await collectAllBlobSizes(app, u1.token.token);
|
||||
ok(size === 8, 'failed to collect all blob sizes');
|
||||
t.pass();
|
||||
});
|
||||
|
@ -1,9 +1,9 @@
|
||||
import { ok } from 'node:assert';
|
||||
import { afterEach, beforeEach, describe, it } from 'node:test';
|
||||
|
||||
import type { INestApplication } from '@nestjs/common';
|
||||
import { Test } from '@nestjs/testing';
|
||||
import { PrismaClient } from '@prisma/client';
|
||||
import test from 'ava';
|
||||
// @ts-expect-error graphql-upload is not typed
|
||||
import graphqlUploadExpress from 'graphql-upload/graphqlUploadExpress.mjs';
|
||||
|
||||
@ -21,169 +21,168 @@ import {
|
||||
signUp,
|
||||
} from './utils';
|
||||
|
||||
describe('Workspace Module - invite', () => {
|
||||
let app: INestApplication;
|
||||
let app: INestApplication;
|
||||
|
||||
const client = new PrismaClient();
|
||||
const client = new PrismaClient();
|
||||
|
||||
let auth: AuthService;
|
||||
let mail: MailService;
|
||||
let auth: AuthService;
|
||||
let mail: MailService;
|
||||
|
||||
// cleanup database before each test
|
||||
beforeEach(async () => {
|
||||
await client.$connect();
|
||||
await client.user.deleteMany({});
|
||||
await client.snapshot.deleteMany({});
|
||||
await client.update.deleteMany({});
|
||||
await client.workspace.deleteMany({});
|
||||
await client.$disconnect();
|
||||
});
|
||||
// cleanup database before each test
|
||||
test.beforeEach(async () => {
|
||||
await client.$connect();
|
||||
await client.user.deleteMany({});
|
||||
await client.snapshot.deleteMany({});
|
||||
await client.update.deleteMany({});
|
||||
await client.workspace.deleteMany({});
|
||||
await client.$disconnect();
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
const module = await Test.createTestingModule({
|
||||
imports: [AppModule],
|
||||
}).compile();
|
||||
app = module.createNestApplication();
|
||||
app.use(
|
||||
graphqlUploadExpress({
|
||||
maxFileSize: 10 * 1024 * 1024,
|
||||
maxFiles: 5,
|
||||
})
|
||||
);
|
||||
await app.init();
|
||||
test.beforeEach(async () => {
|
||||
const module = await Test.createTestingModule({
|
||||
imports: [AppModule],
|
||||
}).compile();
|
||||
app = module.createNestApplication();
|
||||
app.use(
|
||||
graphqlUploadExpress({
|
||||
maxFileSize: 10 * 1024 * 1024,
|
||||
maxFiles: 5,
|
||||
})
|
||||
);
|
||||
await app.init();
|
||||
|
||||
auth = module.get(AuthService);
|
||||
mail = module.get(MailService);
|
||||
});
|
||||
auth = module.get(AuthService);
|
||||
mail = module.get(MailService);
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await app.close();
|
||||
});
|
||||
test.afterEach(async () => {
|
||||
await app.close();
|
||||
});
|
||||
|
||||
it('should invite a user', async () => {
|
||||
test('should invite a user', async t => {
|
||||
const u1 = await signUp(app, 'u1', 'u1@affine.pro', '1');
|
||||
const u2 = await signUp(app, 'u2', 'u2@affine.pro', '1');
|
||||
|
||||
const workspace = await createWorkspace(app, u1.token.token);
|
||||
|
||||
const invite = await inviteUser(
|
||||
app,
|
||||
u1.token.token,
|
||||
workspace.id,
|
||||
u2.email,
|
||||
'Admin'
|
||||
);
|
||||
ok(!!invite, 'failed to invite user');
|
||||
t.pass();
|
||||
});
|
||||
|
||||
test('should accept an invite', async t => {
|
||||
const u1 = await signUp(app, 'u1', 'u1@affine.pro', '1');
|
||||
const u2 = await signUp(app, 'u2', 'u2@affine.pro', '1');
|
||||
|
||||
const workspace = await createWorkspace(app, u1.token.token);
|
||||
await inviteUser(app, u1.token.token, workspace.id, u2.email, 'Admin');
|
||||
|
||||
const accept = await acceptInvite(app, u2.token.token, workspace.id);
|
||||
ok(accept === true, 'failed to accept invite');
|
||||
|
||||
const currWorkspace = await getWorkspace(app, u1.token.token, workspace.id);
|
||||
const currMember = currWorkspace.members.find(u => u.email === u2.email);
|
||||
ok(currMember !== undefined, 'failed to invite user');
|
||||
ok(currMember.id === u2.id, 'failed to invite user');
|
||||
ok(!currMember.accepted, 'failed to invite user');
|
||||
t.pass();
|
||||
});
|
||||
|
||||
test('should leave a workspace', async t => {
|
||||
const u1 = await signUp(app, 'u1', 'u1@affine.pro', '1');
|
||||
const u2 = await signUp(app, 'u2', 'u2@affine.pro', '1');
|
||||
|
||||
const workspace = await createWorkspace(app, u1.token.token);
|
||||
await inviteUser(app, u1.token.token, workspace.id, u2.email, 'Admin');
|
||||
await acceptInvite(app, u2.token.token, workspace.id);
|
||||
|
||||
const leave = await leaveWorkspace(app, u2.token.token, workspace.id);
|
||||
ok(leave === true, 'failed to leave workspace');
|
||||
t.pass();
|
||||
});
|
||||
|
||||
test('should revoke a user', async t => {
|
||||
const u1 = await signUp(app, 'u1', 'u1@affine.pro', '1');
|
||||
const u2 = await signUp(app, 'u2', 'u2@affine.pro', '1');
|
||||
|
||||
const workspace = await createWorkspace(app, u1.token.token);
|
||||
await inviteUser(app, u1.token.token, workspace.id, u2.email, 'Admin');
|
||||
|
||||
const currWorkspace = await getWorkspace(app, u1.token.token, workspace.id);
|
||||
ok(currWorkspace.members.length === 2, 'failed to invite user');
|
||||
|
||||
const revoke = await revokeUser(app, u1.token.token, workspace.id, u2.id);
|
||||
ok(revoke === true, 'failed to revoke user');
|
||||
t.pass();
|
||||
});
|
||||
|
||||
test('should create user if not exist', async t => {
|
||||
const u1 = await signUp(app, 'u1', 'u1@affine.pro', '1');
|
||||
|
||||
const workspace = await createWorkspace(app, u1.token.token);
|
||||
|
||||
await inviteUser(app, u1.token.token, workspace.id, 'u2@affine.pro', 'Admin');
|
||||
|
||||
const user = await auth.getUserByEmail('u2@affine.pro');
|
||||
ok(user !== undefined, 'failed to create user');
|
||||
ok(user?.name === 'Unnamed', 'failed to create user');
|
||||
t.pass();
|
||||
});
|
||||
|
||||
test('should invite a user by link', async t => {
|
||||
const u1 = await signUp(app, 'u1', 'u1@affine.pro', '1');
|
||||
const u2 = await signUp(app, 'u2', 'u2@affine.pro', '1');
|
||||
|
||||
const workspace = await createWorkspace(app, u1.token.token);
|
||||
|
||||
const invite = await inviteUser(
|
||||
app,
|
||||
u1.token.token,
|
||||
workspace.id,
|
||||
u2.email,
|
||||
'Admin'
|
||||
);
|
||||
|
||||
const accept = await acceptInviteById(app, workspace.id, invite);
|
||||
ok(accept === true, 'failed to accept invite');
|
||||
|
||||
const invite1 = await inviteUser(
|
||||
app,
|
||||
u1.token.token,
|
||||
workspace.id,
|
||||
u2.email,
|
||||
'Admin'
|
||||
);
|
||||
|
||||
ok(invite === invite1, 'repeat the invitation must return same id');
|
||||
|
||||
const currWorkspace = await getWorkspace(app, u1.token.token, workspace.id);
|
||||
const currMember = currWorkspace.members.find(u => u.email === u2.email);
|
||||
ok(currMember !== undefined, 'failed to invite user');
|
||||
ok(currMember.inviteId === invite, 'failed to check invite id');
|
||||
t.pass();
|
||||
});
|
||||
|
||||
test('should send invite email', async t => {
|
||||
if (mail.hasConfigured()) {
|
||||
const u1 = await signUp(app, 'u1', 'u1@affine.pro', '1');
|
||||
const u2 = await signUp(app, 'u2', 'u2@affine.pro', '1');
|
||||
const u2 = await signUp(app, 'test', 'production@toeverything.info', '1');
|
||||
|
||||
const workspace = await createWorkspace(app, u1.token.token);
|
||||
|
||||
const invite = await inviteUser(
|
||||
app,
|
||||
u1.token.token,
|
||||
workspace.id,
|
||||
u2.email,
|
||||
'Admin'
|
||||
);
|
||||
ok(!!invite, 'failed to invite user');
|
||||
});
|
||||
|
||||
it('should accept an invite', async () => {
|
||||
const u1 = await signUp(app, 'u1', 'u1@affine.pro', '1');
|
||||
const u2 = await signUp(app, 'u2', 'u2@affine.pro', '1');
|
||||
|
||||
const workspace = await createWorkspace(app, u1.token.token);
|
||||
await inviteUser(app, u1.token.token, workspace.id, u2.email, 'Admin');
|
||||
|
||||
const accept = await acceptInvite(app, u2.token.token, workspace.id);
|
||||
ok(accept === true, 'failed to accept invite');
|
||||
|
||||
const currWorkspace = await getWorkspace(app, u1.token.token, workspace.id);
|
||||
const currMember = currWorkspace.members.find(u => u.email === u2.email);
|
||||
ok(currMember !== undefined, 'failed to invite user');
|
||||
ok(currMember.id === u2.id, 'failed to invite user');
|
||||
ok(!currMember.accepted, 'failed to invite user');
|
||||
});
|
||||
|
||||
it('should leave a workspace', async () => {
|
||||
const u1 = await signUp(app, 'u1', 'u1@affine.pro', '1');
|
||||
const u2 = await signUp(app, 'u2', 'u2@affine.pro', '1');
|
||||
|
||||
const workspace = await createWorkspace(app, u1.token.token);
|
||||
await inviteUser(app, u1.token.token, workspace.id, u2.email, 'Admin');
|
||||
await acceptInvite(app, u2.token.token, workspace.id);
|
||||
|
||||
const leave = await leaveWorkspace(app, u2.token.token, workspace.id);
|
||||
ok(leave === true, 'failed to leave workspace');
|
||||
});
|
||||
|
||||
it('should revoke a user', async () => {
|
||||
const u1 = await signUp(app, 'u1', 'u1@affine.pro', '1');
|
||||
const u2 = await signUp(app, 'u2', 'u2@affine.pro', '1');
|
||||
|
||||
const workspace = await createWorkspace(app, u1.token.token);
|
||||
await inviteUser(app, u1.token.token, workspace.id, u2.email, 'Admin');
|
||||
|
||||
const currWorkspace = await getWorkspace(app, u1.token.token, workspace.id);
|
||||
ok(currWorkspace.members.length === 2, 'failed to invite user');
|
||||
|
||||
const revoke = await revokeUser(app, u1.token.token, workspace.id, u2.id);
|
||||
ok(revoke === true, 'failed to revoke user');
|
||||
});
|
||||
|
||||
it('should create user if not exist', async () => {
|
||||
const u1 = await signUp(app, 'u1', 'u1@affine.pro', '1');
|
||||
|
||||
const workspace = await createWorkspace(app, u1.token.token);
|
||||
|
||||
await inviteUser(
|
||||
app,
|
||||
u1.token.token,
|
||||
workspace.id,
|
||||
'u2@affine.pro',
|
||||
'Admin'
|
||||
);
|
||||
|
||||
const user = await auth.getUserByEmail('u2@affine.pro');
|
||||
ok(user !== undefined, 'failed to create user');
|
||||
ok(user?.name === 'Unnamed', 'failed to create user');
|
||||
});
|
||||
|
||||
it('should invite a user by link', async () => {
|
||||
const u1 = await signUp(app, 'u1', 'u1@affine.pro', '1');
|
||||
const u2 = await signUp(app, 'u2', 'u2@affine.pro', '1');
|
||||
|
||||
const workspace = await createWorkspace(app, u1.token.token);
|
||||
|
||||
const invite = await inviteUser(
|
||||
app,
|
||||
u1.token.token,
|
||||
workspace.id,
|
||||
u2.email,
|
||||
'Admin'
|
||||
'Admin',
|
||||
true
|
||||
);
|
||||
|
||||
const accept = await acceptInviteById(app, workspace.id, invite);
|
||||
ok(accept === true, 'failed to accept invite');
|
||||
|
||||
const invite1 = await inviteUser(
|
||||
app,
|
||||
u1.token.token,
|
||||
workspace.id,
|
||||
u2.email,
|
||||
'Admin'
|
||||
);
|
||||
|
||||
ok(invite === invite1, 'repeat the invitation must return same id');
|
||||
|
||||
const currWorkspace = await getWorkspace(app, u1.token.token, workspace.id);
|
||||
const currMember = currWorkspace.members.find(u => u.email === u2.email);
|
||||
ok(currMember !== undefined, 'failed to invite user');
|
||||
ok(currMember.inviteId === invite, 'failed to check invite id');
|
||||
});
|
||||
|
||||
it('should send invite email', async () => {
|
||||
if (mail.hasConfigured()) {
|
||||
const u1 = await signUp(app, 'u1', 'u1@affine.pro', '1');
|
||||
const u2 = await signUp(app, 'test', 'production@toeverything.info', '1');
|
||||
|
||||
const workspace = await createWorkspace(app, u1.token.token);
|
||||
await inviteUser(
|
||||
app,
|
||||
u1.token.token,
|
||||
workspace.id,
|
||||
u2.email,
|
||||
'Admin',
|
||||
true
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
t.pass();
|
||||
});
|
||||
|
@ -1,9 +1,9 @@
|
||||
import { deepEqual, ok, rejects } from 'node:assert';
|
||||
import { afterEach, beforeEach, describe, it } from 'node:test';
|
||||
|
||||
import type { INestApplication } from '@nestjs/common';
|
||||
import { Test } from '@nestjs/testing';
|
||||
import { PrismaClient } from '@prisma/client';
|
||||
import test from 'ava';
|
||||
// @ts-expect-error graphql-upload is not typed
|
||||
import graphqlUploadExpress from 'graphql-upload/graphqlUploadExpress.mjs';
|
||||
import request from 'supertest';
|
||||
@ -22,222 +22,213 @@ import {
|
||||
updateWorkspace,
|
||||
} from './utils';
|
||||
|
||||
describe('Workspace Module', () => {
|
||||
let app: INestApplication;
|
||||
let app: INestApplication;
|
||||
|
||||
const client = new PrismaClient();
|
||||
const client = new PrismaClient();
|
||||
|
||||
// cleanup database before each test
|
||||
beforeEach(async () => {
|
||||
await client.$connect();
|
||||
await client.user.deleteMany({});
|
||||
await client.update.deleteMany({});
|
||||
await client.snapshot.deleteMany({});
|
||||
await client.workspace.deleteMany({});
|
||||
await client.$disconnect();
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
const module = await Test.createTestingModule({
|
||||
imports: [AppModule],
|
||||
}).compile();
|
||||
app = module.createNestApplication();
|
||||
app.use(
|
||||
graphqlUploadExpress({
|
||||
maxFileSize: 10 * 1024 * 1024,
|
||||
maxFiles: 5,
|
||||
})
|
||||
);
|
||||
await app.init();
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
await app.close();
|
||||
});
|
||||
|
||||
it('should register a user', async () => {
|
||||
const user = await signUp(app, 'u1', 'u1@affine.pro', '123456');
|
||||
ok(typeof user.id === 'string', 'user.id is not a string');
|
||||
ok(user.name === 'u1', 'user.name is not valid');
|
||||
ok(user.email === 'u1@affine.pro', 'user.email is not valid');
|
||||
});
|
||||
|
||||
it.skip('should be throttled at call signUp', async () => {
|
||||
let token = '';
|
||||
for (let i = 0; i < 10; i++) {
|
||||
token = (await signUp(app, `u${i}`, `u${i}@affine.pro`, `${i}`)).token
|
||||
.token;
|
||||
// throttles are applied to each endpoint separately
|
||||
await currentUser(app, token);
|
||||
}
|
||||
await rejects(signUp(app, 'u11', 'u11@affine.pro', '11'));
|
||||
await rejects(currentUser(app, token));
|
||||
});
|
||||
|
||||
it('should create a workspace', async () => {
|
||||
const user = await signUp(app, 'u1', 'u1@affine.pro', '1');
|
||||
|
||||
const workspace = await createWorkspace(app, user.token.token);
|
||||
ok(typeof workspace.id === 'string', 'workspace.id is not a string');
|
||||
});
|
||||
|
||||
it('should can publish workspace', async () => {
|
||||
const user = await signUp(app, 'u1', 'u1@affine.pro', '1');
|
||||
const workspace = await createWorkspace(app, user.token.token);
|
||||
|
||||
const isPublic = await updateWorkspace(
|
||||
app,
|
||||
user.token.token,
|
||||
workspace.id,
|
||||
true
|
||||
);
|
||||
ok(isPublic === true, 'failed to publish workspace');
|
||||
|
||||
const isPrivate = await updateWorkspace(
|
||||
app,
|
||||
user.token.token,
|
||||
workspace.id,
|
||||
false
|
||||
);
|
||||
ok(isPrivate === false, 'failed to unpublish workspace');
|
||||
});
|
||||
|
||||
it('should can read published workspace', async () => {
|
||||
const user = await signUp(app, 'u1', 'u1@affine.pro', '1');
|
||||
const workspace = await createWorkspace(app, user.token.token);
|
||||
|
||||
await rejects(
|
||||
getPublicWorkspace(app, 'not_exists_ws'),
|
||||
'must not get not exists workspace'
|
||||
);
|
||||
await rejects(
|
||||
getPublicWorkspace(app, workspace.id),
|
||||
'must not get private workspace'
|
||||
);
|
||||
|
||||
await updateWorkspace(app, user.token.token, workspace.id, true);
|
||||
|
||||
const publicWorkspace = await getPublicWorkspace(app, workspace.id);
|
||||
ok(publicWorkspace.id === workspace.id, 'failed to get public workspace');
|
||||
});
|
||||
|
||||
it('should share a page', async () => {
|
||||
const u1 = await signUp(app, 'u1', 'u1@affine.pro', '1');
|
||||
const u2 = await signUp(app, 'u2', 'u2@affine.pro', '1');
|
||||
|
||||
const workspace = await createWorkspace(app, u1.token.token);
|
||||
|
||||
const share = await sharePage(app, u1.token.token, workspace.id, 'page1');
|
||||
ok(share === true, 'failed to share page');
|
||||
const pages = await getWorkspaceSharedPages(
|
||||
app,
|
||||
u1.token.token,
|
||||
workspace.id
|
||||
);
|
||||
ok(pages.length === 1, 'failed to get shared pages');
|
||||
ok(pages[0] === 'page1', 'failed to get shared page: page1');
|
||||
|
||||
const msg1 = await sharePage(app, u2.token.token, workspace.id, 'page2');
|
||||
ok(msg1 === 'Permission denied', 'unauthorized user can share page');
|
||||
const msg2 = await revokePage(
|
||||
app,
|
||||
u2.token.token,
|
||||
'not_exists_ws',
|
||||
'page2'
|
||||
);
|
||||
ok(msg2 === 'Permission denied', 'unauthorized user can share page');
|
||||
|
||||
await inviteUser(app, u1.token.token, workspace.id, u2.email, 'Admin');
|
||||
await acceptInvite(app, u2.token.token, workspace.id);
|
||||
const invited = await sharePage(app, u2.token.token, workspace.id, 'page2');
|
||||
ok(invited === true, 'failed to share page');
|
||||
|
||||
const revoke = await revokePage(app, u1.token.token, workspace.id, 'page1');
|
||||
ok(revoke === true, 'failed to revoke page');
|
||||
const pages2 = await getWorkspaceSharedPages(
|
||||
app,
|
||||
u1.token.token,
|
||||
workspace.id
|
||||
);
|
||||
ok(pages2.length === 1, 'failed to get shared pages');
|
||||
ok(pages2[0] === 'page2', 'failed to get shared page: page2');
|
||||
|
||||
const msg3 = await revokePage(app, u1.token.token, workspace.id, 'page3');
|
||||
ok(msg3 === false, 'can revoke non-exists page');
|
||||
|
||||
const msg4 = await revokePage(app, u1.token.token, workspace.id, 'page2');
|
||||
ok(msg4 === true, 'failed to revoke page');
|
||||
const page3 = await getWorkspaceSharedPages(
|
||||
app,
|
||||
u1.token.token,
|
||||
workspace.id
|
||||
);
|
||||
ok(page3.length === 0, 'failed to get shared pages');
|
||||
});
|
||||
|
||||
it('should can get workspace doc', async () => {
|
||||
const u1 = await signUp(app, 'u1', 'u1@affine.pro', '1');
|
||||
const u2 = await signUp(app, 'u2', 'u2@affine.pro', '2');
|
||||
const workspace = await createWorkspace(app, u1.token.token);
|
||||
|
||||
const res1 = await request(app.getHttpServer())
|
||||
.get(`/api/workspaces/${workspace.id}/docs/${workspace.id}`)
|
||||
.auth(u1.token.token, { type: 'bearer' })
|
||||
.expect(200)
|
||||
.type('application/octet-stream');
|
||||
|
||||
deepEqual(
|
||||
res1.body,
|
||||
Buffer.from([0, 0]),
|
||||
'failed to get doc with u1 token'
|
||||
);
|
||||
|
||||
await request(app.getHttpServer())
|
||||
.get(`/api/workspaces/${workspace.id}/docs/${workspace.id}`)
|
||||
.expect(403);
|
||||
await request(app.getHttpServer())
|
||||
.get(`/api/workspaces/${workspace.id}/docs/${workspace.id}`)
|
||||
.auth(u2.token.token, { type: 'bearer' })
|
||||
.expect(403);
|
||||
|
||||
await inviteUser(app, u1.token.token, workspace.id, u2.email, 'Admin');
|
||||
await request(app.getHttpServer())
|
||||
.get(`/api/workspaces/${workspace.id}/docs/${workspace.id}`)
|
||||
.auth(u2.token.token, { type: 'bearer' })
|
||||
.expect(403);
|
||||
|
||||
await acceptInvite(app, u2.token.token, workspace.id);
|
||||
const res2 = await request(app.getHttpServer())
|
||||
.get(`/api/workspaces/${workspace.id}/docs/${workspace.id}`)
|
||||
.auth(u2.token.token, { type: 'bearer' })
|
||||
.expect(200)
|
||||
.type('application/octet-stream');
|
||||
|
||||
deepEqual(
|
||||
res2.body,
|
||||
Buffer.from([0, 0]),
|
||||
'failed to get doc with u2 token'
|
||||
);
|
||||
});
|
||||
|
||||
it('should be able to get public workspace doc', async () => {
|
||||
const user = await signUp(app, 'u1', 'u1@affine.pro', '1');
|
||||
const workspace = await createWorkspace(app, user.token.token);
|
||||
|
||||
const isPublic = await updateWorkspace(
|
||||
app,
|
||||
user.token.token,
|
||||
workspace.id,
|
||||
true
|
||||
);
|
||||
|
||||
ok(isPublic === true, 'failed to publish workspace');
|
||||
|
||||
const res = await request(app.getHttpServer())
|
||||
.get(`/api/workspaces/${workspace.id}/docs/${workspace.id}`)
|
||||
.expect(200)
|
||||
.type('application/octet-stream');
|
||||
|
||||
deepEqual(res.body, Buffer.from([0, 0]), 'failed to get public doc');
|
||||
});
|
||||
// cleanup database before each test
|
||||
test.beforeEach(async () => {
|
||||
await client.$connect();
|
||||
await client.user.deleteMany({});
|
||||
await client.update.deleteMany({});
|
||||
await client.snapshot.deleteMany({});
|
||||
await client.workspace.deleteMany({});
|
||||
await client.$disconnect();
|
||||
});
|
||||
|
||||
test.beforeEach(async () => {
|
||||
const module = await Test.createTestingModule({
|
||||
imports: [AppModule],
|
||||
}).compile();
|
||||
app = module.createNestApplication();
|
||||
app.use(
|
||||
graphqlUploadExpress({
|
||||
maxFileSize: 10 * 1024 * 1024,
|
||||
maxFiles: 5,
|
||||
})
|
||||
);
|
||||
await app.init();
|
||||
});
|
||||
|
||||
test.afterEach(async () => {
|
||||
await app.close();
|
||||
});
|
||||
|
||||
test('should register a user', async t => {
|
||||
const user = await signUp(app, 'u1', 'u1@affine.pro', '123456');
|
||||
t.true(typeof user.id === 'string', 'user.id is not a string');
|
||||
t.true(user.name === 'u1', 'user.name is not valid');
|
||||
t.true(user.email === 'u1@affine.pro', 'user.email is not valid');
|
||||
});
|
||||
|
||||
test.skip('should be throttled at call signUp', async t => {
|
||||
let token = '';
|
||||
for (let i = 0; i < 10; i++) {
|
||||
token = (await signUp(app, `u${i}`, `u${i}@affine.pro`, `${i}`)).token
|
||||
.token;
|
||||
// throttles are applied to each endpoint separately
|
||||
await currentUser(app, token);
|
||||
}
|
||||
await rejects(signUp(app, 'u11', 'u11@affine.pro', '11'));
|
||||
await rejects(currentUser(app, token));
|
||||
t.pass();
|
||||
});
|
||||
|
||||
test('should create a workspace', async t => {
|
||||
const user = await signUp(app, 'u1', 'u1@affine.pro', '1');
|
||||
|
||||
const workspace = await createWorkspace(app, user.token.token);
|
||||
ok(typeof workspace.id === 'string', 'workspace.id is not a string');
|
||||
t.pass();
|
||||
});
|
||||
|
||||
test('should can publish workspace', async t => {
|
||||
const user = await signUp(app, 'u1', 'u1@affine.pro', '1');
|
||||
const workspace = await createWorkspace(app, user.token.token);
|
||||
|
||||
const isPublic = await updateWorkspace(
|
||||
app,
|
||||
user.token.token,
|
||||
workspace.id,
|
||||
true
|
||||
);
|
||||
ok(isPublic === true, 'failed to publish workspace');
|
||||
|
||||
const isPrivate = await updateWorkspace(
|
||||
app,
|
||||
user.token.token,
|
||||
workspace.id,
|
||||
false
|
||||
);
|
||||
ok(isPrivate === false, 'failed to unpublish workspace');
|
||||
t.pass();
|
||||
});
|
||||
|
||||
test('should can read published workspace', async t => {
|
||||
const user = await signUp(app, 'u1', 'u1@affine.pro', '1');
|
||||
const workspace = await createWorkspace(app, user.token.token);
|
||||
|
||||
await rejects(
|
||||
getPublicWorkspace(app, 'not_exists_ws'),
|
||||
'must not get not exists workspace'
|
||||
);
|
||||
await rejects(
|
||||
getPublicWorkspace(app, workspace.id),
|
||||
'must not get private workspace'
|
||||
);
|
||||
|
||||
await updateWorkspace(app, user.token.token, workspace.id, true);
|
||||
|
||||
const publicWorkspace = await getPublicWorkspace(app, workspace.id);
|
||||
ok(publicWorkspace.id === workspace.id, 'failed to get public workspace');
|
||||
t.pass();
|
||||
});
|
||||
|
||||
test('should share a page', async t => {
|
||||
const u1 = await signUp(app, 'u1', 'u1@affine.pro', '1');
|
||||
const u2 = await signUp(app, 'u2', 'u2@affine.pro', '1');
|
||||
|
||||
const workspace = await createWorkspace(app, u1.token.token);
|
||||
|
||||
const share = await sharePage(app, u1.token.token, workspace.id, 'page1');
|
||||
t.true(share === true, 'failed to share page');
|
||||
const pages = await getWorkspaceSharedPages(
|
||||
app,
|
||||
u1.token.token,
|
||||
workspace.id
|
||||
);
|
||||
t.true(pages.length === 1, 'failed to get shared pages');
|
||||
t.true(pages[0] === 'page1', 'failed to get shared page: page1');
|
||||
|
||||
const msg1 = await sharePage(app, u2.token.token, workspace.id, 'page2');
|
||||
t.true(msg1 === 'Permission denied', 'unauthorized user can share page');
|
||||
const msg2 = await revokePage(app, u2.token.token, 'not_exists_ws', 'page2');
|
||||
t.true(msg2 === 'Permission denied', 'unauthorized user can share page');
|
||||
|
||||
await inviteUser(app, u1.token.token, workspace.id, u2.email, 'Admin');
|
||||
await acceptInvite(app, u2.token.token, workspace.id);
|
||||
const invited = await sharePage(app, u2.token.token, workspace.id, 'page2');
|
||||
t.true(invited === true, 'failed to share page');
|
||||
|
||||
const revoke = await revokePage(app, u1.token.token, workspace.id, 'page1');
|
||||
t.true(revoke === true, 'failed to revoke page');
|
||||
const pages2 = await getWorkspaceSharedPages(
|
||||
app,
|
||||
u1.token.token,
|
||||
workspace.id
|
||||
);
|
||||
t.true(pages2.length === 1, 'failed to get shared pages');
|
||||
t.true(pages2[0] === 'page2', 'failed to get shared page: page2');
|
||||
|
||||
const msg3 = await revokePage(app, u1.token.token, workspace.id, 'page3');
|
||||
t.true(msg3 === false, 'can revoke non-exists page');
|
||||
|
||||
const msg4 = await revokePage(app, u1.token.token, workspace.id, 'page2');
|
||||
t.true(msg4 === true, 'failed to revoke page');
|
||||
const page3 = await getWorkspaceSharedPages(
|
||||
app,
|
||||
u1.token.token,
|
||||
workspace.id
|
||||
);
|
||||
t.true(page3.length === 0, 'failed to get shared pages');
|
||||
});
|
||||
|
||||
test('should can get workspace doc', async t => {
|
||||
const u1 = await signUp(app, 'u1', 'u1@affine.pro', '1');
|
||||
const u2 = await signUp(app, 'u2', 'u2@affine.pro', '2');
|
||||
const workspace = await createWorkspace(app, u1.token.token);
|
||||
|
||||
const res1 = await request(app.getHttpServer())
|
||||
.get(`/api/workspaces/${workspace.id}/docs/${workspace.id}`)
|
||||
.auth(u1.token.token, { type: 'bearer' })
|
||||
.expect(200)
|
||||
.type('application/octet-stream');
|
||||
|
||||
deepEqual(res1.body, Buffer.from([0, 0]), 'failed to get doc with u1 token');
|
||||
|
||||
await request(app.getHttpServer())
|
||||
.get(`/api/workspaces/${workspace.id}/docs/${workspace.id}`)
|
||||
.expect(403);
|
||||
await request(app.getHttpServer())
|
||||
.get(`/api/workspaces/${workspace.id}/docs/${workspace.id}`)
|
||||
.auth(u2.token.token, { type: 'bearer' })
|
||||
.expect(403);
|
||||
|
||||
await inviteUser(app, u1.token.token, workspace.id, u2.email, 'Admin');
|
||||
await request(app.getHttpServer())
|
||||
.get(`/api/workspaces/${workspace.id}/docs/${workspace.id}`)
|
||||
.auth(u2.token.token, { type: 'bearer' })
|
||||
.expect(403);
|
||||
|
||||
await acceptInvite(app, u2.token.token, workspace.id);
|
||||
const res2 = await request(app.getHttpServer())
|
||||
.get(`/api/workspaces/${workspace.id}/docs/${workspace.id}`)
|
||||
.auth(u2.token.token, { type: 'bearer' })
|
||||
.expect(200)
|
||||
.type('application/octet-stream');
|
||||
|
||||
deepEqual(res2.body, Buffer.from([0, 0]), 'failed to get doc with u2 token');
|
||||
t.pass();
|
||||
});
|
||||
|
||||
test('should be able to get public workspace doc', async t => {
|
||||
const user = await signUp(app, 'u1', 'u1@affine.pro', '1');
|
||||
const workspace = await createWorkspace(app, user.token.token);
|
||||
|
||||
const isPublic = await updateWorkspace(
|
||||
app,
|
||||
user.token.token,
|
||||
workspace.id,
|
||||
true
|
||||
);
|
||||
|
||||
ok(isPublic === true, 'failed to publish workspace');
|
||||
|
||||
const res = await request(app.getHttpServer())
|
||||
.get(`/api/workspaces/${workspace.id}/docs/${workspace.id}`)
|
||||
.expect(200)
|
||||
.type('application/octet-stream');
|
||||
|
||||
deepEqual(res.body, Buffer.from([0, 0]), 'failed to get public doc');
|
||||
t.pass();
|
||||
});
|
||||
|
450
yarn.lock
450
yarn.lock
@ -699,6 +699,7 @@ __metadata:
|
||||
"@types/sinon": ^10.0.16
|
||||
"@types/supertest": ^2.0.12
|
||||
"@types/ws": ^8.5.5
|
||||
ava: ^5.3.1
|
||||
c8: ^8.0.1
|
||||
cookie-parser: ^1.4.6
|
||||
dotenv: ^16.3.1
|
||||
@ -14762,6 +14763,16 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"aggregate-error@npm:^4.0.0":
|
||||
version: 4.0.1
|
||||
resolution: "aggregate-error@npm:4.0.1"
|
||||
dependencies:
|
||||
clean-stack: ^4.0.0
|
||||
indent-string: ^5.0.0
|
||||
checksum: bb3ffdfd13447800fff237c2cba752c59868ee669104bb995dfbbe0b8320e967d679e683dabb640feb32e4882d60258165cde0baafc4cd467cc7d275a13ad6b5
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"ahocorasick@npm:1.0.2":
|
||||
version: 1.0.2
|
||||
resolution: "ahocorasick@npm:1.0.2"
|
||||
@ -14944,7 +14955,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"ansi-styles@npm:^6.0.0, ansi-styles@npm:^6.1.0":
|
||||
"ansi-styles@npm:^6.0.0, ansi-styles@npm:^6.1.0, ansi-styles@npm:^6.2.1":
|
||||
version: 6.2.1
|
||||
resolution: "ansi-styles@npm:6.2.1"
|
||||
checksum: ef940f2f0ced1a6347398da88a91da7930c33ecac3c77b72c5905f8b8fe402c52e6fde304ff5347f616e27a742da3f1dc76de98f6866c69251ad0b07a66776d9
|
||||
@ -15086,6 +15097,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"array-find-index@npm:^1.0.1":
|
||||
version: 1.0.2
|
||||
resolution: "array-find-index@npm:1.0.2"
|
||||
checksum: aac128bf369e1ac6c06ff0bb330788371c0e256f71279fb92d745e26fb4b9db8920e485b4ec25e841c93146bf71a34dcdbcefa115e7e0f96927a214d237b7081
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"array-flatten@npm:1.1.1":
|
||||
version: 1.1.1
|
||||
resolution: "array-flatten@npm:1.1.1"
|
||||
@ -15150,6 +15168,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"arrgv@npm:^1.0.2":
|
||||
version: 1.0.2
|
||||
resolution: "arrgv@npm:1.0.2"
|
||||
checksum: 470bbb406ea3b34810dd8b03c0b33282617a42d9fce0ab45d58596efefd042fc548eda49161fa8e3f607cbe9df90e7a67003a09043ab9081eff70f97c63dd0e2
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"arrify@npm:^1.0.1":
|
||||
version: 1.0.1
|
||||
resolution: "arrify@npm:1.0.1"
|
||||
@ -15164,6 +15189,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"arrify@npm:^3.0.0":
|
||||
version: 3.0.0
|
||||
resolution: "arrify@npm:3.0.0"
|
||||
checksum: d6c6f3dad9571234f320e130d57fddb2cc283c87f2ac7df6c7005dffc5161b7bb9376f4be655ed257050330336e84afc4f3020d77696ad231ff580a94ae5aba6
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"as-table@npm:^1.0.36":
|
||||
version: 1.0.55
|
||||
resolution: "as-table@npm:1.0.55"
|
||||
@ -15366,6 +15398,64 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"ava@npm:^5.3.1":
|
||||
version: 5.3.1
|
||||
resolution: "ava@npm:5.3.1"
|
||||
dependencies:
|
||||
acorn: ^8.8.2
|
||||
acorn-walk: ^8.2.0
|
||||
ansi-styles: ^6.2.1
|
||||
arrgv: ^1.0.2
|
||||
arrify: ^3.0.0
|
||||
callsites: ^4.0.0
|
||||
cbor: ^8.1.0
|
||||
chalk: ^5.2.0
|
||||
chokidar: ^3.5.3
|
||||
chunkd: ^2.0.1
|
||||
ci-info: ^3.8.0
|
||||
ci-parallel-vars: ^1.0.1
|
||||
clean-yaml-object: ^0.1.0
|
||||
cli-truncate: ^3.1.0
|
||||
code-excerpt: ^4.0.0
|
||||
common-path-prefix: ^3.0.0
|
||||
concordance: ^5.0.4
|
||||
currently-unhandled: ^0.4.1
|
||||
debug: ^4.3.4
|
||||
emittery: ^1.0.1
|
||||
figures: ^5.0.0
|
||||
globby: ^13.1.4
|
||||
ignore-by-default: ^2.1.0
|
||||
indent-string: ^5.0.0
|
||||
is-error: ^2.2.2
|
||||
is-plain-object: ^5.0.0
|
||||
is-promise: ^4.0.0
|
||||
matcher: ^5.0.0
|
||||
mem: ^9.0.2
|
||||
ms: ^2.1.3
|
||||
p-event: ^5.0.1
|
||||
p-map: ^5.5.0
|
||||
picomatch: ^2.3.1
|
||||
pkg-conf: ^4.0.0
|
||||
plur: ^5.1.0
|
||||
pretty-ms: ^8.0.0
|
||||
resolve-cwd: ^3.0.0
|
||||
stack-utils: ^2.0.6
|
||||
strip-ansi: ^7.0.1
|
||||
supertap: ^3.0.1
|
||||
temp-dir: ^3.0.0
|
||||
write-file-atomic: ^5.0.1
|
||||
yargs: ^17.7.2
|
||||
peerDependencies:
|
||||
"@ava/typescript": "*"
|
||||
peerDependenciesMeta:
|
||||
"@ava/typescript":
|
||||
optional: true
|
||||
bin:
|
||||
ava: entrypoints/cli.mjs
|
||||
checksum: 126a5932baef74eccd8bec992bd522e25c05b6ee4985dde87c20cece76c2377f0bf9448f242f3f9cd2abbf7a5ac932fe4e4abde2a23792d6271a6088e5a1984e
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"axios@npm:1.1.3":
|
||||
version: 1.1.3
|
||||
resolution: "axios@npm:1.1.3"
|
||||
@ -15791,6 +15881,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"blueimp-md5@npm:^2.10.0":
|
||||
version: 2.19.0
|
||||
resolution: "blueimp-md5@npm:2.19.0"
|
||||
checksum: 28095dcbd2c67152a2938006e8d7c74c3406ba6556071298f872505432feb2c13241b0476644160ee0a5220383ba94cb8ccdac0053b51f68d168728f9c382530
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"body-parser@npm:1.20.1":
|
||||
version: 1.20.1
|
||||
resolution: "body-parser@npm:1.20.1"
|
||||
@ -16194,6 +16291,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"callsites@npm:^4.0.0":
|
||||
version: 4.1.0
|
||||
resolution: "callsites@npm:4.1.0"
|
||||
checksum: 4ad31de7b7615fa25bdab9c2373865209d2d5190f895cdf2e2f518bd1dafa7ebcda2e6e9cc9640f2dfde6b3893d82fa4359a78ffc27baad2503227553c6882fa
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"camel-case@npm:^4.1.2":
|
||||
version: 4.1.2
|
||||
resolution: "camel-case@npm:4.1.2"
|
||||
@ -16292,6 +16396,15 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"cbor@npm:^8.1.0":
|
||||
version: 8.1.0
|
||||
resolution: "cbor@npm:8.1.0"
|
||||
dependencies:
|
||||
nofilter: ^3.1.0
|
||||
checksum: a90338435dc7b45cc01461af979e3bb6ddd4f2a08584c437586039cd5f2235014c06e49d664295debbfb3514d87b2f06728092ab6aa6175e2e85e9cd7dc0c1fd
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"chai@npm:^4.3.7":
|
||||
version: 4.3.8
|
||||
resolution: "chai@npm:4.3.8"
|
||||
@ -16550,6 +16663,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"chunkd@npm:^2.0.1":
|
||||
version: 2.0.1
|
||||
resolution: "chunkd@npm:2.0.1"
|
||||
checksum: bab8cc08c752a3648984385dc6f61d751e89dbeef648d22a3b661e1d470eaa0f5182f0b4303710f13ae83d2f85144f8eb2dde7a975861d9021b5c56b881f457b
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"ci-info@npm:^3.2.0, ci-info@npm:^3.8.0":
|
||||
version: 3.8.0
|
||||
resolution: "ci-info@npm:3.8.0"
|
||||
@ -16557,6 +16677,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"ci-parallel-vars@npm:^1.0.1":
|
||||
version: 1.0.1
|
||||
resolution: "ci-parallel-vars@npm:1.0.1"
|
||||
checksum: ae859831f7e8e3585db731b8306c336616e37bd709dad1d7775ea4c0731aefd94741dabb48201edc6827d000008fd7fb72cb977967614ee2d99d6b499f0c35fe
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"cjs-module-lexer@npm:^1.0.0, cjs-module-lexer@npm:^1.2.2":
|
||||
version: 1.2.3
|
||||
resolution: "cjs-module-lexer@npm:1.2.3"
|
||||
@ -16596,6 +16723,22 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"clean-stack@npm:^4.0.0":
|
||||
version: 4.2.0
|
||||
resolution: "clean-stack@npm:4.2.0"
|
||||
dependencies:
|
||||
escape-string-regexp: 5.0.0
|
||||
checksum: 373f656a31face5c615c0839213b9b542a0a48057abfb1df66900eab4dc2a5c6097628e4a0b5aa559cdfc4e66f8a14ea47be9681773165a44470ef5fb8ccc172
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"clean-yaml-object@npm:^0.1.0":
|
||||
version: 0.1.0
|
||||
resolution: "clean-yaml-object@npm:0.1.0"
|
||||
checksum: 0374ad2f1fbd4984ecf56ebc62200092f6372b9ccf1b7971bb979c328fb12fe76e759fb1e8adc491c80b7b1861f9f00c7f19813dd2a0f49c88231422c70451f4
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"cli-boxes@npm:^3.0.0":
|
||||
version: 3.0.0
|
||||
resolution: "cli-boxes@npm:3.0.0"
|
||||
@ -16812,6 +16955,15 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"code-excerpt@npm:^4.0.0":
|
||||
version: 4.0.0
|
||||
resolution: "code-excerpt@npm:4.0.0"
|
||||
dependencies:
|
||||
convert-to-spaces: ^2.0.1
|
||||
checksum: d57137d8f4825879283a828cc02a1115b56858dc54ed06c625c8f67d6685d1becd2fbaa7f0ab19ecca1f5cca03f8c97bbc1f013cab40261e4d3275032e65efe9
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"collect-v8-coverage@npm:^1.0.0":
|
||||
version: 1.0.2
|
||||
resolution: "collect-v8-coverage@npm:1.0.2"
|
||||
@ -17089,6 +17241,22 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"concordance@npm:^5.0.4":
|
||||
version: 5.0.4
|
||||
resolution: "concordance@npm:5.0.4"
|
||||
dependencies:
|
||||
date-time: ^3.1.0
|
||||
esutils: ^2.0.3
|
||||
fast-diff: ^1.2.0
|
||||
js-string-escape: ^1.0.1
|
||||
lodash: ^4.17.15
|
||||
md5-hex: ^3.0.1
|
||||
semver: ^7.3.2
|
||||
well-known-symbols: ^2.0.0
|
||||
checksum: 749153ba711492feb7c3d2f5bb04c107157440b3e39509bd5dd19ee7b3ac751d1e4cd75796d9f702e0a713312dbc661421c68aa4a2c34d5f6d91f47e3a1c64a6
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"concurrently@npm:^8.2.1":
|
||||
version: 8.2.1
|
||||
resolution: "concurrently@npm:8.2.1"
|
||||
@ -17210,6 +17378,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"convert-to-spaces@npm:^2.0.1":
|
||||
version: 2.0.1
|
||||
resolution: "convert-to-spaces@npm:2.0.1"
|
||||
checksum: bbb324e5916fe9866f65c0ff5f9c1ea933764d0bdb09fccaf59542e40545ed483db6b2339c6d9eb56a11965a58f1a6038f3174f0e2fb7601343c7107ca5e2751
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"cookie-parser@npm:^1.4.6":
|
||||
version: 1.4.6
|
||||
resolution: "cookie-parser@npm:1.4.6"
|
||||
@ -17651,6 +17826,15 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"currently-unhandled@npm:^0.4.1":
|
||||
version: 0.4.1
|
||||
resolution: "currently-unhandled@npm:0.4.1"
|
||||
dependencies:
|
||||
array-find-index: ^1.0.1
|
||||
checksum: 1f59fe10b5339b54b1a1eee110022f663f3495cf7cf2f480686e89edc7fa8bfe42dbab4b54f85034bc8b092a76cc7becbc2dad4f9adad332ab5831bec39ad540
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"cwd@npm:^0.10.0":
|
||||
version: 0.10.0
|
||||
resolution: "cwd@npm:0.10.0"
|
||||
@ -17698,6 +17882,15 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"date-time@npm:^3.1.0":
|
||||
version: 3.1.0
|
||||
resolution: "date-time@npm:3.1.0"
|
||||
dependencies:
|
||||
time-zone: ^1.0.0
|
||||
checksum: f9cfcd1b15dfeabab15c0b9d18eb9e4e2d9d4371713564178d46a8f91ad577a290b5178b80050718d02d9c0cf646f8a875011e12d1ed05871e9f72c72c8a8fe6
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"dayjs@npm:^1.11.3, dayjs@npm:^1.11.9":
|
||||
version: 1.11.9
|
||||
resolution: "dayjs@npm:1.11.9"
|
||||
@ -18843,6 +19036,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"emittery@npm:^1.0.1":
|
||||
version: 1.0.1
|
||||
resolution: "emittery@npm:1.0.1"
|
||||
checksum: d95faee6ffb2e023cadaa6804265fea5298c53d079f170112af8dfae3e141761363ea4510966128259346418e3ec7639310fd75059ecce2423bf8afd07004226
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"emoji-regex@npm:^8.0.0":
|
||||
version: 8.0.0
|
||||
resolution: "emoji-regex@npm:8.0.0"
|
||||
@ -19415,6 +19615,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"escape-string-regexp@npm:5.0.0, escape-string-regexp@npm:^5.0.0":
|
||||
version: 5.0.0
|
||||
resolution: "escape-string-regexp@npm:5.0.0"
|
||||
checksum: 20daabe197f3cb198ec28546deebcf24b3dbb1a5a269184381b3116d12f0532e06007f4bc8da25669d6a7f8efb68db0758df4cd981f57bc5b57f521a3e12c59e
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"escape-string-regexp@npm:^1.0.2, escape-string-regexp@npm:^1.0.5":
|
||||
version: 1.0.5
|
||||
resolution: "escape-string-regexp@npm:1.0.5"
|
||||
@ -19436,13 +19643,6 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"escape-string-regexp@npm:^5.0.0":
|
||||
version: 5.0.0
|
||||
resolution: "escape-string-regexp@npm:5.0.0"
|
||||
checksum: 20daabe197f3cb198ec28546deebcf24b3dbb1a5a269184381b3116d12f0532e06007f4bc8da25669d6a7f8efb68db0758df4cd981f57bc5b57f521a3e12c59e
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"escodegen@npm:^2.0.0, escodegen@npm:^2.1.0":
|
||||
version: 2.1.0
|
||||
resolution: "escodegen@npm:2.1.0"
|
||||
@ -19809,7 +20009,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"esutils@npm:^2.0.2":
|
||||
"esutils@npm:^2.0.2, esutils@npm:^2.0.3":
|
||||
version: 2.0.3
|
||||
resolution: "esutils@npm:2.0.3"
|
||||
checksum: 22b5b08f74737379a840b8ed2036a5fb35826c709ab000683b092d9054e5c2a82c27818f12604bfc2a9a76b90b6834ef081edbc1c7ae30d1627012e067c6ec87
|
||||
@ -20123,7 +20323,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"fast-diff@npm:^1.1.2":
|
||||
"fast-diff@npm:^1.1.2, fast-diff@npm:^1.2.0":
|
||||
version: 1.3.0
|
||||
resolution: "fast-diff@npm:1.3.0"
|
||||
checksum: d22d371b994fdc8cce9ff510d7b8dc4da70ac327bcba20df607dd5b9cae9f908f4d1028f5fe467650f058d1e7270235ae0b8230809a262b4df587a3b3aa216c3
|
||||
@ -20571,6 +20771,16 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"find-up@npm:^6.0.0":
|
||||
version: 6.3.0
|
||||
resolution: "find-up@npm:6.3.0"
|
||||
dependencies:
|
||||
locate-path: ^7.1.0
|
||||
path-exists: ^5.0.0
|
||||
checksum: 9a21b7f9244a420e54c6df95b4f6fc3941efd3c3e5476f8274eb452f6a85706e7a6a90de71353ee4f091fcb4593271a6f92810a324ec542650398f928783c280
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"flat-cache@npm:^3.0.4":
|
||||
version: 3.1.0
|
||||
resolution: "flat-cache@npm:3.1.0"
|
||||
@ -22325,6 +22535,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"ignore-by-default@npm:^2.1.0":
|
||||
version: 2.1.0
|
||||
resolution: "ignore-by-default@npm:2.1.0"
|
||||
checksum: 2b2df4622b6a07a3e91893987be8f060dc553f7736b67e72aa2312041c450a6fa8371733d03c42f45a02e47ec824e961c2fba63a3d94fc59cbd669220a5b0d7a
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"ignore@npm:^5.0.4, ignore@npm:^5.2.0, ignore@npm:^5.2.4":
|
||||
version: 5.2.4
|
||||
resolution: "ignore@npm:5.2.4"
|
||||
@ -22415,6 +22632,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"indent-string@npm:^5.0.0":
|
||||
version: 5.0.0
|
||||
resolution: "indent-string@npm:5.0.0"
|
||||
checksum: e466c27b6373440e6d84fbc19e750219ce25865cb82d578e41a6053d727e5520dc5725217d6eb1cc76005a1bb1696a0f106d84ce7ebda3033b963a38583fb3b3
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"indexes-of@npm:^1.0.1":
|
||||
version: 1.0.1
|
||||
resolution: "indexes-of@npm:1.0.1"
|
||||
@ -22560,6 +22784,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"irregular-plurals@npm:^3.3.0":
|
||||
version: 3.5.0
|
||||
resolution: "irregular-plurals@npm:3.5.0"
|
||||
checksum: 5b663091dc89155df7b2e9d053e8fb11941a0c4be95c4b6549ed3ea020489fdf4f75ea586c915b5b543704252679a5a6e8c6c3587da5ac3fc57b12da90a9aee7
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"is-absolute-url@npm:^3.0.0":
|
||||
version: 3.0.3
|
||||
resolution: "is-absolute-url@npm:3.0.3"
|
||||
@ -22688,6 +22919,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"is-error@npm:^2.2.2":
|
||||
version: 2.2.2
|
||||
resolution: "is-error@npm:2.2.2"
|
||||
checksum: a97b39587150f0d38f9f93f64699807fe3020fe5edbd63548f234dc2ba96fd7c776d66c062bf031dfeb93c7f48db563ff6bde588418ca041da37c659a416f055
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"is-extglob@npm:^2.1.1":
|
||||
version: 2.1.1
|
||||
resolution: "is-extglob@npm:2.1.1"
|
||||
@ -22869,6 +23107,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"is-promise@npm:^4.0.0":
|
||||
version: 4.0.0
|
||||
resolution: "is-promise@npm:4.0.0"
|
||||
checksum: 0b46517ad47b00b6358fd6553c83ec1f6ba9acd7ffb3d30a0bf519c5c69e7147c132430452351b8a9fc198f8dd6c4f76f8e6f5a7f100f8c77d57d9e0f4261a8a
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"is-regexp@npm:^1.0.0":
|
||||
version: 1.0.0
|
||||
resolution: "is-regexp@npm:1.0.0"
|
||||
@ -23940,6 +24185,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"js-string-escape@npm:^1.0.1":
|
||||
version: 1.0.1
|
||||
resolution: "js-string-escape@npm:1.0.1"
|
||||
checksum: f11e0991bf57e0c183b55c547acec85bd2445f043efc9ea5aa68b41bd2a3e7d3ce94636cb233ae0d84064ba4c1a505d32e969813c5b13f81e7d4be12c59256fe
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"js-tiktoken@npm:^1.0.7":
|
||||
version: 1.0.7
|
||||
resolution: "js-tiktoken@npm:1.0.7"
|
||||
@ -23967,7 +24219,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"js-yaml@npm:^3.10.0, js-yaml@npm:^3.13.1":
|
||||
"js-yaml@npm:^3.10.0, js-yaml@npm:^3.13.1, js-yaml@npm:^3.14.1":
|
||||
version: 3.14.1
|
||||
resolution: "js-yaml@npm:3.14.1"
|
||||
dependencies:
|
||||
@ -24802,6 +25054,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"load-json-file@npm:^7.0.0":
|
||||
version: 7.0.1
|
||||
resolution: "load-json-file@npm:7.0.1"
|
||||
checksum: a560288da6891778321ef993e4bdbdf05374a4f3a3aeedd5ba6b64672798c830d748cfc59a2ec9891a3db30e78b3d04172e0dcb0d4828168289a393147ca0e74
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"loader-runner@npm:^4.1.0, loader-runner@npm:^4.2.0":
|
||||
version: 4.3.0
|
||||
resolution: "loader-runner@npm:4.3.0"
|
||||
@ -24865,6 +25124,15 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"locate-path@npm:^7.1.0":
|
||||
version: 7.2.0
|
||||
resolution: "locate-path@npm:7.2.0"
|
||||
dependencies:
|
||||
p-locate: ^6.0.0
|
||||
checksum: c1b653bdf29beaecb3d307dfb7c44d98a2a98a02ebe353c9ad055d1ac45d6ed4e1142563d222df9b9efebc2bcb7d4c792b507fad9e7150a04c29530b7db570f8
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"lodash-es@npm:^4.17.21":
|
||||
version: 4.17.21
|
||||
resolution: "lodash-es@npm:4.17.21"
|
||||
@ -25374,7 +25642,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"map-age-cleaner@npm:^0.1.1":
|
||||
"map-age-cleaner@npm:^0.1.1, map-age-cleaner@npm:^0.1.3":
|
||||
version: 0.1.3
|
||||
resolution: "map-age-cleaner@npm:0.1.3"
|
||||
dependencies:
|
||||
@ -25474,6 +25742,24 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"matcher@npm:^5.0.0":
|
||||
version: 5.0.0
|
||||
resolution: "matcher@npm:5.0.0"
|
||||
dependencies:
|
||||
escape-string-regexp: ^5.0.0
|
||||
checksum: 28f191c2d23fee0f6f32fd0181d9fe173b0ab815a919edba55605438a2f9fa40372e002574a1b17add981b0a8669c75bc6194318d065ed2dceffd8b160c38118
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"md5-hex@npm:^3.0.1":
|
||||
version: 3.0.1
|
||||
resolution: "md5-hex@npm:3.0.1"
|
||||
dependencies:
|
||||
blueimp-md5: ^2.10.0
|
||||
checksum: 6799a19e8bdd3e0c2861b94c1d4d858a89220488d7885c1fa236797e367d0c2e5f2b789e05309307083503f85be3603a9686a5915568a473137d6b4117419cc2
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"md5@npm:^2.2.1, md5@npm:^2.3.0":
|
||||
version: 2.3.0
|
||||
resolution: "md5@npm:2.3.0"
|
||||
@ -25542,6 +25828,16 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"mem@npm:^9.0.2":
|
||||
version: 9.0.2
|
||||
resolution: "mem@npm:9.0.2"
|
||||
dependencies:
|
||||
map-age-cleaner: ^0.1.3
|
||||
mimic-fn: ^4.0.0
|
||||
checksum: 07829bb182af0e3ecf748dc2edb1c3b10a256ef10458f7e24d06561a2adc2b3ef34d14abe81678bbcedb46faa477e7370223f118b1a5e1252da5fe43496f3967
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"memfs@npm:^3.4.3":
|
||||
version: 3.5.3
|
||||
resolution: "memfs@npm:3.5.3"
|
||||
@ -26108,7 +26404,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"ms@npm:2.1.3, ms@npm:^2.0.0, ms@npm:^2.1.1":
|
||||
"ms@npm:2.1.3, ms@npm:^2.0.0, ms@npm:^2.1.1, ms@npm:^2.1.3":
|
||||
version: 2.1.3
|
||||
resolution: "ms@npm:2.1.3"
|
||||
checksum: aa92de608021b242401676e35cfa5aa42dd70cbdc082b916da7fb925c542173e36bce97ea3e804923fe92c0ad991434e4a38327e15a1b5b5f945d66df615ae6d
|
||||
@ -26598,6 +26894,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"nofilter@npm:^3.1.0":
|
||||
version: 3.1.0
|
||||
resolution: "nofilter@npm:3.1.0"
|
||||
checksum: 58aa85a5b4b35cbb6e42de8a8591c5e338061edc9f3e7286f2c335e9e9b9b8fa7c335ae45daa8a1f3433164dc0b9a3d187fa96f9516e04a17a1f9ce722becc4f
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"nopt@npm:^6.0.0":
|
||||
version: 6.0.0
|
||||
resolution: "nopt@npm:6.0.0"
|
||||
@ -27185,6 +27488,15 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"p-event@npm:^5.0.1":
|
||||
version: 5.0.1
|
||||
resolution: "p-event@npm:5.0.1"
|
||||
dependencies:
|
||||
p-timeout: ^5.0.2
|
||||
checksum: 3bdd8df6092e6b149f25e9c2eb1c0843b3b4279b07be2a2c72c02b65b267a8908c2040fefd606f2497b0f2bcefcd214f8ca5a74f0c883515d400ccf1d88d5683
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"p-finally@npm:^1.0.0":
|
||||
version: 1.0.0
|
||||
resolution: "p-finally@npm:1.0.0"
|
||||
@ -27271,6 +27583,15 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"p-locate@npm:^6.0.0":
|
||||
version: 6.0.0
|
||||
resolution: "p-locate@npm:6.0.0"
|
||||
dependencies:
|
||||
p-limit: ^4.0.0
|
||||
checksum: 2bfe5234efa5e7a4e74b30a5479a193fdd9236f8f6b4d2f3f69e3d286d9a7d7ab0c118a2a50142efcf4e41625def635bd9332d6cbf9cc65d85eb0718c579ab38
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"p-map@npm:^3.0.0":
|
||||
version: 3.0.0
|
||||
resolution: "p-map@npm:3.0.0"
|
||||
@ -27289,6 +27610,15 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"p-map@npm:^5.5.0":
|
||||
version: 5.5.0
|
||||
resolution: "p-map@npm:5.5.0"
|
||||
dependencies:
|
||||
aggregate-error: ^4.0.0
|
||||
checksum: 065cb6fca6b78afbd070dd9224ff160dc23eea96e57863c09a0c8ea7ce921043f76854be7ee0abc295cff1ac9adcf700e79a1fbe3b80b625081087be58e7effb
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"p-queue@npm:^6.6.2":
|
||||
version: 6.6.2
|
||||
resolution: "p-queue@npm:6.6.2"
|
||||
@ -27318,6 +27648,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"p-timeout@npm:^5.0.2":
|
||||
version: 5.1.0
|
||||
resolution: "p-timeout@npm:5.1.0"
|
||||
checksum: f5cd4e17301ff1ff1d8dbf2817df0ad88c6bba99349fc24d8d181827176ad4f8aca649190b8a5b1a428dfd6ddc091af4606835d3e0cb0656e04045da5c9e270c
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"p-try@npm:^1.0.0":
|
||||
version: 1.0.0
|
||||
resolution: "p-try@npm:1.0.0"
|
||||
@ -27446,6 +27783,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"parse-ms@npm:^3.0.0":
|
||||
version: 3.0.0
|
||||
resolution: "parse-ms@npm:3.0.0"
|
||||
checksum: fc602bba093835562321a67a9d6c8c9687ca4f26a09459a77e07ebd7efddd1a5766725ec60eb0c83a2abe67f7a23808f7deb1c1226727776eaf7f9607ae09db2
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"parse-passwd@npm:^1.0.0":
|
||||
version: 1.0.0
|
||||
resolution: "parse-passwd@npm:1.0.0"
|
||||
@ -27513,6 +27857,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"path-exists@npm:^5.0.0":
|
||||
version: 5.0.0
|
||||
resolution: "path-exists@npm:5.0.0"
|
||||
checksum: 8ca842868cab09423994596eb2c5ec2a971c17d1a3cb36dbf060592c730c725cd524b9067d7d2a1e031fef9ba7bd2ac6dc5ec9fb92aa693265f7be3987045254
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"path-is-absolute@npm:^1.0.0":
|
||||
version: 1.0.1
|
||||
resolution: "path-is-absolute@npm:1.0.1"
|
||||
@ -27745,6 +28096,16 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"pkg-conf@npm:^4.0.0":
|
||||
version: 4.0.0
|
||||
resolution: "pkg-conf@npm:4.0.0"
|
||||
dependencies:
|
||||
find-up: ^6.0.0
|
||||
load-json-file: ^7.0.0
|
||||
checksum: 6da0c064a74f6c7ae80d7d68c5853e14f7e762a2a80c6ca9e0aa827002b90b69c86fefe3bac830b10a6f1739e7f96a1f728637f2a141e50b0fdafe92a2c3eab6
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"pkg-dir@npm:^3.0.0":
|
||||
version: 3.0.0
|
||||
resolution: "pkg-dir@npm:3.0.0"
|
||||
@ -27814,6 +28175,15 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"plur@npm:^5.1.0":
|
||||
version: 5.1.0
|
||||
resolution: "plur@npm:5.1.0"
|
||||
dependencies:
|
||||
irregular-plurals: ^3.3.0
|
||||
checksum: 57e400dc4b926768fb0abab7f8688fe17e85673712134546e7beaaee188bae7e0504976e847d7e41d0d6103ff2fd61204095f03c2a45de19a8bad15aecb45cc1
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"pluralize@npm:^8.0.0":
|
||||
version: 8.0.0
|
||||
resolution: "pluralize@npm:8.0.0"
|
||||
@ -28519,6 +28889,15 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"pretty-ms@npm:^8.0.0":
|
||||
version: 8.0.0
|
||||
resolution: "pretty-ms@npm:8.0.0"
|
||||
dependencies:
|
||||
parse-ms: ^3.0.0
|
||||
checksum: b7d2a8182887af0e5ab93f9df331f10db9b8eda86855e2de115eb01a6c501bde5631a8813b1b0abdd7d045e79b08ae875369a8fd279a3dacd6d9e572bdd3bfa6
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"pretty-time@npm:^1.1.0":
|
||||
version: 1.1.0
|
||||
resolution: "pretty-time@npm:1.1.0"
|
||||
@ -31184,7 +31563,7 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"stack-utils@npm:^2.0.3":
|
||||
"stack-utils@npm:^2.0.3, stack-utils@npm:^2.0.6":
|
||||
version: 2.0.6
|
||||
resolution: "stack-utils@npm:2.0.6"
|
||||
dependencies:
|
||||
@ -31725,6 +32104,18 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"supertap@npm:^3.0.1":
|
||||
version: 3.0.1
|
||||
resolution: "supertap@npm:3.0.1"
|
||||
dependencies:
|
||||
indent-string: ^5.0.0
|
||||
js-yaml: ^3.14.1
|
||||
serialize-error: ^7.0.1
|
||||
strip-ansi: ^7.0.1
|
||||
checksum: ee3d71c1d25f7f15d4a849e72b0c5f430df7cd8f702cf082fdbec5642a9546be6557766745655fa3a3e9c88f7c7eed849f2d74457b5b72cb9d94a779c0c8a948
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"supertest@npm:^6.3.3":
|
||||
version: 6.3.3
|
||||
resolution: "supertest@npm:6.3.3"
|
||||
@ -32001,6 +32392,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"temp-dir@npm:^3.0.0":
|
||||
version: 3.0.0
|
||||
resolution: "temp-dir@npm:3.0.0"
|
||||
checksum: 577211e995d1d584dd60f1469351d45e8a5b4524e4a9e42d3bdd12cfde1d0bb8f5898311bef24e02aaafb69514c1feb58c7b4c33dcec7129da3b0861a4ca935b
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"temp@npm:^0.8.4":
|
||||
version: 0.8.4
|
||||
resolution: "temp@npm:0.8.4"
|
||||
@ -32201,6 +32599,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"time-zone@npm:^1.0.0":
|
||||
version: 1.0.0
|
||||
resolution: "time-zone@npm:1.0.0"
|
||||
checksum: e46f5a69b8c236dcd8e91e29d40d4e7a3495ed4f59888c3f84ce1d9678e20461421a6ba41233509d47dd94bc18f1a4377764838b21b584663f942b3426dcbce8
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"tiny-each-async@npm:2.0.3":
|
||||
version: 2.0.3
|
||||
resolution: "tiny-each-async@npm:2.0.3"
|
||||
@ -34039,6 +34444,13 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"well-known-symbols@npm:^2.0.0":
|
||||
version: 2.0.0
|
||||
resolution: "well-known-symbols@npm:2.0.0"
|
||||
checksum: 4f54bbc3012371cb4d228f436891b8e7536d34ac61a57541890257e96788608e096231e0121ac24d08ef2f908b3eb2dc0adba35023eaeb2a7df655da91415402
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"whatwg-encoding@npm:^2.0.0":
|
||||
version: 2.0.0
|
||||
resolution: "whatwg-encoding@npm:2.0.0"
|
||||
@ -34313,6 +34725,16 @@ __metadata:
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"write-file-atomic@npm:^5.0.1":
|
||||
version: 5.0.1
|
||||
resolution: "write-file-atomic@npm:5.0.1"
|
||||
dependencies:
|
||||
imurmurhash: ^0.1.4
|
||||
signal-exit: ^4.0.1
|
||||
checksum: 8dbb0e2512c2f72ccc20ccedab9986c7d02d04039ed6e8780c987dc4940b793339c50172a1008eed7747001bfacc0ca47562668a069a7506c46c77d7ba3926a9
|
||||
languageName: node
|
||||
linkType: hard
|
||||
|
||||
"ws@npm:8.13.0, ws@npm:^8.11.0, ws@npm:^8.12.0, ws@npm:^8.13.0, ws@npm:^8.2.3":
|
||||
version: 8.13.0
|
||||
resolution: "ws@npm:8.13.0"
|
||||
|
Loading…
Reference in New Issue
Block a user