feat(knex): initial knexjs migration

Co-authored-by: Philip Molares <philip.molares@udo.edu>
Signed-off-by: Erik Michelson <github@erik.michelson.eu>
Signed-off-by: Philip Molares <philip.molares@udo.edu>
This commit is contained in:
Erik Michelson 2025-03-13 01:29:47 +01:00
parent a9183e82bf
commit b696c1e661
No known key found for this signature in database
GPG key ID: DB99ADDDC5C0AF82
24 changed files with 1066 additions and 185 deletions

View file

@ -38,14 +38,25 @@ module.exports = {
], ],
}, },
}, },
{
files: ['src/database/**'],
rules: {
'@typescript-eslint/naming-convention': 'off',
},
},
],
plugins: [
'@typescript-eslint',
'jest',
'eslint-plugin-local-rules',
'@darraghor/nestjs-typed',
], ],
plugins: ['@typescript-eslint', 'jest', 'eslint-plugin-local-rules','@darraghor/nestjs-typed'],
extends: [ extends: [
'eslint:recommended', 'eslint:recommended',
'plugin:@typescript-eslint/recommended', 'plugin:@typescript-eslint/recommended',
'plugin:@typescript-eslint/recommended-requiring-type-checking', 'plugin:@typescript-eslint/recommended-requiring-type-checking',
'plugin:prettier/recommended', 'plugin:prettier/recommended',
'plugin:@darraghor/nestjs-typed/recommended' 'plugin:@darraghor/nestjs-typed/recommended',
], ],
root: true, root: true,
env: { env: {

21
backend/knexfile.ts Normal file
View file

@ -0,0 +1,21 @@
/*
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import type { Knex } from 'knex';
/** This is used for the Knex CLI to create migrations during development */
const config: { [key: string]: Knex.Config } = {
development: {
client: 'better-sqlite3',
connection: {
filename: './hedgedoc.sqlite',
},
migrations: {
directory: './src/database/migrations',
},
},
};
module.exports = config;

View file

@ -21,8 +21,7 @@
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
"test:e2e": "jest --config jest-e2e.json && rimraf test_uploads*", "test:e2e": "jest --config jest-e2e.json && rimraf test_uploads*",
"test:e2e:ci": "jest --config jest-e2e.json --coverage && rimraf test_uploads*", "test:e2e:ci": "jest --config jest-e2e.json --coverage && rimraf test_uploads*",
"seed": "ts-node src/seed.ts", "knex": "tsx ../node_modules/.bin/knex --migrations-directory src/database/migrations --knexfile ./knexfile.ts"
"typeorm": "typeorm-ts-node-commonjs -d src/ormconfig.ts"
}, },
"dependencies": { "dependencies": {
"@azure/storage-blob": "12.25.0", "@azure/storage-blob": "12.25.0",
@ -108,8 +107,8 @@
"supertest": "6.3.4", "supertest": "6.3.4",
"ts-jest": "29.2.5", "ts-jest": "29.2.5",
"ts-mockery": "1.2.0", "ts-mockery": "1.2.0",
"ts-node": "11.0.0-beta.1",
"tsconfig-paths": "4.2.0", "tsconfig-paths": "4.2.0",
"tsx": "^4.19.3",
"typescript": "5.6.3" "typescript": "5.6.3"
}, },
"jest": { "jest": {

View file

@ -76,6 +76,9 @@ const routes: Routes = [
deprecate: knexLoggerService.deprecate.bind(knexLoggerService), deprecate: knexLoggerService.deprecate.bind(knexLoggerService),
debug: knexLoggerService.debug.bind(knexLoggerService), debug: knexLoggerService.debug.bind(knexLoggerService),
}, },
migrations: {
directory: 'src/database/migrations/',
},
}, },
}), }),
}), }),

View file

@ -1,32 +0,0 @@
/*
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { ProviderType } from '../auth/provider-type.enum';
/**
* An auth identity holds the information how a {@link User} can authenticate themself using a certain auth provider
*/
export interface Identity {
/** The id of the user */
userId: number;
/** The type of the auth provider */
providerType: ProviderType;
/** The identifier of the auth provider, e.g. gitlab */
providerIdentifier: string | null;
/** Timestamp when this identity was created */
createdAt: Date;
/** Timestamp when this identity was last updated */
updatedAt: Date;
/** The remote id of the user at the auth provider or null for local identities */
providerUserId: string | null;
/** The hashed password for local identities or null for other auth providers */
passwordHash: string | null;
}

View file

@ -1,83 +0,0 @@
/*
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { Knex } from 'knex';
import { Alias } from './alias';
import { ApiToken } from './api-token';
import { AuthorshipInfo } from './authorship-info';
import { Group } from './group';
import { GroupUser } from './group-user';
import { Identity } from './identity';
import { MediaUpload } from './media-upload';
import { Note } from './note';
import { NoteGroupPermission } from './note-group-permission';
import { NoteUserPermission } from './note-user-permission';
import { Revision } from './revision';
import { RevisionTag } from './revision-tag';
import { User } from './user';
import { UserPinnedNote } from './user-pinned-note';
/* eslint-disable @typescript-eslint/naming-convention */
declare module 'knex/types/tables' {
interface Tables {
alias_composite: Knex.CompositeTableType<
Alias,
Alias,
Pick<Alias, 'isPrimary'>
>;
api_token_composite: Knex.CompositeTableType<
ApiToken,
Omit<ApiToken, 'createdAt' | 'lastUsedAt'>,
Pick<ApiToken, 'lastUsedAt'>
>;
authorship_info_composite: Knex.CompositeTableType<
AuthorshipInfo,
Omit<AuthorshipInfo, 'createdAt'>
>;
group_composite: Knex.CompositeTableType<
Group,
Omit<Group, 'id'>,
Pick<Group, 'name' | 'displayName'>
>;
group_user_composite: Knex.CompositeTableType<GroupUser>;
identity_composite: Knex.CompositeTableType<
Identity,
Omit<Identity, 'createdAt'>,
Pick<Identity, 'passwordHash' | 'updatedAt'>
>;
media_upload_composite: Knex.CompositeTableType<
MediaUpload,
Omit<MediaUpload, 'createdAt' | 'uuid'>,
Pick<MediaUpload, 'noteId'>
>;
note_composite: Knex.CompositeTableType<
Note,
Omit<Note, 'createdAt' | 'id'>,
Pick<Note, 'ownerId'>
>;
note_group_permission_composite: Knex.CompositeTableType<
NoteGroupPermission,
NoteGroupPermission,
Pick<NoteGroupPermission, 'canEdit'>
>;
note_user_permission_composite: Knex.CompositeTableType<
NoteUserPermission,
NoteUserPermission,
Pick<NoteUserPermission, 'canEdit'>
>;
revision_composite: Knex.CompositeTableType<
Revision,
Omit<Revision, 'createdAt' | 'id'>
>;
revision_tag_composite: Knex.CompositeTableType<RevisionTag>;
user_composite: Knex.CompositeTableType<
User,
Omit<User, 'id' | 'createdAt'>,
Pick<User, 'displayName' | 'photoUrl' | 'email' | 'authorStyle'>
>;
user_pinned_note_composite: Knex.CompositeTableType<UserPinnedNote>;
}
}

View file

@ -0,0 +1,351 @@
/*
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { NoteType } from '@hedgedoc/commons';
import type { Knex } from 'knex';
import { ProviderType } from '../../auth/provider-type.enum';
import { SpecialGroup } from '../../groups/groups.special';
import { BackendType } from '../../media/backends/backend-type.enum';
import {
FieldNameAlias,
FieldNameApiToken,
FieldNameAuthorshipInfo,
FieldNameGroup,
FieldNameGroupUser,
FieldNameIdentity,
FieldNameMediaUpload,
FieldNameNote,
FieldNameNoteGroupPermission,
FieldNameNoteUserPermission,
FieldNameRevision,
FieldNameRevisionTag,
FieldNameUser,
FieldNameUserPinnedNote,
TableAlias,
TableApiToken,
TableAuthorshipInfo,
TableGroup,
TableGroupUser,
TableIdentity,
TableMediaUpload,
TableNote,
TableNoteGroupPermission,
TableNoteUserPermission,
TableRevision,
TableRevisionTag,
TableUser,
TableUserPinnedNote,
} from '../types';
export async function up(knex: Knex): Promise<void> {
// Create user table first as it's referenced by other tables
await knex.schema.createTable(TableUser, (table) => {
table.increments(FieldNameUser.id).primary();
table.string(FieldNameUser.username).nullable().unique();
table.string(FieldNameUser.displayName).nullable();
table.string(FieldNameUser.photoUrl).nullable();
table.string(FieldNameUser.email).nullable();
table.integer(FieldNameUser.authorStyle).notNullable();
table.uuid(FieldNameUser.guestUuid).nullable().unique();
table.timestamp(FieldNameUser.createdAt).defaultTo(knex.fn.now());
});
// Create group table
await knex.schema.createTable(TableGroup, (table) => {
table.increments(FieldNameGroup.id).primary();
table.string(FieldNameGroup.name).notNullable();
table.string(FieldNameGroup.displayName).notNullable();
table.boolean(FieldNameGroup.isSpecial).notNullable().defaultTo(false);
});
// Create special groups _EVERYONE and _LOGGED_IN
await knex(TableGroup).insert([
{
name: SpecialGroup.EVERYONE,
display_name: SpecialGroup.EVERYONE,
is_special: true,
},
{
name: SpecialGroup.LOGGED_IN,
display_name: SpecialGroup.EVERYONE,
is_special: true,
},
]);
// Create note table
await knex.schema.createTable(TableNote, (table) => {
table.increments(FieldNameNote.id).primary();
table.integer(FieldNameNote.version).notNullable().defaultTo(2);
table.timestamp(FieldNameNote.createdAt).defaultTo(knex.fn.now());
table
.integer(FieldNameNote.ownerId)
.unsigned()
.notNullable()
.references(FieldNameUser.id)
.inTable(TableUser);
});
// Create alias table
await knex.schema.createTable(TableAlias, (table) => {
table.comment(
'Stores aliases of notes, only on alias per note can be is_primary == true, all other need to have is_primary == null ',
);
table.string(FieldNameAlias.alias).primary();
table
.integer(FieldNameAlias.noteId)
.unsigned()
.notNullable()
.references(FieldNameNote.id)
.inTable(TableNote);
table.boolean(FieldNameAlias.isPrimary).nullable();
table.unique([FieldNameAlias.noteId, FieldNameAlias.isPrimary], {
indexName: 'only_one_note_can_be_primary',
useConstraint: true,
});
});
// Create api_token table
await knex.schema.createTable(TableApiToken, (table) => {
table.string(FieldNameApiToken.id).primary();
table
.integer(FieldNameApiToken.userId)
.unsigned()
.notNullable()
.references(FieldNameUser.id)
.inTable(TableUser);
table.string(FieldNameApiToken.label).notNullable();
table.string(FieldNameApiToken.secretHash).notNullable();
table.timestamp(FieldNameApiToken.validUntil).notNullable();
table.timestamp(FieldNameApiToken.lastUsedAt).nullable();
});
// Create identity table
await knex.schema.createTable(TableIdentity, (table) => {
table
.integer(FieldNameIdentity.userId)
.unsigned()
.notNullable()
.references(FieldNameUser.id)
.inTable(TableUser);
table.enu(
FieldNameIdentity.providerType,
[ProviderType.LDAP, ProviderType.LOCAL, ProviderType.OIDC], // ProviderType.GUEST is not relevant for the DB
{
useNative: true,
enumName: FieldNameIdentity.providerType,
},
);
table.string(FieldNameIdentity.providerIdentifier).nullable();
table.string(FieldNameIdentity.providerUserId).nullable();
table.string(FieldNameIdentity.passwordHash).nullable();
table.timestamp(FieldNameIdentity.createdAt).defaultTo(knex.fn.now());
table.timestamp(FieldNameIdentity.updatedAt).defaultTo(knex.fn.now());
table.unique(
[
FieldNameIdentity.userId,
FieldNameIdentity.providerType,
FieldNameIdentity.providerIdentifier,
],
{
indexName: 'each_user_has_only_one_account_per_provider',
useConstraint: true,
},
);
});
// Create group_user join table
await knex.schema.createTable(TableGroupUser, (table) => {
table
.integer(FieldNameGroupUser.userId)
.unsigned()
.notNullable()
.references(FieldNameUser.id)
.inTable(TableUser);
table
.integer(FieldNameGroupUser.groupId)
.unsigned()
.notNullable()
.references(FieldNameGroup.id)
.inTable(TableGroup);
table.primary([FieldNameGroupUser.userId, FieldNameGroupUser.groupId]);
});
// Create revision table
await knex.schema.createTable(TableRevision, (table) => {
table.increments(FieldNameRevision.id).primary();
table
.integer(FieldNameRevision.noteId)
.unsigned()
.notNullable()
.references(FieldNameNote.id)
.inTable(TableNote);
table.text(FieldNameRevision.patch).notNullable();
table.text(FieldNameRevision.content).notNullable();
table.string(FieldNameRevision.title).notNullable();
table.text(FieldNameRevision.description).notNullable();
table.binary(FieldNameRevision.yjsStateVector).nullable();
table.enu(FieldNameRevision.noteType, [NoteType.DOCUMENT, NoteType.SLIDE], {
useNative: true,
enumName: FieldNameRevision.noteType,
});
table.timestamp(FieldNameRevision.createdAt).defaultTo(knex.fn.now());
});
// Create revision_tag table
await knex.schema.createTable(TableRevisionTag, (table) => {
table
.integer(FieldNameRevisionTag.revisionId)
.unsigned()
.notNullable()
.references(FieldNameRevision.id)
.inTable(TableRevision);
table.string(FieldNameRevisionTag.tag).notNullable();
table.primary([FieldNameRevisionTag.revisionId, FieldNameRevisionTag.tag]);
});
// Create authorship_info table
await knex.schema.createTable(TableAuthorshipInfo, (table) => {
table
.integer(FieldNameAuthorshipInfo.revisionId)
.unsigned()
.notNullable()
.references(FieldNameRevision.id)
.inTable(TableRevision);
table
.integer(FieldNameAuthorshipInfo.authorId)
.unsigned()
.notNullable()
.references(FieldNameUser.id)
.inTable(TableUser);
table
.integer(FieldNameAuthorshipInfo.startPosition)
.unsigned()
.notNullable();
table.integer(FieldNameAuthorshipInfo.endPosition).unsigned().notNullable();
});
// Create note_user_permission table
await knex.schema.createTable(TableNoteUserPermission, (table) => {
table
.integer(FieldNameNoteUserPermission.noteId)
.unsigned()
.notNullable()
.references(FieldNameNote.id)
.inTable(TableNote);
table
.integer(FieldNameNoteUserPermission.userId)
.unsigned()
.notNullable()
.references(FieldNameUser.id)
.inTable(TableUser);
table
.boolean(FieldNameNoteUserPermission.canEdit)
.notNullable()
.defaultTo(false);
table.primary([
FieldNameNoteUserPermission.noteId,
FieldNameNoteUserPermission.userId,
]);
});
// Create note_group_permission table
await knex.schema.createTable(TableNoteGroupPermission, (table) => {
table
.integer(FieldNameNoteGroupPermission.noteId)
.unsigned()
.notNullable()
.references(FieldNameNote.id)
.inTable(TableNote);
table
.integer(FieldNameNoteGroupPermission.groupId)
.unsigned()
.notNullable()
.references(FieldNameGroup.id)
.inTable(TableGroup);
table
.boolean(FieldNameNoteGroupPermission.canEdit)
.notNullable()
.defaultTo(false);
table.primary([
FieldNameNoteGroupPermission.noteId,
FieldNameNoteGroupPermission.groupId,
]);
});
// Create media_upload table
await knex.schema.createTable(TableMediaUpload, (table) => {
table.uuid(FieldNameMediaUpload.uuid).primary();
table
.integer(FieldNameMediaUpload.noteId)
.unsigned()
.nullable()
.references(FieldNameNote.id)
.inTable(TableNote);
table
.integer(FieldNameMediaUpload.userId)
.unsigned()
.notNullable()
.references(FieldNameUser.id)
.inTable(TableUser);
table.string(FieldNameMediaUpload.fileName).notNullable();
table
.enu(
FieldNameMediaUpload.backendType,
[
BackendType.AZURE,
BackendType.FILESYSTEM,
BackendType.IMGUR,
BackendType.S3,
BackendType.WEBDAV,
],
{
useNative: true,
enumName: FieldNameMediaUpload.backendType,
},
)
.notNullable();
table.text(FieldNameMediaUpload.backendData).nullable();
table.timestamp(FieldNameMediaUpload.createdAt).defaultTo(knex.fn.now());
});
// Create user_pinned_note table
await knex.schema.createTable(TableUserPinnedNote, (table) => {
table
.integer(FieldNameUserPinnedNote.userId)
.unsigned()
.notNullable()
.references(FieldNameUser.id)
.inTable(TableUser);
table
.integer(FieldNameUserPinnedNote.noteId)
.unsigned()
.notNullable()
.references(FieldNameNote.id)
.inTable(TableNote);
table.primary([
FieldNameUserPinnedNote.userId,
FieldNameUserPinnedNote.noteId,
]);
});
}
export async function down(knex: Knex): Promise<void> {
// Drop tables in reverse order of creation to avoid integer key constraints
await knex.schema.dropTableIfExists(TableUserPinnedNote);
await knex.schema.dropTableIfExists(TableMediaUpload);
await knex.schema.dropTableIfExists(TableNoteGroupPermission);
await knex.schema.dropTableIfExists(TableNoteUserPermission);
await knex.schema.dropTableIfExists(TableAuthorshipInfo);
await knex.schema.dropTableIfExists(TableRevisionTag);
await knex.schema.dropTableIfExists(TableRevision);
await knex.schema.dropTableIfExists(TableGroupUser);
await knex.schema.dropTableIfExists(TableIdentity);
await knex.schema.dropTableIfExists(TableApiToken);
await knex.schema.dropTableIfExists(TableApiToken);
await knex.schema.dropTableIfExists(TableNote);
await knex.schema.dropTableIfExists(TableGroup);
await knex.schema.dropTableIfExists(TableUser);
}

View file

@ -12,11 +12,19 @@
*/ */
export interface Alias { export interface Alias {
/** The alias as defined by the user. Is unique. */ /** The alias as defined by the user. Is unique. */
alias: string; [FieldNameAlias.alias]: string;
/** The id of the associated {@link Note}. */ /** The id of the associated {@link Note}. */
noteId: number; [FieldNameAlias.noteId]: number;
/** Whether the alias is the primary one for the note. */ /** Whether the alias is the primary one for the note. */
isPrimary: boolean; [FieldNameAlias.isPrimary]: boolean;
} }
export enum FieldNameAlias {
alias = 'alias',
noteId = 'note_id',
isPrimary = 'is_primary',
}
export const TableAlias = 'alias';

View file

@ -11,20 +11,31 @@
*/ */
export interface ApiToken { export interface ApiToken {
/** The id of the token, a short random ASCII string. Is unique */ /** The id of the token, a short random ASCII string. Is unique */
id: string; [FieldNameApiToken.id]: string;
/** The {@link User} whose permissions the token has */ /** The {@link User} whose permissions the token has */
userId: number; [FieldNameApiToken.userId]: number;
/** The user-defined label for the token, such as "CLI" */ /** The user-defined label for the token, such as "CLI" */
label: string; [FieldNameApiToken.label]: string;
/** Hashed version of the token's secret */ /** Hashed version of the token's secret */
secretHash: string; [FieldNameApiToken.secretHash]: string;
/** Expiry date of the token */ /** Expiry date of the token */
validUntil: Date; [FieldNameApiToken.validUntil]: Date;
/** When the token was last used. When it was never used yet, this field is null */ /** When the token was last used. When it was never used yet, this field is null */
lastUsedAt: Date | null; [FieldNameApiToken.lastUsedAt]: Date | null;
} }
export enum FieldNameApiToken {
id = 'id',
userId = 'user_id',
label = 'label',
secretHash = 'secret_hash',
validUntil = 'valid_until',
lastUsedAt = 'last_used_at',
}
export const TableApiToken = 'api_token';

View file

@ -11,17 +11,23 @@
*/ */
export interface AuthorshipInfo { export interface AuthorshipInfo {
/** The id of the {@link Revision} this belongs to. */ /** The id of the {@link Revision} this belongs to. */
revisionId: number; [FieldNameAuthorshipInfo.revisionId]: number;
/** The id of the author of the edit. */ /** The id of the author of the edit. */
authorId: number; [FieldNameAuthorshipInfo.authorId]: number;
/** The start position of the change in the note as a positive index. */ /** The start position of the change in the note as a positive index. */
startPos: number; [FieldNameAuthorshipInfo.startPosition]: number;
/** The end position of the change in the note as a positive index. */ /** The end position of the change in the note as a positive index. */
endPos: number; [FieldNameAuthorshipInfo.endPosition]: number;
/** The creation datetime of the edit. */
createdAt: Date;
} }
export enum FieldNameAuthorshipInfo {
revisionId = 'revision_id',
authorId = 'author_id',
startPosition = 'start_position',
endPosition = 'end_position',
}
export const TableAuthorshipInfo = 'authorship_info';

View file

@ -9,8 +9,15 @@
*/ */
export interface GroupUser { export interface GroupUser {
/** The id of the {@link Group} a {@link User} is part of */ /** The id of the {@link Group} a {@link User} is part of */
groupId: number; [FieldNameGroupUser.groupId]: number;
/** The id of the {@link User} */ /** The id of the {@link User} */
userId: number; [FieldNameGroupUser.userId]: number;
} }
export enum FieldNameGroupUser {
groupId = 'group_id',
userId = 'user_id',
}
export const TableGroupUser = 'group_user';

View file

@ -11,14 +11,23 @@
*/ */
export interface Group { export interface Group {
/** The unique id for internal referencing */ /** The unique id for internal referencing */
id: number; [FieldNameGroup.id]: number;
/** The public identifier of the group (username for the group) */ /** The public identifier of the group (username for the group) */
name: string; [FieldNameGroup.name]: string;
/** The display name of the group */ /** The display name of the group */
displayName: string; [FieldNameGroup.displayName]: string;
/** Whether the group is one of the special groups */ /** Whether the group is one of the special groups */
isSpecial: boolean; [FieldNameGroup.isSpecial]: boolean;
} }
export enum FieldNameGroup {
id = 'id',
name = 'name',
displayName = 'display_name',
isSpecial = 'is_special',
}
export const TableGroup = 'group';

View file

@ -0,0 +1,44 @@
/*
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { ProviderType } from '../../auth/provider-type.enum';
/**
* An auth identity holds the information how a {@link User} can authenticate themselves using a certain auth provider
*/
export interface Identity {
/** The id of the user */
[FieldNameIdentity.userId]: number;
/** The type of the auth provider */
[FieldNameIdentity.providerType]: ProviderType;
/** The identifier of the auth provider, e.g. gitlab */
[FieldNameIdentity.providerIdentifier]: string | null;
/** Timestamp when this identity was created */
[FieldNameIdentity.createdAt]: Date;
/** Timestamp when this identity was last updated */
[FieldNameIdentity.updatedAt]: Date;
/** The remote id of the user at the auth provider or null for local identities */
[FieldNameIdentity.providerUserId]: string | null;
/** The hashed password for local identities or null for other auth providers */
[FieldNameIdentity.passwordHash]: string | null;
}
export enum FieldNameIdentity {
userId = 'user_id',
providerType = 'provider_type',
providerIdentifier = 'provider_identifier',
createdAt = 'created_at',
updatedAt = 'updated_at',
providerUserId = 'provider_user_id',
passwordHash = 'password_hash',
}
export const TableIdentity = 'identity';

View file

@ -0,0 +1,44 @@
/*
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
export { Alias, FieldNameAlias, TableAlias } from './alias';
export { ApiToken, FieldNameApiToken, TableApiToken } from './api-token';
export {
AuthorshipInfo,
FieldNameAuthorshipInfo,
TableAuthorshipInfo,
} from './authorship-info';
export { Group, FieldNameGroup, TableGroup } from './group';
export { GroupUser, FieldNameGroupUser, TableGroupUser } from './group-user';
export { Identity, FieldNameIdentity, TableIdentity } from './identity';
export {
MediaUpload,
FieldNameMediaUpload,
TableMediaUpload,
} from './media-upload';
export { Note, FieldNameNote, TableNote } from './note';
export {
NoteGroupPermission,
FieldNameNoteGroupPermission,
TableNoteGroupPermission,
} from './note-group-permission';
export {
NoteUserPermission,
FieldNameNoteUserPermission,
TableNoteUserPermission,
} from './note-user-permission';
export { Revision, FieldNameRevision, TableRevision } from './revision';
export {
RevisionTag,
FieldNameRevisionTag,
TableRevisionTag,
} from './revision-tag';
export { User, FieldNameUser, TableUser } from './user';
export {
UserPinnedNote,
FieldNameUserPinnedNote,
TableUserPinnedNote,
} from './user-pinned-note';

View file

@ -0,0 +1,118 @@
/*
* SPDX-FileCopyrightText: 2025 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { Knex } from 'knex';
import {
Alias,
ApiToken,
AuthorshipInfo,
FieldNameAlias,
FieldNameApiToken,
FieldNameGroup,
FieldNameIdentity,
FieldNameMediaUpload,
FieldNameNote,
FieldNameNoteGroupPermission,
FieldNameNoteUserPermission,
FieldNameRevision,
FieldNameUser,
Group,
GroupUser,
Identity,
MediaUpload,
Note,
NoteGroupPermission,
NoteUserPermission,
Revision,
RevisionTag,
TableAlias,
TableApiToken,
TableAuthorshipInfo,
TableGroup,
TableGroupUser,
TableIdentity,
TableMediaUpload,
TableNote,
TableNoteGroupPermission,
TableNoteUserPermission,
TableRevision,
TableRevisionTag,
TableUser,
TableUserPinnedNote,
User,
UserPinnedNote,
} from './index';
/* eslint-disable @typescript-eslint/naming-convention */
declare module 'knex/types/tables.js' {
interface Tables {
[TableAlias]: Knex.CompositeTableType<
Alias,
Alias,
Pick<Alias, FieldNameAlias.isPrimary>
>;
[TableApiToken]: Knex.CompositeTableType<
ApiToken,
Omit<ApiToken, FieldNameApiToken.lastUsedAt>,
Pick<ApiToken, FieldNameApiToken.lastUsedAt>
>;
[TableAuthorshipInfo]: AuthorshipInfo;
[TableGroup]: Knex.CompositeTableType<
Group,
Omit<Group, FieldNameGroup.id>,
Pick<Group, FieldNameGroup.name | FieldNameGroup.displayName>
>;
[TableGroupUser]: GroupUser;
[TableIdentity]: Knex.CompositeTableType<
Identity,
Omit<Identity, FieldNameIdentity.createdAt | FieldNameIdentity.updatedAt>,
Pick<
Identity,
FieldNameIdentity.passwordHash | FieldNameIdentity.updatedAt
>
>;
[TableMediaUpload]: Knex.CompositeTableType<
MediaUpload,
Omit<
MediaUpload,
FieldNameMediaUpload.createdAt | FieldNameMediaUpload.uuid
>,
Pick<MediaUpload, FieldNameMediaUpload.noteId>
>;
[TableNote]: Knex.CompositeTableType<
Note,
Omit<Note, FieldNameNote.createdAt | FieldNameNote.id>,
Pick<Note, FieldNameNote.ownerId>
>;
[TableNoteGroupPermission]: Knex.CompositeTableType<
NoteGroupPermission,
NoteGroupPermission,
Pick<NoteGroupPermission, FieldNameNoteGroupPermission.canEdit>
>;
[TableNoteUserPermission]: Knex.CompositeTableType<
NoteUserPermission,
NoteUserPermission,
Pick<NoteUserPermission, FieldNameNoteUserPermission.canEdit>
>;
[TableRevision]: Knex.CompositeTableType<
Revision,
Omit<Revision, FieldNameRevision.createdAt | FieldNameRevision.id>
>;
[TableRevisionTag]: RevisionTag;
[TableUser]: Knex.CompositeTableType<
User,
Omit<User, FieldNameUser.id | FieldNameUser.createdAt>,
Pick<
User,
| FieldNameUser.displayName
| FieldNameUser.photoUrl
| FieldNameUser.email
| FieldNameUser.authorStyle
>
>;
[TableUserPinnedNote]: UserPinnedNote;
}
}

View file

@ -3,7 +3,7 @@
* *
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
import { BackendType } from '../media/backends/backend-type.enum'; import { BackendType } from '../../media/backends/backend-type.enum';
/** /**
* A media upload object represents an uploaded file. While the file itself is stored in the configured storage backend, * A media upload object represents an uploaded file. While the file itself is stored in the configured storage backend,
@ -12,23 +12,35 @@ import { BackendType } from '../media/backends/backend-type.enum';
*/ */
export interface MediaUpload { export interface MediaUpload {
/** UUID (v7) identifying the media upload. Is public and unique */ /** UUID (v7) identifying the media upload. Is public and unique */
uuid: string; [FieldNameMediaUpload.uuid]: string;
/** The id of the attached {@link Note} or null if the media upload was detached from a note */ /** The id of the attached {@link Note} or null if the media upload was detached from a note */
noteId: number | null; [FieldNameMediaUpload.noteId]: number | null;
/** The id of the {@link User} who uploaded the media file */ /** The id of the {@link User} who uploaded the media file */
userId: number; [FieldNameMediaUpload.userId]: number;
/** The name of the uploaded file */ /** The name of the uploaded file */
fileName: string; [FieldNameMediaUpload.fileName]: string;
/** The backend where this upload is stored */ /** The backend where this upload is stored */
backendType: BackendType; [FieldNameMediaUpload.backendType]: BackendType;
/** Additional data required by the backend storage to identify the uploaded file */ /** Additional data required by the backend storage to identify the uploaded file */
backendData: string | null; [FieldNameMediaUpload.backendData]: string | null;
/** Timestamp when the file was uploaded */ /** Timestamp when the file was uploaded */
createdAt: Date; [FieldNameMediaUpload.createdAt]: Date;
} }
export enum FieldNameMediaUpload {
uuid = 'uuid',
noteId = 'note_id',
userId = 'user_id',
fileName = 'file_name',
backendType = 'backend_type',
backendData = 'backend_data',
createdAt = 'created_at',
}
export const TableMediaUpload = 'media_upload';

View file

@ -8,11 +8,19 @@
*/ */
export interface NoteGroupPermission { export interface NoteGroupPermission {
/** The id of the {@link Group} to give the {@link Note} permission to. */ /** The id of the {@link Group} to give the {@link Note} permission to. */
groupId: number; [FieldNameNoteGroupPermission.groupId]: number;
/** The id of the {@link Note} to give the {@link Group} permission to. */ /** The id of the {@link Note} to give the {@link Group} permission to. */
noteId: number; [FieldNameNoteGroupPermission.noteId]: number;
/** Whether the {@link Group} can edit the {@link Note} or not. */ /** Whether the {@link Group} can edit the {@link Note} or not. */
canEdit: boolean; [FieldNameNoteGroupPermission.canEdit]: boolean;
} }
export enum FieldNameNoteGroupPermission {
groupId = 'group_id',
noteId = 'note_id',
canEdit = 'can_edit',
}
export const TableNoteGroupPermission = 'note_group_permission';

View file

@ -8,11 +8,19 @@
*/ */
export interface NoteUserPermission { export interface NoteUserPermission {
/** The id of the {@link User} to give the {@link Note} permission to. */ /** The id of the {@link User} to give the {@link Note} permission to. */
noteId: number; [FieldNameNoteUserPermission.userId]: number;
/** The id of the {@link Note} to give the {@link User} permission to. */ /** The id of the {@link Note} to give the {@link User} permission to. */
userId: number; [FieldNameNoteUserPermission.noteId]: number;
/** Whether the {@link User} can edit the {@link Note} or not. */ /** Whether the {@link User} can edit the {@link Note} or not. */
canEdit: boolean; [FieldNameNoteUserPermission.canEdit]: boolean;
} }
export enum FieldNameNoteUserPermission {
userId = 'user_id',
noteId = 'note_id',
canEdit = 'can_edit',
}
export const TableNoteUserPermission = 'note_user_permission';

View file

@ -11,14 +11,23 @@
*/ */
export interface Note { export interface Note {
/** The unique id of the note for internal referencing */ /** The unique id of the note for internal referencing */
id: number; [FieldNameNote.id]: number;
/** The {@link User} id of the note owner */ /** The {@link User} id of the note owner */
ownerId: string; [FieldNameNote.ownerId]: number;
/** The HedgeDoc major version this note was created in. This is used to migrate certain features from HD1 to HD2 */ /** The HedgeDoc major version this note was created in. This is used to migrate certain features from HD1 to HD2 */
version: number; [FieldNameNote.version]: number;
/** Timestamp when the note was created */ /** Timestamp when the note was created */
createdAt: Date; [FieldNameNote.createdAt]: Date;
} }
export enum FieldNameNote {
id = 'id',
ownerId = 'owner_id',
version = 'version',
createdAt = 'created_at',
}
export const TableNote = 'note';

View file

@ -8,8 +8,15 @@
*/ */
export interface RevisionTag { export interface RevisionTag {
/** The id of {@link Revision} the {@link RevisionTag Tags} are asspcoated with. */ /** The id of {@link Revision} the {@link RevisionTag Tags} are asspcoated with. */
revisionId: number; [FieldNameRevisionTag.revisionId]: number;
/** The {@link RevisionTag Tag} text. */ /** The {@link RevisionTag Tag} text. */
tag: string; [FieldNameRevisionTag.tag]: string;
} }
export enum FieldNameRevisionTag {
revisionId = 'revision_id',
tag = 'tag',
}
export const TableRevisionTag = 'revision_tag';

View file

@ -10,29 +10,43 @@ import { NoteType } from '@hedgedoc/commons';
*/ */
export interface Revision { export interface Revision {
/** The unique id of the revision for internal referencing */ /** The unique id of the revision for internal referencing */
id: number; [FieldNameRevision.id]: number;
/** The id of the note that this revision belongs to */ /** The id of the note that this revision belongs to */
noteId: number; [FieldNameRevision.noteId]: number;
/** The changes between this revision and the previous one in patch file format */ /** The changes between this revision and the previous one in patch file format */
patch: string; [FieldNameRevision.patch]: string;
/** The content of the note at this revision */ /** The content of the note at this revision */
content: string; [FieldNameRevision.content]: string;
/** The stored Y.js state for realtime editing */ /** The stored Y.js state for realtime editing */
yjsStateVector: null | ArrayBuffer; [FieldNameRevision.yjsStateVector]: null | ArrayBuffer;
/** Whether the note is a document or presentation at this revision */ /** Whether the note is a document or presentation at this revision */
noteType: NoteType; [FieldNameRevision.noteType]: NoteType;
/** The extracted note title from this revision */ /** The extracted note title from this revision */
title: string; [FieldNameRevision.title]: string;
/** The extracted description from this revision */ /** The extracted description from this revision */
description: string; [FieldNameRevision.description]: string;
/** Timestamp when this revision was created */ /** Timestamp when this revision was created */
createdAt: Date; [FieldNameRevision.createdAt]: Date;
} }
export enum FieldNameRevision {
id = 'id',
noteId = 'note_id',
patch = 'patch',
content = 'content',
yjsStateVector = 'yjs_state_vector',
noteType = 'note_type',
title = 'title',
description = 'description',
createdAt = 'created_at',
}
export const TableRevision = 'revision';

View file

@ -10,8 +10,15 @@
*/ */
export interface UserPinnedNote { export interface UserPinnedNote {
/** The id of the {@link User} */ /** The id of the {@link User} */
userId: number; user_id: number;
/** The id of the {@link Note} */ /** The id of the {@link Note} */
noteId: number; note_id: number;
} }
export enum FieldNameUserPinnedNote {
userId = 'user_id',
noteId = 'note_id',
}
export const TableUserPinnedNote = 'user_pinned_note';

View file

@ -3,7 +3,7 @@
* *
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
import type { Username } from '../utils/username'; import type { Username } from '../../utils/username';
/** /**
* The user object represents either a registered user in the instance or a guest user. * The user object represents either a registered user in the instance or a guest user.
@ -18,26 +18,39 @@ import type { Username } from '../utils/username';
*/ */
export interface User { export interface User {
/** The unique id of the user for internal referencing */ /** The unique id of the user for internal referencing */
id: number; [FieldNameUser.id]: number;
/** The user's chosen username or null if it is a guest user */ /** The user's chosen username or null if it is a guest user */
username: Username | null; [FieldNameUser.username]: Username | null;
/** The guest user's UUID or null if it is a registered user */ /** The guest user's UUID or null if it is a registered user */
guestUuid: string | null; [FieldNameUser.guestUuid]: string | null;
/** The user's chosen display name */ /** The user's chosen display name */
displayName: string; [FieldNameUser.displayName]: string | null;
/** Timestamp when the user was created */ /** Timestamp when the user was created */
createdAt: Date; [FieldNameUser.createdAt]: Date;
/** URL to the user's profile picture if present */ /** URL to the user's profile picture if present */
photoUrl: string | null; [FieldNameUser.photoUrl]: string | null;
/** The user's email address if present */ /** The user's email address if present */
email: string | null; [FieldNameUser.email]: string | null;
/** The index which author style (e.g. color) should be used for this user */ /** The index which author style (e.g. color) should be used for this user */
authorStyle: number; [FieldNameUser.authorStyle]: number;
} }
export enum FieldNameUser {
id = 'id',
username = 'username',
guestUuid = 'guest_uuid',
displayName = 'display_name',
createdAt = 'created_at',
photoUrl = 'photo_url',
email = 'email',
authorStyle = 'author_style',
}
export const TableUser = 'user';

292
yarn.lock
View file

@ -2445,6 +2445,181 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@esbuild/aix-ppc64@npm:0.25.1":
version: 0.25.1
resolution: "@esbuild/aix-ppc64@npm:0.25.1"
conditions: os=aix & cpu=ppc64
languageName: node
linkType: hard
"@esbuild/android-arm64@npm:0.25.1":
version: 0.25.1
resolution: "@esbuild/android-arm64@npm:0.25.1"
conditions: os=android & cpu=arm64
languageName: node
linkType: hard
"@esbuild/android-arm@npm:0.25.1":
version: 0.25.1
resolution: "@esbuild/android-arm@npm:0.25.1"
conditions: os=android & cpu=arm
languageName: node
linkType: hard
"@esbuild/android-x64@npm:0.25.1":
version: 0.25.1
resolution: "@esbuild/android-x64@npm:0.25.1"
conditions: os=android & cpu=x64
languageName: node
linkType: hard
"@esbuild/darwin-arm64@npm:0.25.1":
version: 0.25.1
resolution: "@esbuild/darwin-arm64@npm:0.25.1"
conditions: os=darwin & cpu=arm64
languageName: node
linkType: hard
"@esbuild/darwin-x64@npm:0.25.1":
version: 0.25.1
resolution: "@esbuild/darwin-x64@npm:0.25.1"
conditions: os=darwin & cpu=x64
languageName: node
linkType: hard
"@esbuild/freebsd-arm64@npm:0.25.1":
version: 0.25.1
resolution: "@esbuild/freebsd-arm64@npm:0.25.1"
conditions: os=freebsd & cpu=arm64
languageName: node
linkType: hard
"@esbuild/freebsd-x64@npm:0.25.1":
version: 0.25.1
resolution: "@esbuild/freebsd-x64@npm:0.25.1"
conditions: os=freebsd & cpu=x64
languageName: node
linkType: hard
"@esbuild/linux-arm64@npm:0.25.1":
version: 0.25.1
resolution: "@esbuild/linux-arm64@npm:0.25.1"
conditions: os=linux & cpu=arm64
languageName: node
linkType: hard
"@esbuild/linux-arm@npm:0.25.1":
version: 0.25.1
resolution: "@esbuild/linux-arm@npm:0.25.1"
conditions: os=linux & cpu=arm
languageName: node
linkType: hard
"@esbuild/linux-ia32@npm:0.25.1":
version: 0.25.1
resolution: "@esbuild/linux-ia32@npm:0.25.1"
conditions: os=linux & cpu=ia32
languageName: node
linkType: hard
"@esbuild/linux-loong64@npm:0.25.1":
version: 0.25.1
resolution: "@esbuild/linux-loong64@npm:0.25.1"
conditions: os=linux & cpu=loong64
languageName: node
linkType: hard
"@esbuild/linux-mips64el@npm:0.25.1":
version: 0.25.1
resolution: "@esbuild/linux-mips64el@npm:0.25.1"
conditions: os=linux & cpu=mips64el
languageName: node
linkType: hard
"@esbuild/linux-ppc64@npm:0.25.1":
version: 0.25.1
resolution: "@esbuild/linux-ppc64@npm:0.25.1"
conditions: os=linux & cpu=ppc64
languageName: node
linkType: hard
"@esbuild/linux-riscv64@npm:0.25.1":
version: 0.25.1
resolution: "@esbuild/linux-riscv64@npm:0.25.1"
conditions: os=linux & cpu=riscv64
languageName: node
linkType: hard
"@esbuild/linux-s390x@npm:0.25.1":
version: 0.25.1
resolution: "@esbuild/linux-s390x@npm:0.25.1"
conditions: os=linux & cpu=s390x
languageName: node
linkType: hard
"@esbuild/linux-x64@npm:0.25.1":
version: 0.25.1
resolution: "@esbuild/linux-x64@npm:0.25.1"
conditions: os=linux & cpu=x64
languageName: node
linkType: hard
"@esbuild/netbsd-arm64@npm:0.25.1":
version: 0.25.1
resolution: "@esbuild/netbsd-arm64@npm:0.25.1"
conditions: os=netbsd & cpu=arm64
languageName: node
linkType: hard
"@esbuild/netbsd-x64@npm:0.25.1":
version: 0.25.1
resolution: "@esbuild/netbsd-x64@npm:0.25.1"
conditions: os=netbsd & cpu=x64
languageName: node
linkType: hard
"@esbuild/openbsd-arm64@npm:0.25.1":
version: 0.25.1
resolution: "@esbuild/openbsd-arm64@npm:0.25.1"
conditions: os=openbsd & cpu=arm64
languageName: node
linkType: hard
"@esbuild/openbsd-x64@npm:0.25.1":
version: 0.25.1
resolution: "@esbuild/openbsd-x64@npm:0.25.1"
conditions: os=openbsd & cpu=x64
languageName: node
linkType: hard
"@esbuild/sunos-x64@npm:0.25.1":
version: 0.25.1
resolution: "@esbuild/sunos-x64@npm:0.25.1"
conditions: os=sunos & cpu=x64
languageName: node
linkType: hard
"@esbuild/win32-arm64@npm:0.25.1":
version: 0.25.1
resolution: "@esbuild/win32-arm64@npm:0.25.1"
conditions: os=win32 & cpu=arm64
languageName: node
linkType: hard
"@esbuild/win32-ia32@npm:0.25.1":
version: 0.25.1
resolution: "@esbuild/win32-ia32@npm:0.25.1"
conditions: os=win32 & cpu=ia32
languageName: node
linkType: hard
"@esbuild/win32-x64@npm:0.25.1":
version: 0.25.1
resolution: "@esbuild/win32-x64@npm:0.25.1"
conditions: os=win32 & cpu=x64
languageName: node
linkType: hard
"@eslint-community/eslint-utils@npm:^4.1.2, @eslint-community/eslint-utils@npm:^4.2.0, @eslint-community/eslint-utils@npm:^4.4.0": "@eslint-community/eslint-utils@npm:^4.1.2, @eslint-community/eslint-utils@npm:^4.2.0, @eslint-community/eslint-utils@npm:^4.4.0":
version: 4.4.0 version: 4.4.0
resolution: "@eslint-community/eslint-utils@npm:4.4.0" resolution: "@eslint-community/eslint-utils@npm:4.4.0"
@ -2609,8 +2784,8 @@ __metadata:
supertest: "npm:6.3.4" supertest: "npm:6.3.4"
ts-jest: "npm:29.2.5" ts-jest: "npm:29.2.5"
ts-mockery: "npm:1.2.0" ts-mockery: "npm:1.2.0"
ts-node: "npm:11.0.0-beta.1"
tsconfig-paths: "npm:4.2.0" tsconfig-paths: "npm:4.2.0"
tsx: "npm:^4.19.3"
typescript: "npm:5.6.3" typescript: "npm:5.6.3"
uuid: "npm:11.0.5" uuid: "npm:11.0.5"
ws: "npm:8.18.0" ws: "npm:8.18.0"
@ -10241,6 +10416,92 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"esbuild@npm:~0.25.0":
version: 0.25.1
resolution: "esbuild@npm:0.25.1"
dependencies:
"@esbuild/aix-ppc64": "npm:0.25.1"
"@esbuild/android-arm": "npm:0.25.1"
"@esbuild/android-arm64": "npm:0.25.1"
"@esbuild/android-x64": "npm:0.25.1"
"@esbuild/darwin-arm64": "npm:0.25.1"
"@esbuild/darwin-x64": "npm:0.25.1"
"@esbuild/freebsd-arm64": "npm:0.25.1"
"@esbuild/freebsd-x64": "npm:0.25.1"
"@esbuild/linux-arm": "npm:0.25.1"
"@esbuild/linux-arm64": "npm:0.25.1"
"@esbuild/linux-ia32": "npm:0.25.1"
"@esbuild/linux-loong64": "npm:0.25.1"
"@esbuild/linux-mips64el": "npm:0.25.1"
"@esbuild/linux-ppc64": "npm:0.25.1"
"@esbuild/linux-riscv64": "npm:0.25.1"
"@esbuild/linux-s390x": "npm:0.25.1"
"@esbuild/linux-x64": "npm:0.25.1"
"@esbuild/netbsd-arm64": "npm:0.25.1"
"@esbuild/netbsd-x64": "npm:0.25.1"
"@esbuild/openbsd-arm64": "npm:0.25.1"
"@esbuild/openbsd-x64": "npm:0.25.1"
"@esbuild/sunos-x64": "npm:0.25.1"
"@esbuild/win32-arm64": "npm:0.25.1"
"@esbuild/win32-ia32": "npm:0.25.1"
"@esbuild/win32-x64": "npm:0.25.1"
dependenciesMeta:
"@esbuild/aix-ppc64":
optional: true
"@esbuild/android-arm":
optional: true
"@esbuild/android-arm64":
optional: true
"@esbuild/android-x64":
optional: true
"@esbuild/darwin-arm64":
optional: true
"@esbuild/darwin-x64":
optional: true
"@esbuild/freebsd-arm64":
optional: true
"@esbuild/freebsd-x64":
optional: true
"@esbuild/linux-arm":
optional: true
"@esbuild/linux-arm64":
optional: true
"@esbuild/linux-ia32":
optional: true
"@esbuild/linux-loong64":
optional: true
"@esbuild/linux-mips64el":
optional: true
"@esbuild/linux-ppc64":
optional: true
"@esbuild/linux-riscv64":
optional: true
"@esbuild/linux-s390x":
optional: true
"@esbuild/linux-x64":
optional: true
"@esbuild/netbsd-arm64":
optional: true
"@esbuild/netbsd-x64":
optional: true
"@esbuild/openbsd-arm64":
optional: true
"@esbuild/openbsd-x64":
optional: true
"@esbuild/sunos-x64":
optional: true
"@esbuild/win32-arm64":
optional: true
"@esbuild/win32-ia32":
optional: true
"@esbuild/win32-x64":
optional: true
bin:
esbuild: bin/esbuild
checksum: 10c0/80fca30dd0f21aec23fdfab34f0a8d5f55df5097dd7f475f2ab561d45662c32ee306f5649071cd1a0ba0614b164c48ca3dc3ee1551a4daf204b8af90e4d893f5
languageName: node
linkType: hard
"escalade@npm:^3.1.1": "escalade@npm:^3.1.1":
version: 3.1.2 version: 3.1.2
resolution: "escalade@npm:3.1.2" resolution: "escalade@npm:3.1.2"
@ -11462,7 +11723,7 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"fsevents@npm:^2.3.2, fsevents@npm:~2.3.2": "fsevents@npm:^2.3.2, fsevents@npm:~2.3.2, fsevents@npm:~2.3.3":
version: 2.3.3 version: 2.3.3
resolution: "fsevents@npm:2.3.3" resolution: "fsevents@npm:2.3.3"
dependencies: dependencies:
@ -11472,7 +11733,7 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"fsevents@patch:fsevents@npm%3A^2.3.2#optional!builtin<compat/fsevents>, fsevents@patch:fsevents@npm%3A~2.3.2#optional!builtin<compat/fsevents>": "fsevents@patch:fsevents@npm%3A^2.3.2#optional!builtin<compat/fsevents>, fsevents@patch:fsevents@npm%3A~2.3.2#optional!builtin<compat/fsevents>, fsevents@patch:fsevents@npm%3A~2.3.3#optional!builtin<compat/fsevents>":
version: 2.3.3 version: 2.3.3
resolution: "fsevents@patch:fsevents@npm%3A2.3.3#optional!builtin<compat/fsevents>::version=2.3.3&hash=df0bf1" resolution: "fsevents@patch:fsevents@npm%3A2.3.3#optional!builtin<compat/fsevents>::version=2.3.3&hash=df0bf1"
dependencies: dependencies:
@ -11577,6 +11838,15 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"get-tsconfig@npm:^4.7.5":
version: 4.10.0
resolution: "get-tsconfig@npm:4.10.0"
dependencies:
resolve-pkg-maps: "npm:^1.0.0"
checksum: 10c0/c9b5572c5118923c491c04285c73bd55b19e214992af957c502a3be0fc0043bb421386ffd45ca3433c0a7fba81221ca300479e8393960acf15d0ed4563f38a86
languageName: node
linkType: hard
"get-tsconfig@npm:^4.8.1": "get-tsconfig@npm:^4.8.1":
version: 4.8.1 version: 4.8.1
resolution: "get-tsconfig@npm:4.8.1" resolution: "get-tsconfig@npm:4.8.1"
@ -18531,6 +18801,22 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"tsx@npm:^4.19.3":
version: 4.19.3
resolution: "tsx@npm:4.19.3"
dependencies:
esbuild: "npm:~0.25.0"
fsevents: "npm:~2.3.3"
get-tsconfig: "npm:^4.7.5"
dependenciesMeta:
fsevents:
optional: true
bin:
tsx: dist/cli.mjs
checksum: 10c0/cacfb4cf1392ae10e8e4fe032ad26ccb07cd8a3b32e5a0da270d9c48d06ee74f743e4a84686cbc9d89b48032d59bbc56cd911e076f53cebe61dc24fa525ff790
languageName: node
linkType: hard
"tunnel-agent@npm:^0.6.0": "tunnel-agent@npm:^0.6.0":
version: 0.6.0 version: 0.6.0
resolution: "tunnel-agent@npm:0.6.0" resolution: "tunnel-agent@npm:0.6.0"