discourse 3/3
This commit is contained in:
parent
36f46ca292
commit
e1dd99b693
1
packages/discourse/dist/src/_cli.d.ts
vendored
Normal file
1
packages/discourse/dist/src/_cli.d.ts
vendored
Normal file
@ -0,0 +1 @@
|
||||
export declare const defaults: () => void;
|
||||
13
packages/discourse/dist/src/_cli.js
vendored
Normal file
13
packages/discourse/dist/src/_cli.js
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
export const defaults = () => {
|
||||
// default command
|
||||
const DefaultCommand = 'info';
|
||||
if (process.argv.length === 2) {
|
||||
process.argv.push(DefaultCommand);
|
||||
}
|
||||
process.env['NODE_TLS_REJECT_UNAUTHORIZED'] = '0';
|
||||
// currently no default handler, display only :
|
||||
process.on('unhandledRejection', (reason) => {
|
||||
console.error('Unhandled rejection, reason: ', reason);
|
||||
});
|
||||
};
|
||||
//# sourceMappingURL=_cli.js.map
|
||||
1
packages/discourse/dist/src/_cli.js.map
vendored
Normal file
1
packages/discourse/dist/src/_cli.js.map
vendored
Normal file
@ -0,0 +1 @@
|
||||
{"version":3,"file":"_cli.js","sourceRoot":"","sources":["../../src/_cli.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,QAAQ,GAAG,GAAG,EAAE;IACzB,kBAAkB;IAClB,MAAM,cAAc,GAAG,MAAM,CAAC;IAC9B,IAAI,OAAO,CAAC,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE;QAC3B,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;KACrC;IAED,OAAO,CAAC,GAAG,CAAC,8BAA8B,CAAC,GAAG,GAAG,CAAC;IAElD,+CAA+C;IAC/C,OAAO,CAAC,EAAE,CAAC,oBAAoB,EAAE,CAAC,MAAc,EAAE,EAAE;QAChD,OAAO,CAAC,KAAK,CAAC,+BAA+B,EAAE,MAAM,CAAC,CAAC;IAC3D,CAAC,CAAC,CAAC;AACP,CAAC,CAAA"}
|
||||
2
packages/discourse/dist/src/cli.d.ts
vendored
Normal file
2
packages/discourse/dist/src/cli.d.ts
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
/// <reference types="yargs" />
|
||||
export declare const cli: import("yargs").Argv<{}>;
|
||||
4
packages/discourse/dist/src/cli.js
vendored
Normal file
4
packages/discourse/dist/src/cli.js
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
import yargs from 'yargs';
|
||||
import { hideBin } from 'yargs/helpers';
|
||||
export const cli = yargs(hideBin(process.argv));
|
||||
//# sourceMappingURL=cli.js.map
|
||||
1
packages/discourse/dist/src/cli.js.map
vendored
Normal file
1
packages/discourse/dist/src/cli.js.map
vendored
Normal file
@ -0,0 +1 @@
|
||||
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../../src/cli.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AACzB,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAA;AAEvC,MAAM,CAAC,MAAM,GAAG,GAAG,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAA"}
|
||||
1
packages/discourse/dist/src/constants.d.ts
vendored
Normal file
1
packages/discourse/dist/src/constants.d.ts
vendored
Normal file
@ -0,0 +1 @@
|
||||
export declare const MODULE_NAME = "OSR-DISCOURSE";
|
||||
2
packages/discourse/dist/src/constants.js
vendored
Normal file
2
packages/discourse/dist/src/constants.js
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
export const MODULE_NAME = `OSR-DISCOURSE`;
|
||||
//# sourceMappingURL=constants.js.map
|
||||
1
packages/discourse/dist/src/constants.js.map
vendored
Normal file
1
packages/discourse/dist/src/constants.js.map
vendored
Normal file
@ -0,0 +1 @@
|
||||
{"version":3,"file":"constants.js","sourceRoot":"","sources":["../../src/constants.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,WAAW,GAAG,eAAe,CAAA"}
|
||||
6
packages/discourse/dist/src/index.d.ts
vendored
Normal file
6
packages/discourse/dist/src/index.d.ts
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
export * from './constants.js';
|
||||
export * from './types.js';
|
||||
export * from './lib/index.js';
|
||||
export { Logger } from 'tslog';
|
||||
import { IObjectLiteral } from "@polymech/core";
|
||||
export declare const substitute: (alt: boolean, template: string, vars?: IObjectLiteral) => any;
|
||||
7
packages/discourse/dist/src/index.js
vendored
Normal file
7
packages/discourse/dist/src/index.js
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
export * from './constants.js';
|
||||
export * from './types.js';
|
||||
export * from './lib/index.js';
|
||||
export { Logger } from 'tslog';
|
||||
import { substitute as _substitute, substituteAlt as _substituteAlt } from "@polymech/core/strings";
|
||||
export const substitute = (alt, template, vars = {}) => alt ? _substituteAlt(template, vars) : _substitute(template, vars);
|
||||
//# sourceMappingURL=index.js.map
|
||||
1
packages/discourse/dist/src/index.js.map
vendored
Normal file
1
packages/discourse/dist/src/index.js.map
vendored
Normal file
@ -0,0 +1 @@
|
||||
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAA;AAC9B,cAAc,YAAY,CAAA;AAC1B,cAAc,gBAAgB,CAAA;AAC9B,OAAO,EAAE,MAAM,EAAE,MAAM,OAAO,CAAA;AAC9B,OAAO,EAAE,UAAU,IAAI,WAAW,EAAE,aAAa,IAAI,cAAc,EAAE,MAAM,wBAAwB,CAAA;AAEnG,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,GAAY,EAAE,QAAgB,EAAE,OAAuB,EAAE,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,cAAc,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAA"}
|
||||
11
packages/discourse/dist/src/lib/discourse/cache.d.ts
vendored
Normal file
11
packages/discourse/dist/src/lib/discourse/cache.d.ts
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
/// <reference types="node" resolution-mode="require"/>
|
||||
/// <reference types="node" resolution-mode="require"/>
|
||||
import { IDiscourseUser } from '../../index.js';
|
||||
import { Discourser } from '../index.js';
|
||||
export declare const fileAsBuffer: (path: string) => Buffer;
|
||||
export declare const cacheCategories: (options: any, discourse: Discourser) => Promise<any>;
|
||||
export declare const cacheTopics: (options: any, discourse: Discourser) => Promise<void>;
|
||||
export declare const _getForumUsers: (d: Discourser, page: any, detail: any) => any;
|
||||
export declare const getForumUsers: (d: any, detail: any) => Promise<IDiscourseUser[]>;
|
||||
export declare const cacheUsers: (options: any, discourse: Discourser) => Promise<IDiscourseUser[]>;
|
||||
export declare const cacheTags: (options: any, discourse: Discourser) => Promise<any>;
|
||||
110
packages/discourse/dist/src/lib/discourse/cache.js
vendored
Normal file
110
packages/discourse/dist/src/lib/discourse/cache.js
vendored
Normal file
@ -0,0 +1,110 @@
|
||||
import { Promise as BPromise } from 'bluebird';
|
||||
import { sync as read } from '@polymech/fs/read';
|
||||
import { sync as exists } from '@polymech/fs/exists';
|
||||
import { sync as write } from '@polymech/fs/write';
|
||||
import { resolve } from '@polymech/commons';
|
||||
import { DISCOURSE_CATEGORY_CACHE, DISCOURSE_TAGS_CACHE, DISCOURSE_USER_CACHE, } from '../discourse/constants.js';
|
||||
import * as path from 'path';
|
||||
export const fileAsBuffer = (path) => read(path, 'buffer') || Buffer.from("-");
|
||||
import { get_cached, set_cached } from '@polymech/cache/lib';
|
||||
import { OSR_CACHE } from '@polymech/commons';
|
||||
import { MODULE_NAME } from '../../constants.js';
|
||||
export const cacheCategories = async (options, discourse) => {
|
||||
const osr_cache = OSR_CACHE();
|
||||
const cPath = path.resolve(resolve(DISCOURSE_CATEGORY_CACHE));
|
||||
const cached = exists(cPath) ? await get_cached(cPath, {}, MODULE_NAME) : null;
|
||||
if (osr_cache && cached && options.cache !== false) {
|
||||
return JSON.parse(cached);
|
||||
}
|
||||
let cats = await discourse.getCategories({
|
||||
include_subcategories: true
|
||||
});
|
||||
write(cPath, cats);
|
||||
if (osr_cache && options.cache !== false) {
|
||||
await set_cached(cPath, {}, MODULE_NAME, cats);
|
||||
}
|
||||
return cats;
|
||||
};
|
||||
export const cacheTopics = async (options, discourse) => {
|
||||
};
|
||||
let uPage = 1;
|
||||
let usersAll = [];
|
||||
export const _getForumUsers = async (d, page, detail) => {
|
||||
const uPath = path.resolve(resolve(DISCOURSE_USER_CACHE));
|
||||
if (uPage == 1) {
|
||||
usersAll = [];
|
||||
}
|
||||
let users = await d.getUsers(page);
|
||||
if (users.length) {
|
||||
usersAll = usersAll.concat(users);
|
||||
uPage++;
|
||||
return _getForumUsers(d, uPage, detail);
|
||||
}
|
||||
else {
|
||||
uPage = 1;
|
||||
write(uPath, usersAll);
|
||||
let fUsers = read(uPath, 'json') || [];
|
||||
const add = async (u) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
let fUser = fUsers.find((fu) => u.id == fu.id);
|
||||
if (!fUser) {
|
||||
fUsers.push(u);
|
||||
fUser = u;
|
||||
}
|
||||
if (fUser.detail) {
|
||||
console.log('Retrieve User Detail ' + u.name);
|
||||
setTimeout(() => {
|
||||
d.getUser(fUser.id).then((detail) => {
|
||||
if (detail) {
|
||||
fUser.detail = detail;
|
||||
}
|
||||
write(uPath, fUsers);
|
||||
resolve(fUser);
|
||||
});
|
||||
}, 200);
|
||||
}
|
||||
else {
|
||||
resolve(fUser);
|
||||
}
|
||||
});
|
||||
};
|
||||
return await BPromise.resolve(usersAll).map((u) => {
|
||||
return add(u);
|
||||
}, { concurrency: 1 });
|
||||
}
|
||||
};
|
||||
export const getForumUsers = async (d, detail) => {
|
||||
return _getForumUsers(d, uPage, detail);
|
||||
};
|
||||
export const cacheUsers = async (options, discourse) => {
|
||||
const osr_cache = OSR_CACHE();
|
||||
const uPath = path.resolve(resolve(DISCOURSE_USER_CACHE));
|
||||
const cached = exists(uPath) ? await get_cached(uPath, {}, MODULE_NAME) : null;
|
||||
if (osr_cache && options.cache !== false && exists(uPath)) {
|
||||
return read(uPath, 'json');
|
||||
}
|
||||
if (osr_cache && cached && options.cache !== false) {
|
||||
return JSON.parse(cached);
|
||||
}
|
||||
let users = await getForumUsers(discourse, false);
|
||||
write(uPath, users);
|
||||
if (osr_cache && options.cache !== false) {
|
||||
await set_cached(uPath, {}, MODULE_NAME, users);
|
||||
}
|
||||
return users;
|
||||
};
|
||||
export const cacheTags = async (options, discourse) => {
|
||||
const osr_cache = OSR_CACHE();
|
||||
const tPath = path.resolve(resolve(DISCOURSE_TAGS_CACHE));
|
||||
const cached = exists(tPath) ? await get_cached(tPath, {}, MODULE_NAME) : null;
|
||||
if (osr_cache && cached && options.cache !== false) {
|
||||
return JSON.parse(cached);
|
||||
}
|
||||
let tags = await discourse.getTags();
|
||||
write(tPath, tags);
|
||||
if (osr_cache && options.cache !== false) {
|
||||
await set_cached(tPath, {}, MODULE_NAME, tags);
|
||||
}
|
||||
return tags;
|
||||
};
|
||||
//# sourceMappingURL=cache.js.map
|
||||
1
packages/discourse/dist/src/lib/discourse/cache.js.map
vendored
Normal file
1
packages/discourse/dist/src/lib/discourse/cache.js.map
vendored
Normal file
@ -0,0 +1 @@
|
||||
{"version":3,"file":"cache.js","sourceRoot":"","sources":["../../../../src/lib/discourse/cache.ts"],"names":[],"mappings":"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"}
|
||||
70
packages/discourse/dist/src/lib/discourse/constants.d.ts
vendored
Normal file
70
packages/discourse/dist/src/lib/discourse/constants.d.ts
vendored
Normal file
@ -0,0 +1,70 @@
|
||||
export type EDiscourseConfigKey = 'discourse' | 'discourse_admin';
|
||||
export declare const SYNC_TRACK_FILENAME = "discourse-sync.json";
|
||||
export declare const OSR_CACHE_ROOT = "${OSR_CACHE}";
|
||||
export declare const OSR_DISCOURSE_CACHE = "${OSR_CACHE}/discourse";
|
||||
export declare const DISCOURSE_CATEGORY_CACHE = "${OSR_CACHE}/discourse/cats.json";
|
||||
export declare const DISCOURSE_TOPICS_CACHE = "${OSR_CACHE}/discourse/topics.json";
|
||||
export declare const DISCOURSE_TAGS_CACHE = "${OSR_CACHE}/discourse/tags.json";
|
||||
export declare const DISCOURSE_USER_CACHE = "${OSR_CACHE}/discourse/users.json";
|
||||
export declare const OA_DIRECTORY_OVERVIEW_TOPIC = 28873;
|
||||
export declare const OA_USER_IMPORT_GROUP = 43;
|
||||
export declare const KB_USERS = "${KB_ROOT}/static/users";
|
||||
export declare const DATA_PATH = "${OSR_ROOT}/osr-directory/pp";
|
||||
export declare const LATEST_TRACK = "${OSR_ROOT}/osr-directory/pp/merged.json";
|
||||
export declare const LATEST_TEST = "./latest_test.json";
|
||||
export declare const FETCH_DUSERS = false;
|
||||
export declare const F_USERS_NOW = "./fusers.json";
|
||||
export declare const F_USERS_ALL = "./fusers-all.json";
|
||||
export declare const DEFAULT_PASSWORD: () => string;
|
||||
export declare const HOWTOS_ASSETS_URL: () => string;
|
||||
export declare const MACHINES_ASSETS_URL: () => string;
|
||||
export declare const CAT_TEST = 65;
|
||||
export declare const TAGS_TEST = "plastic, meta";
|
||||
export declare const DEFAULT_IMPORT_OWNER = 1;
|
||||
export declare const D_ROOT_CAT = 97;
|
||||
export declare const D_ROOT_AFRICA = 79;
|
||||
export declare const D_ROOT_ASIA = 60;
|
||||
export declare const D_ROOT_EUROPE = 59;
|
||||
export declare const D_ROOT_AUSTRALIA = 76;
|
||||
export declare const D_ROOT_NAMERICA = 101;
|
||||
export declare const D_ROOT_SAMERICA = 102;
|
||||
export declare const D_ROOT_OCEANIA = 103;
|
||||
export declare const HT_CAT_ROOT = 54;
|
||||
export declare const HT_CAT_GUIDES = 86;
|
||||
export declare const HT_CAT_MACHINES = 87;
|
||||
export declare const HT_CAT_PRODUCTS = 88;
|
||||
export declare const HT_CAT_MOULDS = 89;
|
||||
export declare const HT_CAT_IDS: {
|
||||
HT_CAT_ROOT: number;
|
||||
HT_CAT_GUIDES: number;
|
||||
HT_CAT_MACHINES: number;
|
||||
HT_CAT_PRODUCTS: number;
|
||||
HT_CAT_MOULDS: number;
|
||||
};
|
||||
export declare const HT_CATS: {
|
||||
Guides: number;
|
||||
Machines: number;
|
||||
Products: number;
|
||||
Moulds: number;
|
||||
};
|
||||
export declare const MACHINE_CAT_INJECTION = 50;
|
||||
export declare const MACHINE_CAT_EXTRUSION = 51;
|
||||
export declare const MACHINE_CAT_SHREDDER = 52;
|
||||
export declare const MACHINE_CAT_SHEETPRESS = 63;
|
||||
export declare const MACHINE_CAT_3DPRINT = 70;
|
||||
export declare const MACHINE_CAT_MOULDS = 71;
|
||||
export declare const MACHINE_CAT_IDS: {
|
||||
MACHINE_CAT_INJECTION: number;
|
||||
MACHINE_CAT_EXTRUSION: number;
|
||||
MACHINE_CAT_SHREDDER: number;
|
||||
MACHINE_CAT_SHEETPRESS: number;
|
||||
MACHINE_CAT_3DPRINT: number;
|
||||
MACHINE_CAT_MOULDS: number;
|
||||
};
|
||||
export declare const MACHINE_CATS: {
|
||||
Injection: number;
|
||||
Extrusion: number;
|
||||
Sheetpress: number;
|
||||
'3DPrint': number;
|
||||
Moulds: number;
|
||||
};
|
||||
94
packages/discourse/dist/src/lib/discourse/constants.js
vendored
Normal file
94
packages/discourse/dist/src/lib/discourse/constants.js
vendored
Normal file
@ -0,0 +1,94 @@
|
||||
//////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Keys
|
||||
//
|
||||
//////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Paths
|
||||
//
|
||||
export const SYNC_TRACK_FILENAME = 'discourse-sync.json';
|
||||
export const OSR_CACHE_ROOT = '${OSR_CACHE}';
|
||||
export const OSR_DISCOURSE_CACHE = '${OSR_CACHE}/discourse';
|
||||
export const DISCOURSE_CATEGORY_CACHE = '${OSR_CACHE}/discourse/cats.json';
|
||||
export const DISCOURSE_TOPICS_CACHE = '${OSR_CACHE}/discourse/topics.json';
|
||||
export const DISCOURSE_TAGS_CACHE = '${OSR_CACHE}/discourse/tags.json';
|
||||
export const DISCOURSE_USER_CACHE = '${OSR_CACHE}/discourse/users.json';
|
||||
//////////////////////////////////////////////////////////////
|
||||
//
|
||||
// OA - User Import
|
||||
//
|
||||
export const OA_DIRECTORY_OVERVIEW_TOPIC = 28873;
|
||||
export const OA_USER_IMPORT_GROUP = 43;
|
||||
export const KB_USERS = '${KB_ROOT}/static/users';
|
||||
export const DATA_PATH = '${OSR_ROOT}/osr-directory/pp';
|
||||
export const LATEST_TRACK = '${OSR_ROOT}/osr-directory/pp/merged.json';
|
||||
export const LATEST_TEST = './latest_test.json';
|
||||
export const FETCH_DUSERS = false;
|
||||
export const F_USERS_NOW = './fusers.json';
|
||||
export const F_USERS_ALL = './fusers-all.json';
|
||||
export const DEFAULT_PASSWORD = () => '4g0&KPN$e*Un';
|
||||
export const HOWTOS_ASSETS_URL = () => `https://kb.osr-plastic.org/howtos/`;
|
||||
export const MACHINES_ASSETS_URL = () => `https://assets.osr-plastic.org/machines/`;
|
||||
export const CAT_TEST = 65;
|
||||
export const TAGS_TEST = 'plastic, meta';
|
||||
export const DEFAULT_IMPORT_OWNER = 1;
|
||||
//////////////////////////////////////////////////////////////
|
||||
//
|
||||
// OA - Directory Import
|
||||
//
|
||||
export const D_ROOT_CAT = 97;
|
||||
export const D_ROOT_AFRICA = 79;
|
||||
export const D_ROOT_ASIA = 60;
|
||||
export const D_ROOT_EUROPE = 59;
|
||||
export const D_ROOT_AUSTRALIA = 76;
|
||||
export const D_ROOT_NAMERICA = 101;
|
||||
export const D_ROOT_SAMERICA = 102;
|
||||
export const D_ROOT_OCEANIA = 103;
|
||||
//////////////////////////////////////////////////////////////
|
||||
//
|
||||
// OA - Howto Import
|
||||
//
|
||||
export const HT_CAT_ROOT = 54;
|
||||
export const HT_CAT_GUIDES = 86;
|
||||
export const HT_CAT_MACHINES = 87;
|
||||
export const HT_CAT_PRODUCTS = 88;
|
||||
export const HT_CAT_MOULDS = 89;
|
||||
export const HT_CAT_IDS = {
|
||||
HT_CAT_ROOT,
|
||||
HT_CAT_GUIDES,
|
||||
HT_CAT_MACHINES,
|
||||
HT_CAT_PRODUCTS,
|
||||
HT_CAT_MOULDS
|
||||
};
|
||||
export const HT_CATS = {
|
||||
'Guides': HT_CAT_GUIDES,
|
||||
'Machines': HT_CAT_MACHINES,
|
||||
'Products': HT_CAT_PRODUCTS,
|
||||
'Moulds': HT_CAT_MOULDS
|
||||
};
|
||||
//////////////////////////////////////////////////////////////
|
||||
//
|
||||
// OA - Library Import
|
||||
//
|
||||
export const MACHINE_CAT_INJECTION = 50;
|
||||
export const MACHINE_CAT_EXTRUSION = 51;
|
||||
export const MACHINE_CAT_SHREDDER = 52;
|
||||
export const MACHINE_CAT_SHEETPRESS = 63;
|
||||
export const MACHINE_CAT_3DPRINT = 70;
|
||||
export const MACHINE_CAT_MOULDS = 71;
|
||||
export const MACHINE_CAT_IDS = {
|
||||
MACHINE_CAT_INJECTION,
|
||||
MACHINE_CAT_EXTRUSION,
|
||||
MACHINE_CAT_SHREDDER,
|
||||
MACHINE_CAT_SHEETPRESS,
|
||||
MACHINE_CAT_3DPRINT,
|
||||
MACHINE_CAT_MOULDS
|
||||
};
|
||||
export const MACHINE_CATS = {
|
||||
'Injection': MACHINE_CAT_INJECTION,
|
||||
'Extrusion': MACHINE_CAT_EXTRUSION,
|
||||
'Sheetpress': MACHINE_CAT_SHEETPRESS,
|
||||
'3DPrint': MACHINE_CAT_3DPRINT,
|
||||
'Moulds': HT_CAT_MOULDS
|
||||
};
|
||||
//# sourceMappingURL=constants.js.map
|
||||
1
packages/discourse/dist/src/lib/discourse/constants.js.map
vendored
Normal file
1
packages/discourse/dist/src/lib/discourse/constants.js.map
vendored
Normal file
@ -0,0 +1 @@
|
||||
{"version":3,"file":"constants.js","sourceRoot":"","sources":["../../../../src/lib/discourse/constants.ts"],"names":[],"mappings":"AACA,8DAA8D;AAC9D,EAAE;AACF,QAAQ;AACR,EAAE;AAIF,8DAA8D;AAC9D,EAAE;AACF,SAAS;AACT,EAAE;AAEF,MAAM,CAAC,MAAM,mBAAmB,GAAG,qBAAqB,CAAA;AAExD,MAAM,CAAC,MAAM,cAAc,GAAG,cAAc,CAAA;AAC5C,MAAM,CAAC,MAAM,mBAAmB,GAAG,wBAAwB,CAAA;AAE3D,MAAM,CAAC,MAAM,wBAAwB,GAAG,kCAAkC,CAAA;AAC1E,MAAM,CAAC,MAAM,sBAAsB,GAAG,oCAAoC,CAAA;AAC1E,MAAM,CAAC,MAAM,oBAAoB,GAAG,kCAAkC,CAAA;AACtE,MAAM,CAAC,MAAM,oBAAoB,GAAG,mCAAmC,CAAA;AAEvE,8DAA8D;AAC9D,EAAE;AACF,oBAAoB;AACpB,EAAE;AAEF,MAAM,CAAC,MAAM,2BAA2B,GAAG,KAAK,CAAA;AAChD,MAAM,CAAC,MAAM,oBAAoB,GAAG,EAAE,CAAA;AACtC,MAAM,CAAC,MAAM,QAAQ,GAAG,yBAAyB,CAAA;AACjD,MAAM,CAAC,MAAM,SAAS,GAAG,8BAA8B,CAAA;AAEvD,MAAM,CAAC,MAAM,YAAY,GAAG,0CAA0C,CAAA;AACtE,MAAM,CAAC,MAAM,WAAW,GAAG,oBAAoB,CAAA;AAC/C,MAAM,CAAC,MAAM,YAAY,GAAG,KAAK,CAAA;AACjC,MAAM,CAAC,MAAM,WAAW,GAAG,eAAe,CAAA;AAC1C,MAAM,CAAC,MAAM,WAAW,GAAG,mBAAmB,CAAA;AAE9C,MAAM,CAAC,MAAM,gBAAgB,GAAG,GAAG,EAAE,CAAC,cAAc,CAAA;AAEpD,MAAM,CAAC,MAAM,iBAAiB,GAAG,GAAG,EAAE,CAAC,oCAAoC,CAAA;AAC3E,MAAM,CAAC,MAAM,mBAAmB,GAAG,GAAG,EAAE,CAAC,0CAA0C,CAAA;AAEnF,MAAM,CAAC,MAAM,QAAQ,GAAG,EAAE,CAAA;AAC1B,MAAM,CAAC,MAAM,SAAS,GAAG,eAAe,CAAA;AACxC,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,CAAA;AAErC,8DAA8D;AAC9D,EAAE;AACF,yBAAyB;AACzB,EAAE;AACF,MAAM,CAAC,MAAM,UAAU,GAAG,EAAE,CAAA;AAE5B,MAAM,CAAC,MAAM,aAAa,GAAG,EAAE,CAAA;AAC/B,MAAM,CAAC,MAAM,WAAW,GAAG,EAAE,CAAA;AAC7B,MAAM,CAAC,MAAM,aAAa,GAAG,EAAE,CAAA;AAC/B,MAAM,CAAC,MAAM,gBAAgB,GAAG,EAAE,CAAA;AAClC,MAAM,CAAC,MAAM,eAAe,GAAG,GAAG,CAAA;AAClC,MAAM,CAAC,MAAM,eAAe,GAAG,GAAG,CAAA;AAClC,MAAM,CAAC,MAAM,cAAc,GAAG,GAAG,CAAA;AAEjC,8DAA8D;AAC9D,EAAE;AACF,qBAAqB;AACrB,EAAE;AACF,MAAM,CAAC,MAAM,WAAW,GAAG,EAAE,CAAA;AAC7B,MAAM,CAAC,MAAM,aAAa,GAAG,EAAE,CAAA;AAC/B,MAAM,CAAC,MAAM,eAAe,GAAG,EAAE,CAAA;AACjC,MAAM,CAAC,MAAM,eAAe,GAAG,EAAE,CAAA;AACjC,MAAM,CAAC,MAAM,aAAa,GAAG,EAAE,CAAA;AAE/B,MAAM,CAAC,MAAM,UAAU,GAAG;IACtB,WAAW;IACX,aAAa;IACb,eAAe;IACf,eAAe;IACf,aAAa;CAChB,CAAA;AAGD,MAAM,CAAC,MAAM,OAAO,GAAG;IACnB,QAAQ,EAAE,aAAa;IACvB,UAAU,EAAE,eAAe;IAC3B,UAAU,EAAE,eAAe;IAC3B,QAAQ,EAAE,aAAa;CAC1B,CAAA;AAED,8DAA8D;AAC9D,EAAE;AACF,uBAAuB;AACvB,EAAE;AAEF,MAAM,CAAC,MAAM,qBAAqB,GAAG,EAAE,CAAA;AACvC,MAAM,CAAC,MAAM,qBAAqB,GAAG,EAAE,CAAA;AACvC,MAAM,CAAC,MAAM,oBAAoB,GAAG,EAAE,CAAA;AACtC,MAAM,CAAC,MAAM,sBAAsB,GAAG,EAAE,CAAA;AACxC,MAAM,CAAC,MAAM,mBAAmB,GAAG,EAAE,CAAA;AACrC,MAAM,CAAC,MAAM,kBAAkB,GAAG,EAAE,CAAA;AAEpC,MAAM,CAAC,MAAM,eAAe,GAAG;IAC3B,qBAAqB;IACrB,qBAAqB;IACrB,oBAAoB;IACpB,sBAAsB;IACtB,mBAAmB;IACnB,kBAAkB;CACrB,CAAA;AAED,MAAM,CAAC,MAAM,YAAY,GAAG;IACxB,WAAW,EAAE,qBAAqB;IAClC,WAAW,EAAE,qBAAqB;IAClC,YAAY,EAAE,sBAAsB;IACpC,SAAS,EAAE,mBAAmB;IAC9B,QAAQ,EAAE,aAAa;CAC1B,CAAA"}
|
||||
180
packages/discourse/dist/src/lib/discourse/index.d.ts
vendored
Normal file
180
packages/discourse/dist/src/lib/discourse/index.d.ts
vendored
Normal file
@ -0,0 +1,180 @@
|
||||
import { IDiscourseConfig } from '@polymech/commons/types';
|
||||
import PromisePool from 'native-promise-pool';
|
||||
export declare const escape: (path: string) => string;
|
||||
import { TPostStatus, TPostStatusUpdate, UserPreferencesUpdate } from './types.js';
|
||||
import { Category, CategoriesResponse, PostsResponse, PostResponse, TopicResponse, TopicItem, CategoryResponse, PostItem, PostUpdateItem, IDiscourserConfig, Thread, PostModifier, FetchConfig, FetchOptions, ISearchResult, ICreateUserResponse, IUserDetail, TagsResponse, Tag, TopicUpdateBasicTopic } from './types.js';
|
||||
import { IDiscourseUser } from '@polymech/commons';
|
||||
import { EDiscourseConfigKey } from './constants.js';
|
||||
/**
|
||||
* Discourser is an API Client for the [Discourse API](https://docs.discourse.org)
|
||||
* It special features are:
|
||||
* - TypeScript Types
|
||||
* - Respecting Rate Limits
|
||||
* - Optional Heavy Caching
|
||||
* - Post Modifiers (can be used for global find and replace across all posts on the forum)
|
||||
*/
|
||||
export declare class Discourser {
|
||||
readonly host: string;
|
||||
readonly key: string;
|
||||
readonly username: string;
|
||||
readonly cache?: string;
|
||||
readonly useCache?: boolean;
|
||||
readonly dry: boolean;
|
||||
readonly pool: PromisePool<any>;
|
||||
/**
|
||||
* Construct our Discourser instance
|
||||
* See {@link IDiscourserConfig} for available configuration.
|
||||
*/
|
||||
constructor(config: IDiscourserConfig);
|
||||
/** Get the URL of a topic */
|
||||
getTopicURL(topic: TopicItem | TopicResponse | number): string;
|
||||
/** Fetch a discourse API URL, with rate limit concurrency and optional caching */
|
||||
fetch<T>({ url, useCache, request }: FetchConfig): Promise<T>;
|
||||
/** Fetch a discourse API URL, with rate limit retries */
|
||||
private _post;
|
||||
/** Fetch a discourse API URL, with rate limit retries */
|
||||
private _fetch;
|
||||
/**
|
||||
* API Helper for {@link .search}
|
||||
* https://docs.discourse.org/#tag/Search/operation/search
|
||||
*/
|
||||
search(query: string, params?: string, opts?: FetchOptions): Promise<ISearchResult>;
|
||||
/**
|
||||
* API Helper for {@link .getTags}
|
||||
*/
|
||||
protected getTagsResponse(opts?: FetchOptions): Promise<TagsResponse>;
|
||||
/**
|
||||
* Fetch the whole information, for all categories of the forum
|
||||
*/
|
||||
getTags(opts?: FetchOptions): Promise<Tag[]>;
|
||||
createTag(name: any): Promise<any>;
|
||||
/**
|
||||
* API Helper for {@link .getCategories}
|
||||
*/
|
||||
protected getCategoriesResponse(opts?: FetchOptions): Promise<CategoriesResponse>;
|
||||
/**
|
||||
* Fetch the whole information, for all categories of the forum
|
||||
*/
|
||||
getCategories(opts?: FetchOptions): Promise<Category[]>;
|
||||
/**
|
||||
* API Helper for {@link .getTopicItemsOfCategory}
|
||||
* Discourse does not provide an API for fetching category information for a specific category.
|
||||
* Instead, all that it provides is a way of getting the topics for a specific category.
|
||||
*/
|
||||
protected getCategoryResponse(categoryID: number, opts?: FetchOptions): Promise<CategoryResponse>;
|
||||
/**
|
||||
* Fetch the partial information, for all topics of a specific category
|
||||
*/
|
||||
getTopicItemsOfCategory(categoryID: number, opts?: FetchOptions): Promise<TopicItem[]>;
|
||||
/**
|
||||
* Fetch the partial information, for all topics of specific categoires
|
||||
*/
|
||||
getTopicItemsOfCategories(categoryIDs: number[], opts?: FetchOptions): Promise<TopicItem[]>;
|
||||
/**
|
||||
* Fetch the partial information, for all topics of the forum
|
||||
*/
|
||||
getTopicItems(opts?: FetchOptions): Promise<TopicItem[]>;
|
||||
/**
|
||||
* Fetch the whole information, for a specific topic of the forum
|
||||
*/
|
||||
getTopic(id: number, opts?: FetchOptions): Promise<TopicResponse>;
|
||||
/**
|
||||
* Fetch the whole information, for all topics, or specific topics, of the forum
|
||||
*/
|
||||
getTopics(topicIDs?: number[] | null, opts?: FetchOptions): Promise<TopicItem[] | TopicResponse[]>;
|
||||
updateTopicVisibility(topicID: number, listed?: boolean, visible?: TPostStatus): Promise<TPostStatusUpdate>;
|
||||
updateTopicTimestamp(topicID: number, timestamp: Date | string | number, token: string): Promise<any>;
|
||||
/**
|
||||
* API Helper for {@link .getPostItemsOfTopic}
|
||||
*/
|
||||
protected getPostItemsOfTopicResponse(topicID: number, opts?: FetchOptions): Promise<PostsResponse>;
|
||||
_createUser(name: any, email: any, pUserGroup: any): Promise<ICreateUserResponse>;
|
||||
getUsers(page: any): Promise<IDiscourseUser>;
|
||||
getUser(id: any): Promise<IUserDetail>;
|
||||
/**
|
||||
* Fetch the partial information, for all posts of a specific topic
|
||||
*/
|
||||
getPostItemsOfTopic(topicID: number, opts?: FetchOptions): Promise<PostItem[]>;
|
||||
/**
|
||||
* Fetch the partial information, for all posts of specific topics
|
||||
*/
|
||||
getPostItemsOfTopics(topicIDs: number[], opts?: FetchOptions): Promise<PostItem[]>;
|
||||
/**
|
||||
* Fetch the partial information, for all posts of a specific category
|
||||
*/
|
||||
getPostItemsOfCategory(categoryID: number, opts?: FetchOptions): Promise<PostItem[]>;
|
||||
/**
|
||||
* Fetch the partial information, for all posts of specific categories
|
||||
*/
|
||||
getPostItemsOfCategories(categoryIDs: number[], opts?: FetchOptions): Promise<PostItem[]>;
|
||||
/**
|
||||
* Fetch the partial information, for all posts of the forum
|
||||
*/
|
||||
getPostItems(opts?: FetchOptions): Promise<PostItem[]>;
|
||||
/**
|
||||
* Fetch the whole information, for a specific post of the forum
|
||||
*/
|
||||
getPost(id: number, opts?: FetchOptions): Promise<PostResponse>;
|
||||
createReply(postId: any, raw: any, category: any): Promise<any>;
|
||||
changeOwner(postId: string | number, topicId: string | number, owner: string): Promise<any>;
|
||||
createUser(data: any): Promise<ICreateUserResponse>;
|
||||
getUserByUsername(username: any): Promise<IUserDetail>;
|
||||
setUserAvatar(user_name: any, upload_id: any): Promise<PostResponse[]>;
|
||||
updateUser(user_name: any, args: any): Promise<PostResponse[]>;
|
||||
updateGroup(user_name: any, group: any): Promise<any>;
|
||||
upload(userId: any, file: any): Promise<any[]>;
|
||||
uploadFile(userId: any, file: any): Promise<PostResponse[]>;
|
||||
/**
|
||||
* Fetch the whole information, for all posts, or specific posts, of the forum
|
||||
*/
|
||||
getPosts(postIDs?: number[] | null, opts?: FetchOptions): Promise<PostResponse[]>;
|
||||
createPost(title: string, raw: string, category: number): Promise<unknown>;
|
||||
/**
|
||||
* Update a post with the content
|
||||
* @param postID the identifier of the post to update
|
||||
* @param content the new raw content for the post
|
||||
* @param reason the reason, if provided, for modifying the post
|
||||
* @param old if the old raw content is provided, then the update verified that you are working with the latest post content before applying the update
|
||||
*/
|
||||
updatePost(postID: number, content: string, reason?: string, old?: string): Promise<PostUpdateItem>;
|
||||
/**
|
||||
* Update post meta
|
||||
*/
|
||||
updateTopic(postId: number, category_id: number, title: string, tags?: string[]): Promise<TopicUpdateBasicTopic>;
|
||||
rebakePost(postID: number): Promise<any>;
|
||||
/**
|
||||
* Modify a post using a modifier
|
||||
*/
|
||||
modifyPost(post: PostResponse, modifier: PostModifier): Promise<PostUpdateItem | null>;
|
||||
/**
|
||||
* Modify a post (via its post identifier) using a modifier
|
||||
*/
|
||||
modifyPostID(post: number, modifier: PostModifier): Promise<PostUpdateItem>;
|
||||
/**
|
||||
* Modify a post (via fetching the whole post from the partial post identifier) using a modifier
|
||||
*/
|
||||
modifyPostItem(post: PostItem, modifier: PostModifier): Promise<PostUpdateItem>;
|
||||
/**
|
||||
* Run the post modifier on all specified posts
|
||||
*/
|
||||
modifyPosts(posts: PostResponse[], modifier: PostModifier): Promise<PostUpdateItem[]>;
|
||||
/**
|
||||
* Fetch the partial information, for all posts of a specific topic
|
||||
* Alias of {@link .getPostItemsOfTopic}.
|
||||
*/
|
||||
getThread(topicID: number, opts?: FetchOptions): Promise<Thread>;
|
||||
/**
|
||||
* Fetch the partial information, for all posts of specific topics, grouped by topic
|
||||
*/
|
||||
getThreads(topicIDs: number[], opts?: FetchOptions): Promise<Thread[]>;
|
||||
/**
|
||||
* Fetch the partial information, for all posts of specific categories, grouped by topic
|
||||
*/
|
||||
getThreadsOfCategory(categoryID: number, opts?: FetchOptions): Promise<Thread[]>;
|
||||
/**
|
||||
* Fetch the partial information, for all posts of specific categories, grouped by category, then topic
|
||||
*/
|
||||
getThreadsOfCategories(categoryIDs: number[], opts?: FetchOptions): Promise<Thread[][]>;
|
||||
updateUserProfile(userId: any, prefs: UserPreferencesUpdate): Promise<any>;
|
||||
}
|
||||
export declare const Instance: (config?: IDiscourseConfig, key?: EDiscourseConfigKey) => Discourser;
|
||||
831
packages/discourse/dist/src/lib/discourse/index.js
vendored
Normal file
831
packages/discourse/dist/src/lib/discourse/index.js
vendored
Normal file
@ -0,0 +1,831 @@
|
||||
import { MODULE_NAME } from '../../constants.js';
|
||||
import { CONFIG_DEFAULT } from '@polymech/commons';
|
||||
import { createLogger } from '@polymech/log';
|
||||
const logger = createLogger(MODULE_NAME);
|
||||
import { sync as write } from '@polymech/fs/write';
|
||||
import { sync as read } from '@polymech/fs/write';
|
||||
import { sync as exists } from '@polymech/fs/exists';
|
||||
import PromisePool from 'native-promise-pool';
|
||||
import { join } from 'path';
|
||||
import * as _axios from 'axios';
|
||||
const axios = _axios.default || _axios;
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import * as _FormData from 'form-data';
|
||||
const FormData = _FormData.default || _FormData;
|
||||
import * as https from 'https';
|
||||
import * as _fetch from 'isomorphic-unfetch';
|
||||
const fetch = _fetch.default || _fetch;
|
||||
export const escape = (path) => path.replace(/[^\w]/g, '-').replace(/-+/, '-');
|
||||
import { generate } from 'generate-password';
|
||||
/**
|
||||
* Discourser is an API Client for the [Discourse API](https://docs.discourse.org)
|
||||
* It special features are:
|
||||
* - TypeScript Types
|
||||
* - Respecting Rate Limits
|
||||
* - Optional Heavy Caching
|
||||
* - Post Modifiers (can be used for global find and replace across all posts on the forum)
|
||||
*/
|
||||
export class Discourser {
|
||||
host;
|
||||
key;
|
||||
username;
|
||||
cache;
|
||||
useCache;
|
||||
dry;
|
||||
pool;
|
||||
/**
|
||||
* Construct our Discourser instance
|
||||
* See {@link IDiscourserConfig} for available configuration.
|
||||
*/
|
||||
constructor(config) {
|
||||
this.host = config.host;
|
||||
this.key = config.key;
|
||||
this.username = config.username;
|
||||
this.cache = config.cache;
|
||||
this.useCache = config.useCache;
|
||||
this.dry = config.dry || false;
|
||||
this.pool = new PromisePool(config.rateLimitConcurrency || 60);
|
||||
}
|
||||
/** Get the URL of a topic */
|
||||
getTopicURL(topic) {
|
||||
if (typeof topic === 'number') {
|
||||
return `${this.host}/t/${topic}`;
|
||||
}
|
||||
return `${this.host}/t/${topic.slug}/${topic.id}`;
|
||||
}
|
||||
/** Fetch a discourse API URL, with rate limit concurrency and optional caching */
|
||||
async fetch({ url, useCache, request }) {
|
||||
// check if cache is enabled
|
||||
useCache = false;
|
||||
const cache = this.cache &&
|
||||
(request?.method || 'get') === 'get' &&
|
||||
join(this.cache, escape(url));
|
||||
// check if we should and can read from cache
|
||||
if (cache &&
|
||||
this.useCache !== false &&
|
||||
useCache !== false &&
|
||||
(exists(cache))) {
|
||||
const result = read(cache, 'json');
|
||||
return result;
|
||||
}
|
||||
// fetch
|
||||
const result = await this.pool.open(() => this._fetch({ url, request }));
|
||||
// write to cache if cache is enabled
|
||||
if (cache) {
|
||||
write(cache, result);
|
||||
}
|
||||
// return the result
|
||||
return result;
|
||||
}
|
||||
/** Fetch a discourse API URL, with rate limit retries */
|
||||
async _post(url, data) {
|
||||
const opts = {
|
||||
headers: {
|
||||
'Api-Key': this.key,
|
||||
'Api-Username': this.username,
|
||||
},
|
||||
};
|
||||
let d = data;
|
||||
const res = await axios.post(url, d, {
|
||||
headers: opts.headers
|
||||
});
|
||||
// fetch text then parse as json, so that when errors occur we can output what it was
|
||||
// rather than being stuck with errors like these:
|
||||
// FetchError: invalid json response body at https://discuss.bevry.me/posts/507.json reason: Unexpected token < in JSON at position 0
|
||||
const text = await res.data;
|
||||
// check if there are errors
|
||||
if (typeof data.errors !== 'undefined') {
|
||||
// check if the error is a rate limit
|
||||
const wait = data.extras?.wait_seconds;
|
||||
if (wait != null) {
|
||||
// if it was, try later
|
||||
// return await retry(wait + 1)
|
||||
}
|
||||
// otherwise fail
|
||||
// logger.debug({ data, url, opts })
|
||||
return Promise.reject(new Error(`fetch of [${url}] received failed response:\n${data}`));
|
||||
}
|
||||
return text;
|
||||
}
|
||||
/** Fetch a discourse API URL, with rate limit retries */
|
||||
async _fetch({ url, request }) {
|
||||
const httpsAgent = new https.Agent({
|
||||
rejectUnauthorized: false,
|
||||
});
|
||||
const opts = {
|
||||
...request,
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
'Api-Key': this.key,
|
||||
'Api-Username': this.username,
|
||||
...request?.headers
|
||||
},
|
||||
rejectUnauthorized: false,
|
||||
agent: httpsAgent
|
||||
};
|
||||
const retry = (seconds) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
setTimeout(() => this._fetch({ url, request })
|
||||
.then(resolve)
|
||||
.catch(reject), (seconds || 60) * 1000);
|
||||
});
|
||||
};
|
||||
try {
|
||||
const res = await fetch(url, opts);
|
||||
// fetch text then parse as json, so that when errors occur we can output what it was
|
||||
// rather than being stuck with errors like these:
|
||||
// FetchError: invalid json response body at https://discuss.bevry.me/posts/507.json reason: Unexpected token < in JSON at position 0
|
||||
const text = await res.text();
|
||||
let data;
|
||||
try {
|
||||
data = JSON.parse(text);
|
||||
}
|
||||
catch (err) {
|
||||
// check if it was cloudflare reporting that the server has been hit too hard
|
||||
if (text.includes('Please try again in a few minutes')) {
|
||||
logger.debug('server has stalled, trying again in a minute');
|
||||
return await retry(60);
|
||||
}
|
||||
// otherwise log the error page and die
|
||||
// logger.debug({ text, url , opts })
|
||||
return Promise.reject(logger.error(text, url, opts, err) &&
|
||||
new Error(`fetch of [${url}] received invalid response:\n${text}`));
|
||||
}
|
||||
// check if there are errors
|
||||
if (typeof data.errors !== 'undefined') {
|
||||
// check if the error is a rate limit
|
||||
const wait = data.extras?.wait_seconds;
|
||||
if (wait != null) {
|
||||
// if it was, try later
|
||||
return await retry(wait + 1);
|
||||
}
|
||||
// otherwise fail
|
||||
// logger.debug({ data, url, opts })
|
||||
return Promise.reject(new Error(`fetch of [${url}] received failed response:\n${data}`));
|
||||
}
|
||||
return data;
|
||||
}
|
||||
catch (err) {
|
||||
// logger.debug({ err, url, opts })
|
||||
return Promise.reject(logger.error(`fetch of [${url}] failed with error`, err));
|
||||
}
|
||||
}
|
||||
// =================================
|
||||
// Search
|
||||
/**
|
||||
* API Helper for {@link .search}
|
||||
* https://docs.discourse.org/#tag/Search/operation/search
|
||||
*/
|
||||
async search(query, params = '', opts = {}) {
|
||||
let url = `${this.host}/search.json?q=${encodeURIComponent(query)} ${encodeURIComponent(params)}`;
|
||||
return await this.fetch({ url, ...opts });
|
||||
}
|
||||
// =================================
|
||||
// Tags
|
||||
/**
|
||||
* API Helper for {@link .getTags}
|
||||
*/
|
||||
async getTagsResponse(opts = {}) {
|
||||
const url = `${this.host}/tags.json`;
|
||||
return await this.fetch({ url, ...opts });
|
||||
}
|
||||
/**
|
||||
* Fetch the whole information, for all categories of the forum
|
||||
*/
|
||||
async getTags(opts = {}) {
|
||||
const response = await this.getTagsResponse(opts);
|
||||
const tags = response.tags;
|
||||
return tags;
|
||||
}
|
||||
async createTag(name) {
|
||||
const url = `${this.host}/tag_groups.json`;
|
||||
try {
|
||||
return await this._post(url, {
|
||||
name
|
||||
});
|
||||
}
|
||||
catch (error) {
|
||||
debugger;
|
||||
}
|
||||
}
|
||||
// =================================
|
||||
// CATEGORIES
|
||||
/**
|
||||
* API Helper for {@link .getCategories}
|
||||
*/
|
||||
async getCategoriesResponse(opts = {}) {
|
||||
const url = `${this.host}/categories.json` +
|
||||
(opts.include_subcategories ? '?include_subcategories=true' : '');
|
||||
return await this.fetch({ url, ...opts });
|
||||
}
|
||||
/**
|
||||
* Fetch the whole information, for all categories of the forum
|
||||
*/
|
||||
async getCategories(opts = {}) {
|
||||
const response = await this.getCategoriesResponse(opts);
|
||||
const categories = response.category_list.categories;
|
||||
return categories;
|
||||
}
|
||||
/**
|
||||
* API Helper for {@link .getTopicItemsOfCategory}
|
||||
* Discourse does not provide an API for fetching category information for a specific category.
|
||||
* Instead, all that it provides is a way of getting the topics for a specific category.
|
||||
*/
|
||||
async getCategoryResponse(categoryID, opts = {}) {
|
||||
const url = `${this.host}/c/${categoryID}.json` +
|
||||
(opts.page !== 0 ? `?page=${opts.page}` : '');
|
||||
return await this.fetch({ url, ...opts });
|
||||
}
|
||||
// =================================
|
||||
// TOPICS
|
||||
/**
|
||||
* Fetch the partial information, for all topics of a specific category
|
||||
*/
|
||||
async getTopicItemsOfCategory(categoryID, opts = {}) {
|
||||
// prepare and fetch
|
||||
let page = opts.page || 0;
|
||||
const response = await this.getCategoryResponse(categoryID, {
|
||||
...opts,
|
||||
page,
|
||||
});
|
||||
let topics = response.topic_list.topics;
|
||||
// fetch the next page
|
||||
if (topics.length === response.topic_list.per_page) {
|
||||
page += 1;
|
||||
const more = await this.getTopicItemsOfCategory(categoryID, {
|
||||
...opts,
|
||||
page,
|
||||
});
|
||||
topics.push(...more);
|
||||
}
|
||||
// if we are the first page, then output count as we now have all of them
|
||||
if (page === 0) {
|
||||
const ids = topics.map((i) => i.id);
|
||||
}
|
||||
topics = topics.filter((t) => t.visible === true);
|
||||
return topics;
|
||||
}
|
||||
/**
|
||||
* Fetch the partial information, for all topics of specific categoires
|
||||
*/
|
||||
async getTopicItemsOfCategories(categoryIDs, opts = {}) {
|
||||
// fetch topic items for specific categories
|
||||
try {
|
||||
const topicsOfCategories = await Promise.all(categoryIDs.map((id) => this.getTopicItemsOfCategory(id, opts)));
|
||||
// @ts-ignore
|
||||
return topicsOfCategories.flat();
|
||||
}
|
||||
catch (error) {
|
||||
logger.error(error);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Fetch the partial information, for all topics of the forum
|
||||
*/
|
||||
async getTopicItems(opts = {}) {
|
||||
const categories = await this.getCategories();
|
||||
const categoryIDs = categories.map((i) => i.id);
|
||||
return this.getTopicItemsOfCategories(categoryIDs, opts);
|
||||
}
|
||||
/**
|
||||
* Fetch the whole information, for a specific topic of the forum
|
||||
*/
|
||||
getTopic(id, opts = {}) {
|
||||
const url = `${this.host}/t/${id}.json`;
|
||||
return this.fetch({ url, ...opts });
|
||||
}
|
||||
/**
|
||||
* Fetch the whole information, for all topics, or specific topics, of the forum
|
||||
*/
|
||||
async getTopics(topicIDs, opts = {}) {
|
||||
// if no topics, use all topics
|
||||
if (!topicIDs) {
|
||||
const topics = await this.getTopicItems(opts);
|
||||
topicIDs = topics.map((i) => i.id);
|
||||
}
|
||||
// fetch whole topics
|
||||
return Promise.all(topicIDs.map((id) => this.getTopic(id, opts)));
|
||||
}
|
||||
async updateTopicVisibility(topicID, listed = true, visible = 'visible') {
|
||||
const url = `${this.host}/t/${topicID}/status`;
|
||||
let ret = await fetch(url, {
|
||||
"headers": {
|
||||
"content-type": "application/x-www-form-urlencoded; charset=UTF-8",
|
||||
'Api-Key': this.key,
|
||||
'Api-Username': this.username
|
||||
},
|
||||
"body": `status=${visible}&enabled=${listed}`,
|
||||
"method": "PUT"
|
||||
});
|
||||
return ret;
|
||||
}
|
||||
async updateTopicTimestamp(topicID, timestamp, token) {
|
||||
let time;
|
||||
if (typeof timestamp === 'number') {
|
||||
time = timestamp;
|
||||
}
|
||||
else if (typeof timestamp === 'number') {
|
||||
time = Number(timestamp);
|
||||
}
|
||||
else if (timestamp instanceof Date) {
|
||||
// ms to seconds
|
||||
time = timestamp.getTime() / 1000;
|
||||
}
|
||||
else {
|
||||
return Promise.reject(new Error('invalid timestamp format'));
|
||||
}
|
||||
const url = `${this.host}/t/${topicID}/change-timestamp`;
|
||||
// prepare the request
|
||||
const request = {
|
||||
timestamp: time,
|
||||
};
|
||||
// send the update
|
||||
logger.debug('updating', topicID, 'topic timestamp with', request);
|
||||
//const url = `${this.host}/t/${topicID}/change-timestamp`
|
||||
const response = await this.fetch({
|
||||
url,
|
||||
request: {
|
||||
method: 'put',
|
||||
body: `timestamp=${time}`,
|
||||
headers: {
|
||||
'Api-Key': this.key,
|
||||
'Api-Username': this.username,
|
||||
"content-type": "application/x-www-form-urlencoded; charset=UTF-8"
|
||||
}
|
||||
}
|
||||
});
|
||||
// check it
|
||||
if (response.success !== 'OK') {
|
||||
return Promise.reject(new Error(`timestamp update of topic ${topicID} failed:\n${{
|
||||
url,
|
||||
request,
|
||||
response,
|
||||
}}`));
|
||||
}
|
||||
return response;
|
||||
}
|
||||
// =================================
|
||||
// POSTS
|
||||
/**
|
||||
* API Helper for {@link .getPostItemsOfTopic}
|
||||
*/
|
||||
async getPostItemsOfTopicResponse(topicID, opts = {}) {
|
||||
const url = `${this.host}/t/${topicID}/posts.json`;
|
||||
const response = await this.fetch({ url, ...opts });
|
||||
return response;
|
||||
}
|
||||
async _createUser(name, email, pUserGroup) {
|
||||
const pwd = generate({
|
||||
length: 10,
|
||||
numbers: true
|
||||
});
|
||||
let user = await this.createUser({
|
||||
"name": name,
|
||||
"email": email,
|
||||
"password": pwd,
|
||||
"username": name,
|
||||
"active": true,
|
||||
"approved": true,
|
||||
"user_fields[1]": true
|
||||
});
|
||||
if (user && user.user_id) {
|
||||
await this.updateGroup(name, pUserGroup);
|
||||
return { ...user, password: pwd };
|
||||
}
|
||||
else {
|
||||
if (user && user.message && user.message == 'Username must be unique\nPrimary email has already been taken') {
|
||||
return null;
|
||||
}
|
||||
else if (user && user.message && user.message == 'Your account is activated and ready to use.') {
|
||||
if (user.user_id) {
|
||||
return { ...user, password: pwd };
|
||||
}
|
||||
return null;
|
||||
}
|
||||
else {
|
||||
console.log('cant create user ' + name, user);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
async getUsers(page) {
|
||||
const url = `${this.host}/admin/users/list/active.json?page=` + page;
|
||||
const response = await this.fetch({ url });
|
||||
return response;
|
||||
}
|
||||
async getUser(id) {
|
||||
const url = `${this.host}/admin/users/${id}.json`;
|
||||
const response = await this.fetch({ url });
|
||||
return response;
|
||||
}
|
||||
/**
|
||||
* Fetch the partial information, for all posts of a specific topic
|
||||
*/
|
||||
async getPostItemsOfTopic(topicID, opts = {}) {
|
||||
const response = await this.getPostItemsOfTopicResponse(topicID, opts);
|
||||
const posts = response.post_stream.posts;
|
||||
const ids = posts.map((i) => i.id);
|
||||
return posts;
|
||||
}
|
||||
/**
|
||||
* Fetch the partial information, for all posts of specific topics
|
||||
*/
|
||||
async getPostItemsOfTopics(topicIDs, opts = {}) {
|
||||
// fetch post items for specific topics
|
||||
const postItemsOfTopics = await Promise.all(topicIDs.map((id) => this.getPostItemsOfTopic(id, opts)));
|
||||
// @ts-ignore
|
||||
return postItemsOfTopics.flat();
|
||||
}
|
||||
/**
|
||||
* Fetch the partial information, for all posts of a specific category
|
||||
*/
|
||||
async getPostItemsOfCategory(categoryID, opts = {}) {
|
||||
// fetch topics for the category
|
||||
const topics = await this.getTopicItemsOfCategory(categoryID, opts);
|
||||
const topicIDs = topics.map((i) => i.id);
|
||||
// fetch
|
||||
const posts = await this.getPostItemsOfTopics(topicIDs);
|
||||
const ids = posts.map((i) => i.id);
|
||||
return posts;
|
||||
}
|
||||
/**
|
||||
* Fetch the partial information, for all posts of specific categories
|
||||
*/
|
||||
async getPostItemsOfCategories(categoryIDs, opts = {}) {
|
||||
// fetch post items for specific categories
|
||||
const postItemsOfCategories = await Promise.all(categoryIDs.map((id) => this.getPostItemsOfCategory(id, opts)));
|
||||
// @ts-ignore
|
||||
return postItemsOfCategories.flat();
|
||||
}
|
||||
/**
|
||||
* Fetch the partial information, for all posts of the forum
|
||||
*/
|
||||
async getPostItems(opts = {}) {
|
||||
const categories = await this.getCategories();
|
||||
const categoryIDs = categories.map((i) => i.id);
|
||||
return this.getPostItemsOfCategories(categoryIDs, opts);
|
||||
}
|
||||
/**
|
||||
* Fetch the whole information, for a specific post of the forum
|
||||
*/
|
||||
getPost(id, opts = {}) {
|
||||
const url = `${this.host}/posts/${id}.json`;
|
||||
return this.fetch({ url, ...opts });
|
||||
}
|
||||
async createReply(postId, raw, category) {
|
||||
const url = `${this.host}/posts.json`;
|
||||
const data = new URLSearchParams();
|
||||
data.append('raw', raw);
|
||||
data.append('unlist_topic', 'false');
|
||||
data.append('category', String(category));
|
||||
data.append('topic_id', String(postId));
|
||||
data.append('is_warning', 'false');
|
||||
data.append('archetype', 'regular');
|
||||
data.append('featured_link', '');
|
||||
data.append('shared_draft', 'false');
|
||||
data.append('nested_post', 'true');
|
||||
return await axios.post(url, data.toString(), {
|
||||
headers: {
|
||||
'content-type': 'application/x-www-form-urlencoded; charset=UTF-8',
|
||||
'Api-Key': this.key,
|
||||
'Api-Username': this.username
|
||||
}
|
||||
});
|
||||
}
|
||||
async changeOwner(postId, topicId, owner) {
|
||||
const url = `${this.host}/t/${topicId}/change-owner.json`;
|
||||
const body = `post_ids%5B%5D=${postId}&username=${owner}`;
|
||||
return await axios.post(url, body, {
|
||||
headers: {
|
||||
'content-type': 'application/x-www-form-urlencoded; charset=UTF-8',
|
||||
'Api-Key': this.key,
|
||||
'Api-Username': this.username
|
||||
}
|
||||
});
|
||||
}
|
||||
async createUser(data) {
|
||||
const url = `${this.host}/users`;
|
||||
return await this._post(url, data);
|
||||
}
|
||||
async getUserByUsername(username) {
|
||||
const url = `${this.host}/u/${username}.json`;
|
||||
const response = await this.fetch({ url });
|
||||
return response.user;
|
||||
}
|
||||
async setUserAvatar(user_name, upload_id) {
|
||||
// fetch whole posts
|
||||
const url = `${this.host}/u/${user_name}/preferences/avatar/pick.json`;
|
||||
return await axios.put(url, {
|
||||
upload_id,
|
||||
username: user_name,
|
||||
type: 'uploaded'
|
||||
}, {
|
||||
headers: {
|
||||
'accept': 'application/json',
|
||||
'Accept-Language': 'en-US,en;q=0.8',
|
||||
'Api-Key': this.key,
|
||||
'Api-Username': this.username
|
||||
}
|
||||
});
|
||||
}
|
||||
async updateUser(user_name, args) {
|
||||
const url = `${this.host}/u/${user_name}.json`;
|
||||
return await axios.put(url, {
|
||||
...args
|
||||
}, {
|
||||
headers: {
|
||||
'accept': 'application/json',
|
||||
'Accept-Language': 'en-US,en;q=0.8',
|
||||
'Api-Key': this.key,
|
||||
'Api-Username': this.username
|
||||
}
|
||||
});
|
||||
}
|
||||
async updateGroup(user_name, group) {
|
||||
// fetch whole posts
|
||||
const url = `${this.host}/groups/${group}/members.json`;
|
||||
const t = axios.put(url, {
|
||||
usernames: user_name,
|
||||
notify_users: false
|
||||
}, {
|
||||
headers: {
|
||||
'accept': 'application/json',
|
||||
'Accept-Language': 'en-US,en;q=0.8',
|
||||
'Api-Key': this.key,
|
||||
'Api-Username': this.username
|
||||
}
|
||||
});
|
||||
t.then((d) => {
|
||||
}).catch((e) => {
|
||||
//debugger;
|
||||
});
|
||||
return t;
|
||||
}
|
||||
async upload(userId, file) {
|
||||
// fetch whole posts
|
||||
const url = `${this.host}/uploads.json`;
|
||||
let data = new FormData();
|
||||
const fsData = path.parse(file);
|
||||
data.append('file', file, fsData.base);
|
||||
data.append('user_id', userId);
|
||||
data.append('upload_type', 'avatar');
|
||||
data.append('file', fs.createReadStream(file));
|
||||
return (await axios.post(url, data, {
|
||||
headers: {
|
||||
'accept': 'application/json',
|
||||
'Accept-Language': 'en-US,en;q=0.8',
|
||||
'Content-Type': `multipart/form-data; boundary=${data._boundary}`,
|
||||
'Api-Key': this.key,
|
||||
'Api-Username': this.username
|
||||
}
|
||||
})).data;
|
||||
}
|
||||
async uploadFile(userId, file) {
|
||||
// fetch whole posts
|
||||
const url = `${this.host}/uploads.json`;
|
||||
let data = new FormData();
|
||||
const fsData = path.parse(file);
|
||||
data.append('file', file, fsData.base);
|
||||
data.append('user_id', userId);
|
||||
data.append('upload_type', 'composer');
|
||||
data.append('file', fs.createReadStream(file));
|
||||
return (await axios.post(url, data, {
|
||||
headers: {
|
||||
'accept': 'application/json',
|
||||
'Accept-Language': 'en-US,en;q=0.8',
|
||||
'Content-Type': `multipart/form-data; boundary=${data._boundary}`,
|
||||
'Api-Key': this.key,
|
||||
'Api-Username': this.username
|
||||
}
|
||||
})).data;
|
||||
}
|
||||
// =================================
|
||||
// POSTS: UPDATING
|
||||
/**
|
||||
* Fetch the whole information, for all posts, or specific posts, of the forum
|
||||
*/
|
||||
async getPosts(postIDs, opts = {}) {
|
||||
// if no posts, use all
|
||||
if (!postIDs) {
|
||||
const posts = await this.getPostItems(opts);
|
||||
postIDs = posts.map((i) => i.id);
|
||||
}
|
||||
// fetch whole posts
|
||||
return await Promise.all(postIDs.map((id) => this.getPost(id, opts)));
|
||||
}
|
||||
async createPost(title, raw, category) {
|
||||
// fetch whole posts
|
||||
const url = `${this.host}/posts`;
|
||||
return new Promise((resolve) => {
|
||||
return this._post(url, { raw, title, category }).then((d) => {
|
||||
resolve(d);
|
||||
}).catch((e) => {
|
||||
resolve(e.response.data);
|
||||
});
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Update a post with the content
|
||||
* @param postID the identifier of the post to update
|
||||
* @param content the new raw content for the post
|
||||
* @param reason the reason, if provided, for modifying the post
|
||||
* @param old if the old raw content is provided, then the update verified that you are working with the latest post content before applying the update
|
||||
*/
|
||||
async updatePost(postID, content, reason = 'api update', old) {
|
||||
// prepare the request
|
||||
const data = {
|
||||
post: {
|
||||
raw: content,
|
||||
edit_reason: reason,
|
||||
},
|
||||
};
|
||||
if (old) {
|
||||
data.post.raw_old = old;
|
||||
}
|
||||
// send the update
|
||||
const url = `${this.host}/posts/${postID}.json`;
|
||||
const response = await this.fetch({
|
||||
url,
|
||||
request: {
|
||||
method: 'put',
|
||||
body: JSON.stringify(data),
|
||||
},
|
||||
});
|
||||
// return the response
|
||||
return response.post;
|
||||
}
|
||||
/**
|
||||
* Update post meta
|
||||
*/
|
||||
async updateTopic(postId, category_id, title, tags) {
|
||||
const data = {
|
||||
title,
|
||||
tags: tags || [],
|
||||
featuredLink: null,
|
||||
category_id
|
||||
};
|
||||
const url = `${this.host}/t/${postId}.json`;
|
||||
const response = await this.fetch({
|
||||
url,
|
||||
request: {
|
||||
method: 'put',
|
||||
body: JSON.stringify(data),
|
||||
},
|
||||
});
|
||||
return response.basic_topic;
|
||||
}
|
||||
async rebakePost(postID) {
|
||||
const url = `${this.host}/posts/${postID}/rebake`;
|
||||
logger.debug('rebaking', postID);
|
||||
const response = await this.fetch({
|
||||
url,
|
||||
request: {
|
||||
method: 'put'
|
||||
}
|
||||
});
|
||||
logger.debug('rebaked', postID);
|
||||
return response;
|
||||
}
|
||||
/**
|
||||
* Modify a post using a modifier
|
||||
*/
|
||||
async modifyPost(post, modifier) {
|
||||
// check if we received a post item, instead of a post response
|
||||
if (post.raw == null) {
|
||||
post = await this.getPost(post.id);
|
||||
}
|
||||
// check
|
||||
if (!post.raw) {
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
// replace
|
||||
const { result, reason } = modifier(post);
|
||||
if (result === post.raw) {
|
||||
// if (post.cooked) {
|
||||
// const { result, reason } = modifier(post.cooked)
|
||||
// if (result !== post.cooked) {
|
||||
// logger.debug(
|
||||
// 'replace did have an effect on cooked post',
|
||||
// postID,
|
||||
// 'so rebaking it'
|
||||
// )
|
||||
// return await this.rebakePost(postID)
|
||||
// }
|
||||
// }
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
// dry
|
||||
if (this.dry) {
|
||||
return Promise.resolve({
|
||||
...post,
|
||||
result,
|
||||
reason,
|
||||
});
|
||||
}
|
||||
// update
|
||||
try {
|
||||
return await this.updatePost(post.id, result, reason, post.raw);
|
||||
}
|
||||
catch (err) {
|
||||
if (err.message.includes('That post was edited by another user and your changes can no longer be saved.')) {
|
||||
return this.modifyPost(await this.getPost(post.id, { useCache: false }), modifier);
|
||||
}
|
||||
logger.error(err);
|
||||
return Promise.reject(`modifying post ${post.id} failed`);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Modify a post (via its post identifier) using a modifier
|
||||
*/
|
||||
async modifyPostID(post, modifier) {
|
||||
return this.modifyPost(await this.getPost(post), modifier);
|
||||
}
|
||||
/**
|
||||
* Modify a post (via fetching the whole post from the partial post identifier) using a modifier
|
||||
*/
|
||||
async modifyPostItem(post, modifier) {
|
||||
return this.modifyPost(await this.getPost(post.id), modifier);
|
||||
}
|
||||
/**
|
||||
* Run the post modifier on all specified posts
|
||||
*/
|
||||
async modifyPosts(posts, modifier) {
|
||||
const updates = await Promise.all(posts.map((post) => this.modifyPost(post, modifier)));
|
||||
const updated = updates.filter((i) => i);
|
||||
return updated;
|
||||
}
|
||||
// =================================
|
||||
// THREADS
|
||||
/**
|
||||
* Fetch the partial information, for all posts of a specific topic
|
||||
* Alias of {@link .getPostItemsOfTopic}.
|
||||
*/
|
||||
async getThread(topicID, opts = {}) {
|
||||
const topic = await this.getTopic(topicID, opts);
|
||||
const [post, ...replies] = await this.getPostItemsOfTopic(topicID, opts);
|
||||
return {
|
||||
topic,
|
||||
post,
|
||||
replies,
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Fetch the partial information, for all posts of specific topics, grouped by topic
|
||||
*/
|
||||
async getThreads(topicIDs, opts = {}) {
|
||||
return await Promise.all(topicIDs.map((id) => this.getThread(id, opts)));
|
||||
}
|
||||
/**
|
||||
* Fetch the partial information, for all posts of specific categories, grouped by topic
|
||||
*/
|
||||
async getThreadsOfCategory(categoryID, opts = {}) {
|
||||
// fetch topics for the category
|
||||
const topics = await this.getTopicItemsOfCategory(categoryID, opts);
|
||||
const topicIDs = topics.map((i) => i.id);
|
||||
// return threads
|
||||
return await this.getThreads(topicIDs);
|
||||
}
|
||||
/**
|
||||
* Fetch the partial information, for all posts of specific categories, grouped by category, then topic
|
||||
*/
|
||||
async getThreadsOfCategories(categoryIDs, opts = {}) {
|
||||
return await Promise.all(categoryIDs.map((id) => this.getThreadsOfCategory(id, opts)));
|
||||
}
|
||||
async updateUserProfile(userId, prefs) {
|
||||
const url = `${this.host}/u/${userId}.json`;
|
||||
let data = new FormData();
|
||||
data.append('bio_raw', prefs.bio_raw);
|
||||
prefs.location && data.append('location', prefs.location);
|
||||
prefs.website && data.append('website', prefs.website);
|
||||
return await axios.put(url, data, {
|
||||
headers: {
|
||||
'accept': 'application/json',
|
||||
'Accept-Language': 'en-US,en;q=0.8',
|
||||
"content-type": "application/x-www-form-urlencoded; charset=UTF-8",
|
||||
'Api-Key': this.key,
|
||||
'Api-Username': this.username
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
export const Instance = (config, key = 'discourse_admin') => {
|
||||
return new Discourser(config || CONFIG_DEFAULT()[key]);
|
||||
/*
|
||||
|
||||
d.getTopicItemsOfCategories([cat]).then(posts => {
|
||||
//console.log('posts', posts)
|
||||
let content = "<ul>"
|
||||
posts = posts.map((p) => {
|
||||
const url = `${config.discourse.host}/t/${p.id}`;
|
||||
const title = `${p.fancy_title}`;
|
||||
return `<li><a href="${url}">${title}</a></li>`;
|
||||
}).join('\n');
|
||||
content += posts + "</ul>";
|
||||
resolve(content);
|
||||
|
||||
});
|
||||
*/
|
||||
};
|
||||
//# sourceMappingURL=index.js.map
|
||||
1
packages/discourse/dist/src/lib/discourse/index.js.map
vendored
Normal file
1
packages/discourse/dist/src/lib/discourse/index.js.map
vendored
Normal file
File diff suppressed because one or more lines are too long
697
packages/discourse/dist/src/lib/discourse/types.d.ts
vendored
Normal file
697
packages/discourse/dist/src/lib/discourse/types.d.ts
vendored
Normal file
@ -0,0 +1,697 @@
|
||||
export interface Failure {
|
||||
success: 'OK';
|
||||
}
|
||||
export interface Success {
|
||||
failed: 'FAILED';
|
||||
}
|
||||
export type Response = Failure & Success;
|
||||
export interface Action {
|
||||
can_act: boolean;
|
||||
id: number;
|
||||
count?: number;
|
||||
hidden?: boolean;
|
||||
}
|
||||
export interface Poster {
|
||||
description: string;
|
||||
extras: string;
|
||||
user_id: number;
|
||||
}
|
||||
export interface Person {
|
||||
avatar_template: string;
|
||||
id: number;
|
||||
username: string;
|
||||
}
|
||||
export interface Participant extends Person {
|
||||
post_count: number;
|
||||
}
|
||||
export interface Link {
|
||||
url: string;
|
||||
internal: boolean;
|
||||
reflection: boolean;
|
||||
title: string;
|
||||
clicks: number;
|
||||
}
|
||||
/**
|
||||
* Update a Topic Timestamp
|
||||
* https://docs.discourse.org/#tag/Topics/paths/~1t~1{id}~1change-timestamp/put
|
||||
*/
|
||||
export interface TopicUpdateTimestampRequest {
|
||||
timestamp: number;
|
||||
}
|
||||
export type TopicUpdateTimestampResponse = Response;
|
||||
/**
|
||||
* Update a Post
|
||||
* https://docs.discourse.org/#tag/Posts/paths/~1posts~1{id}.json/put
|
||||
*/
|
||||
export interface PostUpdateResponse {
|
||||
post: PostUpdateItem;
|
||||
}
|
||||
export interface TopicUpdateBasicTopic {
|
||||
fancy_title: string;
|
||||
id: number;
|
||||
posts_count: number;
|
||||
slug: string;
|
||||
title: string;
|
||||
}
|
||||
export interface TopicUpdateResponse {
|
||||
basic_topic: TopicUpdateBasicTopic;
|
||||
}
|
||||
export interface PostUpdateItem {
|
||||
actions_summary: Array<Action>;
|
||||
admin: boolean;
|
||||
avatar_template: string;
|
||||
avg_time: object;
|
||||
can_delete: boolean;
|
||||
can_edit: boolean;
|
||||
can_recover: boolean;
|
||||
can_view_edit_history: boolean;
|
||||
can_wiki: boolean;
|
||||
cooked: string;
|
||||
created_at: string;
|
||||
deleted_at: object;
|
||||
display_username: string;
|
||||
draft_sequence: number;
|
||||
edit_reason: object;
|
||||
hidden_reason_id: object;
|
||||
hidden: boolean;
|
||||
id: number;
|
||||
incoming_link_count: number;
|
||||
moderator: boolean;
|
||||
name: string;
|
||||
post_number: number;
|
||||
post_type: number;
|
||||
primary_group_flair_bg_color: object;
|
||||
primary_group_flair_color: object;
|
||||
primary_group_flair_url: object;
|
||||
primary_group_name: object;
|
||||
quote_count: number;
|
||||
reads: number;
|
||||
reply_count: number;
|
||||
reply_to_post_number: object;
|
||||
score: number;
|
||||
staff: boolean;
|
||||
topic_id: number;
|
||||
topic_slug: string;
|
||||
trust_level: number;
|
||||
updated_at: string;
|
||||
user_deleted: boolean;
|
||||
user_id: number;
|
||||
user_title: object;
|
||||
username: string;
|
||||
version: number;
|
||||
wiki: boolean;
|
||||
yours: boolean;
|
||||
}
|
||||
export interface PostUpdateRequest {
|
||||
post: {
|
||||
raw: string;
|
||||
raw_old?: string;
|
||||
edit_reason?: string;
|
||||
cooked?: string;
|
||||
};
|
||||
}
|
||||
export interface TagsResponse {
|
||||
tags: Tag[];
|
||||
extras: TagsExtras;
|
||||
}
|
||||
export interface TagsExtras {
|
||||
categories: any[];
|
||||
}
|
||||
export interface Tag {
|
||||
count: number;
|
||||
description: null;
|
||||
id: string;
|
||||
name: string;
|
||||
pm_only: boolean;
|
||||
target_tag: null;
|
||||
text: string;
|
||||
}
|
||||
/** https://docs.discourse.org/#tag/Categories/paths/~1categories.json/get */
|
||||
export interface CategoriesResponse {
|
||||
category_list: {
|
||||
can_create_category: boolean;
|
||||
can_create_topic: boolean;
|
||||
categories: Category[];
|
||||
draft_key: string;
|
||||
draft_sequence: number;
|
||||
draft: boolean;
|
||||
};
|
||||
}
|
||||
export interface Category {
|
||||
background_url: string;
|
||||
can_edit: boolean;
|
||||
color: string;
|
||||
description_excerpt: string;
|
||||
description_text: string;
|
||||
description: string;
|
||||
has_children: boolean;
|
||||
id: number;
|
||||
logo_url: string;
|
||||
name: string;
|
||||
notification_level: string;
|
||||
permission: number;
|
||||
position: number;
|
||||
post_count: number;
|
||||
read_restricted: boolean;
|
||||
slug: string;
|
||||
text_color: string;
|
||||
topic_count: number;
|
||||
topic_template: string;
|
||||
topic_url: string;
|
||||
topics_all_time: number;
|
||||
topics_day: number;
|
||||
topics_month: number;
|
||||
topics_week: number;
|
||||
topics_year: number;
|
||||
}
|
||||
/**
|
||||
* Get Single Topic
|
||||
* https://docs.discourse.org/#tag/Topics/paths/~1t~1{id}.json/get
|
||||
*/
|
||||
export interface TopicResponse {
|
||||
actions_summary: Array<Action>;
|
||||
archetype: string;
|
||||
archived: boolean;
|
||||
bookmarked: object;
|
||||
category_id: number;
|
||||
chunk_size: number;
|
||||
closed: boolean;
|
||||
created_at: string;
|
||||
deleted_at: object;
|
||||
deleted_by: object;
|
||||
details: TopicDetails;
|
||||
draft_key: string;
|
||||
draft_sequence: object;
|
||||
draft: object;
|
||||
fancy_title: string;
|
||||
has_summary: boolean;
|
||||
highest_post_number: number;
|
||||
id: number;
|
||||
last_posted_at: object;
|
||||
like_count: number;
|
||||
participant_count: number;
|
||||
pinned_at: string;
|
||||
pinned_globally: boolean;
|
||||
pinned_until: object;
|
||||
pinned: boolean;
|
||||
posts_count: number;
|
||||
reply_count: number;
|
||||
slug: string;
|
||||
tags: string[];
|
||||
title: string;
|
||||
unpinned: object;
|
||||
user_id: number;
|
||||
views: number;
|
||||
visible: boolean;
|
||||
word_count: object;
|
||||
post_stream: {
|
||||
posts: Array<PostItem>;
|
||||
stream: Array<object>;
|
||||
};
|
||||
timeline_lookup: [
|
||||
{
|
||||
'0': Array<object>;
|
||||
}
|
||||
];
|
||||
}
|
||||
export interface TopicDetails {
|
||||
auto_close_at: object;
|
||||
auto_close_based_on_last_post: boolean;
|
||||
auto_close_hours: object;
|
||||
can_flag_topic: boolean;
|
||||
created_by: Person;
|
||||
last_poster: Person;
|
||||
notification_level: number;
|
||||
participants: Array<Participant>;
|
||||
suggested_topics: Array<TopicItem>;
|
||||
}
|
||||
export interface TopicItem {
|
||||
archetype: string;
|
||||
archived: boolean;
|
||||
bookmarked: object;
|
||||
bumped_at: string;
|
||||
bumped: boolean;
|
||||
category_id: number;
|
||||
closed: boolean;
|
||||
created_at: string;
|
||||
excerpt: string;
|
||||
fancy_title: string;
|
||||
has_summary: boolean;
|
||||
highest_post_number: number;
|
||||
id: number;
|
||||
image_url: string;
|
||||
last_posted_at: string;
|
||||
last_poster_username: string;
|
||||
like_count: number;
|
||||
liked: object;
|
||||
pinned_globally: boolean;
|
||||
pinned: boolean;
|
||||
posters: Array<Poster>;
|
||||
posts_count: number;
|
||||
reply_count: number;
|
||||
slug: string;
|
||||
title: string;
|
||||
unpinned: boolean;
|
||||
unseen: boolean;
|
||||
views: number;
|
||||
visible: boolean;
|
||||
}
|
||||
/**
|
||||
* Get Topics for Category
|
||||
* https://docs.discourse.org/#tag/Categories/paths/~1c~1{id}.json/get
|
||||
*/
|
||||
export interface CategoryResponse {
|
||||
users: Person[];
|
||||
topic_list: {
|
||||
can_create_topic: boolean;
|
||||
draft: boolean;
|
||||
draft_key: string;
|
||||
draft_sequence: number;
|
||||
per_page: number;
|
||||
topics: Array<TopicItem>;
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Whole Post Information
|
||||
* As returned by getting a single Post
|
||||
* https://docs.discourse.org/#tag/Posts/paths/~1posts~1{id}.json/get
|
||||
*/
|
||||
export interface PostResponse {
|
||||
actions_summary: Array<Action>;
|
||||
admin: boolean;
|
||||
avatar_template: string;
|
||||
avg_time: object;
|
||||
can_delete: boolean;
|
||||
can_edit: boolean;
|
||||
can_recover: boolean;
|
||||
can_view_edit_history: boolean;
|
||||
can_wiki: boolean;
|
||||
cooked: string;
|
||||
created_at: string;
|
||||
deleted_at: object;
|
||||
display_username: string;
|
||||
edit_reason: object;
|
||||
hidden_reason_id: object;
|
||||
hidden: boolean;
|
||||
id: number;
|
||||
incoming_link_count: number;
|
||||
moderator: boolean;
|
||||
name: string;
|
||||
post_number: number;
|
||||
post_type: number;
|
||||
primary_group_flair_bg_color: object;
|
||||
primary_group_flair_color: object;
|
||||
primary_group_flair_url: object;
|
||||
primary_group_name: object;
|
||||
quote_count: number;
|
||||
raw: string;
|
||||
reads: number;
|
||||
reply_count: number;
|
||||
reply_to_post_number: object;
|
||||
score: number;
|
||||
staff: boolean;
|
||||
topic_id: number;
|
||||
topic_slug: string;
|
||||
trust_level: number;
|
||||
updated_at: string;
|
||||
user_deleted: boolean;
|
||||
user_id: number;
|
||||
user_title: object;
|
||||
username: string;
|
||||
version: number;
|
||||
wiki: boolean;
|
||||
yours: boolean;
|
||||
}
|
||||
export interface ICreateUserResponse {
|
||||
success: boolean;
|
||||
active: boolean;
|
||||
message: string;
|
||||
user_id: number;
|
||||
password: string;
|
||||
}
|
||||
/**
|
||||
* Get the Posts of a Topic
|
||||
* https://docs.discourse.org/#tag/Topics/paths/~1t~1{id}~1posts.json/get
|
||||
*/
|
||||
export interface PostsResponse {
|
||||
post_stream: {
|
||||
posts: Array<PostItem>;
|
||||
};
|
||||
id: number;
|
||||
}
|
||||
/**
|
||||
* Partial Post Information
|
||||
* As returned by a listing
|
||||
*/
|
||||
export interface PostItem {
|
||||
accepted_answer: boolean;
|
||||
actions_summary: Array<Action>;
|
||||
admin: boolean;
|
||||
avatar_template: string;
|
||||
can_accept_answer: boolean;
|
||||
can_delete: boolean;
|
||||
can_edit: boolean;
|
||||
can_recover: boolean;
|
||||
can_unaccept_answer: boolean;
|
||||
can_view_edit_history: boolean;
|
||||
can_wiki: boolean;
|
||||
cooked: string;
|
||||
created_at: string;
|
||||
deleted_at: null;
|
||||
display_username: string;
|
||||
edit_reason: null;
|
||||
hidden: boolean;
|
||||
id: number;
|
||||
incoming_link_count: number;
|
||||
link_counts: Array<Link>;
|
||||
moderator: boolean;
|
||||
name: string;
|
||||
post_number: number;
|
||||
post_type: number;
|
||||
primary_group_flair_bg_color: null | object;
|
||||
primary_group_flair_color: null | object;
|
||||
primary_group_flair_url: null | object;
|
||||
primary_group_name: null | object;
|
||||
quote_count: number;
|
||||
read: boolean;
|
||||
readers_count: number;
|
||||
reads: number;
|
||||
reply_count: number;
|
||||
reply_to_post_number: null | number;
|
||||
reviewable_id: number;
|
||||
reviewable_score_count: number;
|
||||
reviewable_score_pending_count: number;
|
||||
score: number;
|
||||
staff: boolean;
|
||||
topic_id: number;
|
||||
topic_slug: string;
|
||||
trust_level: number;
|
||||
updated_at: string;
|
||||
user_deleted: boolean;
|
||||
user_id: number;
|
||||
user_title: null | object;
|
||||
username: string;
|
||||
version: number;
|
||||
wiki: boolean;
|
||||
yours: boolean;
|
||||
}
|
||||
export type Thread = {
|
||||
topic: TopicResponse;
|
||||
post: PostItem;
|
||||
replies: PostItem[];
|
||||
};
|
||||
/** When finding and replacing, determine replacements using a method that matches this */
|
||||
export type PostModifier = (post: PostResponse) => {
|
||||
result: string;
|
||||
reason?: string;
|
||||
};
|
||||
/** Configuration for Discourser */
|
||||
export interface IDiscourserConfig {
|
||||
/** the discourse hostname to connect to, including protocol */
|
||||
host: string;
|
||||
/** the API key to connect with */
|
||||
key: string;
|
||||
/** the username to behave as */
|
||||
username: string;
|
||||
/** the cache directory to use, if we are caching */
|
||||
cache?: string;
|
||||
/** Whether or not we should read from the cache */
|
||||
useCache?: boolean;
|
||||
/** whether or not updates should be dry (non-applying) */
|
||||
dry?: boolean;
|
||||
/** how many concurrency requests to send to the server at once */
|
||||
rateLimitConcurrency?: number;
|
||||
}
|
||||
export interface FetchOptions {
|
||||
/** Whether or not we should read from the cache */
|
||||
useCache?: boolean;
|
||||
/** Only applicable to fetching topics of category */
|
||||
page?: number;
|
||||
/** Any thing to init the fetch call with? */
|
||||
request?: RequestInit;
|
||||
include_subcategories?: boolean;
|
||||
}
|
||||
export interface FetchConfig extends FetchOptions {
|
||||
url: string;
|
||||
}
|
||||
export interface PostConfig extends FetchOptions {
|
||||
url: string;
|
||||
data: any;
|
||||
}
|
||||
export interface ISearchPost {
|
||||
id: number;
|
||||
name: string;
|
||||
username: string;
|
||||
avatar_template: string;
|
||||
created_at: Date;
|
||||
like_count: number;
|
||||
blurb: string;
|
||||
post_number: number;
|
||||
topic_id: number;
|
||||
}
|
||||
export interface ISearchTagsDescriptions {
|
||||
}
|
||||
export interface ISearchTopic {
|
||||
id: number;
|
||||
title: string;
|
||||
fancy_title: string;
|
||||
slug: string;
|
||||
posts_count: number;
|
||||
reply_count: number;
|
||||
highest_post_number: number;
|
||||
created_at: Date;
|
||||
last_posted_at: Date;
|
||||
bumped: boolean;
|
||||
bumped_at: Date;
|
||||
archetype: string;
|
||||
unseen: boolean;
|
||||
pinned: boolean;
|
||||
unpinned?: any;
|
||||
excerpt: string;
|
||||
visible: boolean;
|
||||
closed: boolean;
|
||||
archived: boolean;
|
||||
bookmarked?: any;
|
||||
liked?: any;
|
||||
tags: any[];
|
||||
tags_descriptions: ISearchTagsDescriptions;
|
||||
category_id: number;
|
||||
has_accepted_answer: boolean;
|
||||
}
|
||||
export interface IGroupedSearchResult {
|
||||
more_posts?: any;
|
||||
more_users?: any;
|
||||
more_categories?: any;
|
||||
term: string;
|
||||
search_log_id: number;
|
||||
more_full_page_results?: any;
|
||||
can_create_topic: boolean;
|
||||
error?: any;
|
||||
post_ids: number[];
|
||||
user_ids: any[];
|
||||
category_ids: any[];
|
||||
tag_ids: any[];
|
||||
group_ids: any[];
|
||||
}
|
||||
export interface ISearchResult {
|
||||
posts: ISearchPost[];
|
||||
topics: ISearchTopic[];
|
||||
users: any[];
|
||||
categories: any[];
|
||||
tags: any[];
|
||||
groups: any[];
|
||||
grouped_search_result: IGroupedSearchResult;
|
||||
}
|
||||
export interface IUserDetail {
|
||||
id: number;
|
||||
username: string;
|
||||
name: string;
|
||||
avatar_template: string;
|
||||
email: string;
|
||||
secondary_emails: any[];
|
||||
active: boolean;
|
||||
admin: boolean;
|
||||
moderator: boolean;
|
||||
last_seen_at: string;
|
||||
last_emailed_at: string;
|
||||
created_at: string;
|
||||
last_seen_age: number;
|
||||
last_emailed_age: number;
|
||||
created_at_age: number;
|
||||
trust_level: number;
|
||||
manual_locked_trust_level: any;
|
||||
flag_level: number;
|
||||
title: string;
|
||||
time_read: number;
|
||||
staged: boolean;
|
||||
days_visited: number;
|
||||
posts_read_count: number;
|
||||
topics_entered: number;
|
||||
post_count: number;
|
||||
associated_accounts: AssociatedAccount[];
|
||||
can_send_activation_email: boolean;
|
||||
can_activate: boolean;
|
||||
can_deactivate: boolean;
|
||||
ip_address: string;
|
||||
registration_ip_address: string;
|
||||
can_grant_admin: boolean;
|
||||
can_revoke_admin: boolean;
|
||||
can_grant_moderation: boolean;
|
||||
can_revoke_moderation: boolean;
|
||||
can_impersonate: boolean;
|
||||
like_count: number;
|
||||
like_given_count: number;
|
||||
topic_count: number;
|
||||
post_edits_count: number;
|
||||
flags_given_count: number;
|
||||
flags_received_count: number;
|
||||
private_topics_count: number;
|
||||
can_delete_all_posts: boolean;
|
||||
can_be_deleted: boolean;
|
||||
can_be_anonymized: boolean;
|
||||
can_be_merged: boolean;
|
||||
full_suspend_reason: any;
|
||||
silence_reason: any;
|
||||
penalty_counts: PenaltyCounts;
|
||||
next_penalty: string;
|
||||
primary_group_id: any;
|
||||
badge_count: number;
|
||||
warnings_received_count: number;
|
||||
user_fields: UserFields;
|
||||
bounce_score: number;
|
||||
reset_bounce_score_after: any;
|
||||
can_view_action_logs: boolean;
|
||||
can_disable_second_factor: boolean;
|
||||
can_delete_sso_record: boolean;
|
||||
api_key_count: number;
|
||||
external_ids: ExternalIds;
|
||||
single_sign_on_record: any;
|
||||
approved_by: ApprovedBy;
|
||||
suspended_by: any;
|
||||
silenced_by: any;
|
||||
tl3_requirements: Tl3Requirements;
|
||||
groups: Group[];
|
||||
}
|
||||
export interface AssociatedAccount {
|
||||
name: string;
|
||||
description: string;
|
||||
}
|
||||
export interface PenaltyCounts {
|
||||
silenced: number;
|
||||
suspended: number;
|
||||
}
|
||||
export interface UserFields {
|
||||
"1": string;
|
||||
"2": string;
|
||||
"3": string;
|
||||
"4": string;
|
||||
"5": string;
|
||||
}
|
||||
export interface ExternalIds {
|
||||
google_oauth2: string;
|
||||
}
|
||||
export interface ApprovedBy {
|
||||
id: number;
|
||||
username: string;
|
||||
name: string;
|
||||
avatar_template: string;
|
||||
}
|
||||
export interface Tl3Requirements {
|
||||
time_period: number;
|
||||
requirements_met: boolean;
|
||||
requirements_lost: boolean;
|
||||
trust_level_locked: boolean;
|
||||
on_grace_period: boolean;
|
||||
days_visited: number;
|
||||
min_days_visited: number;
|
||||
num_topics_replied_to: number;
|
||||
min_topics_replied_to: number;
|
||||
topics_viewed: number;
|
||||
min_topics_viewed: number;
|
||||
posts_read: number;
|
||||
min_posts_read: number;
|
||||
topics_viewed_all_time: number;
|
||||
min_topics_viewed_all_time: number;
|
||||
posts_read_all_time: number;
|
||||
min_posts_read_all_time: number;
|
||||
num_flagged_posts: number;
|
||||
max_flagged_posts: number;
|
||||
num_flagged_by_users: number;
|
||||
max_flagged_by_users: number;
|
||||
num_likes_given: number;
|
||||
min_likes_given: number;
|
||||
num_likes_received: number;
|
||||
min_likes_received: number;
|
||||
num_likes_received_days: number;
|
||||
min_likes_received_days: number;
|
||||
num_likes_received_users: number;
|
||||
min_likes_received_users: number;
|
||||
penalty_counts: PenaltyCounts2;
|
||||
}
|
||||
export interface PenaltyCounts2 {
|
||||
silenced: number;
|
||||
suspended: number;
|
||||
total: number;
|
||||
}
|
||||
export interface Group {
|
||||
id: number;
|
||||
automatic: boolean;
|
||||
name: string;
|
||||
display_name?: string;
|
||||
user_count: number;
|
||||
mentionable_level: number;
|
||||
messageable_level: number;
|
||||
visibility_level: number;
|
||||
primary_group: boolean;
|
||||
title: any;
|
||||
grant_trust_level?: number;
|
||||
incoming_email: any;
|
||||
has_messages: boolean;
|
||||
flair_url: any;
|
||||
flair_bg_color?: string;
|
||||
flair_color?: string;
|
||||
bio_raw?: string;
|
||||
bio_cooked?: string;
|
||||
bio_excerpt?: string;
|
||||
public_admission: boolean;
|
||||
public_exit: boolean;
|
||||
allow_membership_requests: boolean;
|
||||
full_name?: string;
|
||||
default_notification_level: number;
|
||||
membership_request_template: any;
|
||||
members_visibility_level: number;
|
||||
can_see_members: boolean;
|
||||
can_admin_group: boolean;
|
||||
publish_read_state: boolean;
|
||||
can_edit_group?: boolean;
|
||||
}
|
||||
export type TPostStatus = 'visible' | 'archived' | 'pinned' | 'closed';
|
||||
export interface TPostStatusUpdate {
|
||||
success: string;
|
||||
topic_status_update: any;
|
||||
}
|
||||
export interface UserPreferencesUpdate {
|
||||
bio_raw?: string;
|
||||
website?: string;
|
||||
location?: string;
|
||||
custom_fields?: CustomFields;
|
||||
timezone?: string;
|
||||
default_calendar?: string;
|
||||
profile_background_upload_url?: string;
|
||||
card_background_upload_url?: string;
|
||||
}
|
||||
export interface CustomFields {
|
||||
geo_location: GeoLocation;
|
||||
}
|
||||
export interface GeoLocation {
|
||||
lat: string;
|
||||
lon: string;
|
||||
address: string;
|
||||
countrycode: string;
|
||||
city: string;
|
||||
state: string;
|
||||
country: string;
|
||||
postalcode: string;
|
||||
boundingbox: string[];
|
||||
type: string;
|
||||
}
|
||||
4
packages/discourse/dist/src/lib/discourse/types.js
vendored
Normal file
4
packages/discourse/dist/src/lib/discourse/types.js
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
// Attempt at TypeScript Types for the Discourse API
|
||||
// https://docs.discourse.org
|
||||
export {};
|
||||
//# sourceMappingURL=types.js.map
|
||||
1
packages/discourse/dist/src/lib/discourse/types.js.map
vendored
Normal file
1
packages/discourse/dist/src/lib/discourse/types.js.map
vendored
Normal file
@ -0,0 +1 @@
|
||||
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../../src/lib/discourse/types.ts"],"names":[],"mappings":"AAAA,oDAAoD;AACpD,6BAA6B"}
|
||||
2
packages/discourse/dist/src/lib/index.d.ts
vendored
Normal file
2
packages/discourse/dist/src/lib/index.d.ts
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
export * from './discourse/index.js';
|
||||
export * from './discourse/types.js';
|
||||
3
packages/discourse/dist/src/lib/index.js
vendored
Normal file
3
packages/discourse/dist/src/lib/index.js
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
export * from './discourse/index.js';
|
||||
export * from './discourse/types.js';
|
||||
//# sourceMappingURL=index.js.map
|
||||
1
packages/discourse/dist/src/lib/index.js.map
vendored
Normal file
1
packages/discourse/dist/src/lib/index.js.map
vendored
Normal file
@ -0,0 +1 @@
|
||||
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/lib/index.ts"],"names":[],"mappings":"AAAA,cAAc,sBAAsB,CAAA;AACpC,cAAc,sBAAsB,CAAA"}
|
||||
7
packages/discourse/dist/src/lib/markdown/Pattern.d.ts
vendored
Normal file
7
packages/discourse/dist/src/lib/markdown/Pattern.d.ts
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
import { RegExCallback } from './types.js';
|
||||
export declare class Pattern {
|
||||
regex: RegExp;
|
||||
replacement: RegExCallback;
|
||||
constructor(regex: RegExp, replacement: any);
|
||||
apply(raw: string): string;
|
||||
}
|
||||
12
packages/discourse/dist/src/lib/markdown/Pattern.js
vendored
Normal file
12
packages/discourse/dist/src/lib/markdown/Pattern.js
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
export class Pattern {
|
||||
regex;
|
||||
replacement;
|
||||
constructor(regex, replacement) {
|
||||
this.regex = regex;
|
||||
this.replacement = replacement;
|
||||
}
|
||||
apply(raw) {
|
||||
return raw.replace(this.regex, this.replacement);
|
||||
}
|
||||
}
|
||||
//# sourceMappingURL=Pattern.js.map
|
||||
1
packages/discourse/dist/src/lib/markdown/Pattern.js.map
vendored
Normal file
1
packages/discourse/dist/src/lib/markdown/Pattern.js.map
vendored
Normal file
@ -0,0 +1 @@
|
||||
{"version":3,"file":"Pattern.js","sourceRoot":"","sources":["../../../../src/lib/markdown/Pattern.ts"],"names":[],"mappings":"AACA,MAAM,OAAO,OAAO;IAClB,KAAK,CAAS;IACd,WAAW,CAAe;IAC1B,YAAY,KAAa,EAAE,WAAgB;QACzC,IAAI,CAAC,KAAK,GAAG,KAAK,CAAA;QAClB,IAAI,CAAC,WAAW,GAAG,WAAW,CAAA;IAChC,CAAC;IAED,KAAK,CAAC,GAAW;QACf,OAAO,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,WAAW,CAAC,CAAA;IAClD,CAAC;CACF"}
|
||||
7
packages/discourse/dist/src/lib/markdown/Rule.d.ts
vendored
Normal file
7
packages/discourse/dist/src/lib/markdown/Rule.d.ts
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
import { Pattern } from './Pattern.js';
|
||||
export declare class Rule {
|
||||
name: string;
|
||||
patterns: Pattern[];
|
||||
constructor(name: string, patterns: Pattern[]);
|
||||
apply(raw: string): string;
|
||||
}
|
||||
12
packages/discourse/dist/src/lib/markdown/Rule.js
vendored
Normal file
12
packages/discourse/dist/src/lib/markdown/Rule.js
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
export class Rule {
|
||||
name;
|
||||
patterns;
|
||||
constructor(name, patterns) {
|
||||
this.name = name;
|
||||
this.patterns = patterns;
|
||||
}
|
||||
apply(raw) {
|
||||
return this.patterns.reduce((result, pattern) => pattern.apply(result), raw);
|
||||
}
|
||||
}
|
||||
//# sourceMappingURL=Rule.js.map
|
||||
1
packages/discourse/dist/src/lib/markdown/Rule.js.map
vendored
Normal file
1
packages/discourse/dist/src/lib/markdown/Rule.js.map
vendored
Normal file
@ -0,0 +1 @@
|
||||
{"version":3,"file":"Rule.js","sourceRoot":"","sources":["../../../../src/lib/markdown/Rule.ts"],"names":[],"mappings":"AAEA,MAAM,OAAO,IAAI;IACf,IAAI,CAAS;IACb,QAAQ,CAAY;IACpB,YAAY,IAAY,EAAE,QAAmB;QAC3C,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;IAC3B,CAAC;IAED,KAAK,CAAC,GAAW;QACf,OAAO,IAAI,CAAC,QAAQ,CAAC,MAAM,CACzB,CAAC,MAAM,EAAE,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,EAC1C,GAAG,CACJ,CAAC;IACJ,CAAC;CACF"}
|
||||
14
packages/discourse/dist/src/lib/markdown/index.d.ts
vendored
Normal file
14
packages/discourse/dist/src/lib/markdown/index.d.ts
vendored
Normal file
@ -0,0 +1,14 @@
|
||||
import { Rule } from './Rule.js';
|
||||
import { RMarkOptions } from './types.js';
|
||||
export declare const RE_IMAGES: RegExp;
|
||||
export declare const RE_LINKS: RegExp;
|
||||
export declare class RMark {
|
||||
constructor(options: RMarkOptions);
|
||||
private rules;
|
||||
addRuleBefore(rule: Rule, before: string): RMark;
|
||||
addRule(rule: Rule): RMark;
|
||||
render(raw: string): string;
|
||||
}
|
||||
export { Rule } from './Rule.js';
|
||||
export { Pattern } from './Pattern.js';
|
||||
export declare const toHTML: (content: any) => string;
|
||||
98
packages/discourse/dist/src/lib/markdown/index.js
vendored
Normal file
98
packages/discourse/dist/src/lib/markdown/index.js
vendored
Normal file
@ -0,0 +1,98 @@
|
||||
import { Rule } from './Rule.js';
|
||||
import { Pattern } from './Pattern.js';
|
||||
export const RE_IMAGES = /\!\[([^\]]+)\]\((\S+)\)/g;
|
||||
export const RE_LINKS = /\[([^\n]+)\]\(([^\n]+)\)/g;
|
||||
import markdown from 'markdown-it';
|
||||
const defaultRules = [
|
||||
new Rule('header', [
|
||||
new Pattern(/^#{6}\s?([^\n]+)/gm, '<h6>$1</h6>'),
|
||||
new Pattern(/^#{5}\s?([^\n]+)/gm, '<h5>$1</h5>'),
|
||||
new Pattern(/^#{4}\s?([^\n]+)/gm, '<h4>$1</h4>'),
|
||||
new Pattern(/^#{3}\s?([^\n]+)/gm, '<h3>$1</h3>'),
|
||||
new Pattern(/^#{2}\s?([^\n]+)/gm, '<h2>$1</h2>'),
|
||||
new Pattern(/^#{1}\s?([^\n]+)/gm, '<h1>$1</h1>'),
|
||||
]),
|
||||
new Rule('bold', [
|
||||
new Pattern(/\*\*\s?([^\n]+)\*\*/g, '<b>$1</b>'),
|
||||
new Pattern(/\_\_\s?([^\n]+)\_\_/g, '<b>$1</b>'),
|
||||
]),
|
||||
new Rule('italic', [
|
||||
new Pattern(/\*\s?([^\n]+)\*/g, '<i>$1</i>'),
|
||||
new Pattern(/\_\s?([^\n]+)\_/g, '<i>$1</i>'),
|
||||
]),
|
||||
new Rule('image', [
|
||||
new Pattern(/\!\[([^\]]+)\]\((\S+)\)/g, '<img src="$2" alt="$1" />'),
|
||||
]),
|
||||
new Rule('link', [
|
||||
new Pattern(/\[([^\n]+)\]\(([^\n]+)\)/g, '<a href2="$2" target="_blank" rel="noopener">$1</a>'),
|
||||
]),
|
||||
new Rule('paragraph', [
|
||||
// this regex can't skip processed HTML
|
||||
new Pattern(/([^\n]+\n?)/g, '\n<p>$1</p>\n'),
|
||||
// another possible regex that can't skip processed HTML
|
||||
// new Pattern(/(?:^|\n)([^\n\<]+(?:\n[^\n\>]+)*)(?:\n|$)/gm, '\n<p>$1</p>\n'),
|
||||
])
|
||||
];
|
||||
const defaultRulesDiscourse = (images, links) => {
|
||||
return [
|
||||
new Rule('image', [
|
||||
new Pattern(RE_LINKS, images)
|
||||
]) /*,
|
||||
new Rule('link', [
|
||||
new Pattern(
|
||||
RE_LINKS,
|
||||
links
|
||||
)
|
||||
])*/
|
||||
];
|
||||
};
|
||||
export class RMark {
|
||||
constructor(options) {
|
||||
this.rules = defaultRulesDiscourse(options.images, options.links);
|
||||
}
|
||||
rules;
|
||||
addRuleBefore(rule, before) {
|
||||
const index = this.rules.findIndex((r) => r.name === before);
|
||||
if (index !== -1) {
|
||||
this.rules.splice(index, 0, rule);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
addRule(rule) {
|
||||
this.addRuleBefore(rule, 'paragraph');
|
||||
return this;
|
||||
}
|
||||
render(raw) {
|
||||
let result = raw;
|
||||
this.rules.forEach((rule) => {
|
||||
result = rule.apply(result);
|
||||
});
|
||||
return result;
|
||||
}
|
||||
}
|
||||
export { Rule } from './Rule.js';
|
||||
export { Pattern } from './Pattern.js';
|
||||
// export const find = (content:string, reg:RegExp) => content.match(reg)
|
||||
export const toHTML = (content) => {
|
||||
const md = new markdown({
|
||||
html: true,
|
||||
breaks: true
|
||||
});
|
||||
return md.render(content);
|
||||
};
|
||||
function image_urls(input) {
|
||||
const regex = /https?:\/\/(?:[a-z0-9\-]+\.)+[a-z]{2,}(?:\/[^\/#\s]*)*\.(?:jpe?g|gif|png|webp)/g;
|
||||
const matches = input.match(regex);
|
||||
return matches || [];
|
||||
}
|
||||
function image_urls_local(input) {
|
||||
const regex = /\/(?:[^\/#\s]+\/)*[^\/#\s]+\.(?:jpe?g|gif|png|webp)/g;
|
||||
const matches = input.match(regex);
|
||||
return matches || [];
|
||||
}
|
||||
function findUploadImageUrls(input) {
|
||||
const regex = /upload:\/\/[^\s]+?\.(?:jpe?g|gif|png)/gi;
|
||||
const matches = input.match(regex);
|
||||
return matches || [];
|
||||
}
|
||||
//# sourceMappingURL=index.js.map
|
||||
1
packages/discourse/dist/src/lib/markdown/index.js.map
vendored
Normal file
1
packages/discourse/dist/src/lib/markdown/index.js.map
vendored
Normal file
@ -0,0 +1 @@
|
||||
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/lib/markdown/index.ts"],"names":[],"mappings":"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"}
|
||||
1
packages/discourse/dist/src/lib/markdown/index.test.d.ts
vendored
Normal file
1
packages/discourse/dist/src/lib/markdown/index.test.d.ts
vendored
Normal file
@ -0,0 +1 @@
|
||||
export {};
|
||||
@ -1,5 +1,4 @@
|
||||
import { Pattern, RMark, Rule } from './index';
|
||||
|
||||
const sampleText = `# Header 1
|
||||
## Header 2
|
||||
### Header 3
|
||||
@ -20,7 +19,6 @@ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam ornare erat fac
|
||||
Sed pellentesque nulla sit amet tincidunt sagittis. Phasellus eget justo nulla. Cras nisi odio, lobortis nec ante eget, commodo euismod
|
||||
turpis. Cras id orci dolor. Etiam auctor, nisl luctus volutpat lacinia, turpis orci euismod magna, pharetra eleifend massa metus aliquet
|
||||
`;
|
||||
|
||||
const sampleHtml = `
|
||||
<p><h1>Header 1</h1>
|
||||
</p>
|
||||
@ -69,76 +67,46 @@ const sampleHtml = `
|
||||
<p>turpis. Cras id orci dolor. Etiam auctor, nisl luctus volutpat lacinia, turpis orci euismod magna, pharetra eleifend massa metus aliquet
|
||||
</p>
|
||||
`;
|
||||
|
||||
describe('testing index file', () => {
|
||||
test('empty string should render nothing', () => {
|
||||
expect(new RMark().render('')).toBe('');
|
||||
});
|
||||
test('should render paragraph', () => {
|
||||
expect(
|
||||
new RMark().render(
|
||||
'Lorem ipsum dolor sit amet, consectetur adipiscing elit.'
|
||||
)
|
||||
).toBe(
|
||||
'\n<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p>\n'
|
||||
);
|
||||
});
|
||||
test('should render header', () => {
|
||||
expect(new RMark().render('# Header 1')).toBe(
|
||||
'\n<p><h1>Header 1</h1></p>\n'
|
||||
);
|
||||
expect(new RMark().render('## Header 2')).toBe(
|
||||
'\n<p><h2>Header 2</h2></p>\n'
|
||||
);
|
||||
expect(new RMark().render('### Header 3')).toBe(
|
||||
'\n<p><h3>Header 3</h3></p>\n'
|
||||
);
|
||||
expect(new RMark().render('#### Header 4')).toBe(
|
||||
'\n<p><h4>Header 4</h4></p>\n'
|
||||
);
|
||||
expect(new RMark().render('##### Header 5')).toBe(
|
||||
'\n<p><h5>Header 5</h5></p>\n'
|
||||
);
|
||||
expect(new RMark().render('###### Header 6')).toBe(
|
||||
'\n<p><h6>Header 6</h6></p>\n'
|
||||
);
|
||||
});
|
||||
test('should render bold', () => {
|
||||
expect(new RMark().render('**Bold**')).toBe('\n<p><b>Bold</b></p>\n');
|
||||
expect(new RMark().render('__Bold__')).toBe('\n<p><b>Bold</b></p>\n');
|
||||
expect(new RMark().render('This is **Bold**')).toBe(
|
||||
'\n<p>This is <b>Bold</b></p>\n'
|
||||
);
|
||||
});
|
||||
test('should render italic', () => {
|
||||
expect(new RMark().render('*Italic*')).toBe('\n<p><i>Italic</i></p>\n');
|
||||
expect(new RMark().render('_Italic_')).toBe('\n<p><i>Italic</i></p>\n');
|
||||
});
|
||||
test('should render image', () => {
|
||||
expect(
|
||||
new RMark().render(
|
||||
''
|
||||
)
|
||||
).toBe(
|
||||
'\n<p><img src="https://github.githubassets.com/images/modules/logos_page/GitHub-Mark.png" alt="Image" /></p>\n'
|
||||
);
|
||||
});
|
||||
test('should render link', () => {
|
||||
expect(new RMark().render('[Link](https://github.com)')).toBe(
|
||||
'\n<p><a href="https://github.com" target="_blank" rel="noopener">Link</a></p>\n'
|
||||
);
|
||||
});
|
||||
test('should render paragraph with multiple lines', () => {
|
||||
expect(new RMark().render(sampleText)).toBe(sampleHtml);
|
||||
});
|
||||
test('should work with adding rules', () => {
|
||||
const rmark = new RMark();
|
||||
rmark.addRule(
|
||||
new Rule('horizontal', [
|
||||
new Pattern(/^(-{3})/gm, '<hr />'),
|
||||
new Pattern(/^(_{3})/gm, '<hr />'),
|
||||
])
|
||||
);
|
||||
expect(rmark.render('---')).toBe('\n<p><hr /></p>\n');
|
||||
});
|
||||
test('empty string should render nothing', () => {
|
||||
expect(new RMark().render('')).toBe('');
|
||||
});
|
||||
test('should render paragraph', () => {
|
||||
expect(new RMark().render('Lorem ipsum dolor sit amet, consectetur adipiscing elit.')).toBe('\n<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p>\n');
|
||||
});
|
||||
test('should render header', () => {
|
||||
expect(new RMark().render('# Header 1')).toBe('\n<p><h1>Header 1</h1></p>\n');
|
||||
expect(new RMark().render('## Header 2')).toBe('\n<p><h2>Header 2</h2></p>\n');
|
||||
expect(new RMark().render('### Header 3')).toBe('\n<p><h3>Header 3</h3></p>\n');
|
||||
expect(new RMark().render('#### Header 4')).toBe('\n<p><h4>Header 4</h4></p>\n');
|
||||
expect(new RMark().render('##### Header 5')).toBe('\n<p><h5>Header 5</h5></p>\n');
|
||||
expect(new RMark().render('###### Header 6')).toBe('\n<p><h6>Header 6</h6></p>\n');
|
||||
});
|
||||
test('should render bold', () => {
|
||||
expect(new RMark().render('**Bold**')).toBe('\n<p><b>Bold</b></p>\n');
|
||||
expect(new RMark().render('__Bold__')).toBe('\n<p><b>Bold</b></p>\n');
|
||||
expect(new RMark().render('This is **Bold**')).toBe('\n<p>This is <b>Bold</b></p>\n');
|
||||
});
|
||||
test('should render italic', () => {
|
||||
expect(new RMark().render('*Italic*')).toBe('\n<p><i>Italic</i></p>\n');
|
||||
expect(new RMark().render('_Italic_')).toBe('\n<p><i>Italic</i></p>\n');
|
||||
});
|
||||
test('should render image', () => {
|
||||
expect(new RMark().render('')).toBe('\n<p><img src="https://github.githubassets.com/images/modules/logos_page/GitHub-Mark.png" alt="Image" /></p>\n');
|
||||
});
|
||||
test('should render link', () => {
|
||||
expect(new RMark().render('[Link](https://github.com)')).toBe('\n<p><a href="https://github.com" target="_blank" rel="noopener">Link</a></p>\n');
|
||||
});
|
||||
test('should render paragraph with multiple lines', () => {
|
||||
expect(new RMark().render(sampleText)).toBe(sampleHtml);
|
||||
});
|
||||
test('should work with adding rules', () => {
|
||||
const rmark = new RMark();
|
||||
rmark.addRule(new Rule('horizontal', [
|
||||
new Pattern(/^(-{3})/gm, '<hr />'),
|
||||
new Pattern(/^(_{3})/gm, '<hr />'),
|
||||
]));
|
||||
expect(rmark.render('---')).toBe('\n<p><hr /></p>\n');
|
||||
});
|
||||
});
|
||||
//# sourceMappingURL=index.test.js.map
|
||||
1
packages/discourse/dist/src/lib/markdown/index.test.js.map
vendored
Normal file
1
packages/discourse/dist/src/lib/markdown/index.test.js.map
vendored
Normal file
@ -0,0 +1 @@
|
||||
{"version":3,"file":"index.test.js","sourceRoot":"","sources":["../../../../src/lib/markdown/index.test.ts"],"names":[],"mappings":"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"}
|
||||
1
packages/discourse/dist/src/lib/markdown/page.d.ts
vendored
Normal file
1
packages/discourse/dist/src/lib/markdown/page.d.ts
vendored
Normal file
@ -0,0 +1 @@
|
||||
export {};
|
||||
@ -1,5 +1,4 @@
|
||||
import { RMark } from '.';
|
||||
|
||||
import { RMark } from './index.js';
|
||||
const sampleText = `# Header 1
|
||||
## Header 2
|
||||
### Header 3
|
||||
@ -20,9 +19,8 @@ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam ornare erat fac
|
||||
Sed pellentesque nulla sit amet tincidunt sagittis. Phasellus eget justo nulla. Cras nisi odio, lobortis nec ante eget, commodo euismod
|
||||
turpis. Cras id orci dolor. Etiam auctor, nisl luctus volutpat lacinia, turpis orci euismod magna, pharetra eleifend massa metus aliquet
|
||||
`;
|
||||
|
||||
const page = document.getElementById('page');
|
||||
|
||||
if (page) {
|
||||
page.innerHTML = new RMark().render(sampleText);
|
||||
page.innerHTML = new RMark().render(sampleText);
|
||||
}
|
||||
//# sourceMappingURL=page.js.map
|
||||
1
packages/discourse/dist/src/lib/markdown/page.js.map
vendored
Normal file
1
packages/discourse/dist/src/lib/markdown/page.js.map
vendored
Normal file
@ -0,0 +1 @@
|
||||
{"version":3,"file":"page.js","sourceRoot":"","sources":["../../../../src/lib/markdown/page.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAEnC,MAAM,UAAU,GAAG;;;;;;;;;;;;;;;;;;;CAmBlB,CAAC;AAEF,MAAM,IAAI,GAAG,QAAQ,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC;AAE7C,IAAI,IAAI,EAAE;IACR,IAAI,CAAC,SAAS,GAAG,IAAI,KAAK,EAAE,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;CACjD"}
|
||||
5
packages/discourse/dist/src/lib/markdown/types.d.ts
vendored
Normal file
5
packages/discourse/dist/src/lib/markdown/types.d.ts
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
export type RegExCallback = (match: any, capture: any, arg1: any, arg2: any) => string;
|
||||
export interface RMarkOptions {
|
||||
images: RegExCallback;
|
||||
links?: RegExCallback;
|
||||
}
|
||||
2
packages/discourse/dist/src/lib/markdown/types.js
vendored
Normal file
2
packages/discourse/dist/src/lib/markdown/types.js
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
export {};
|
||||
//# sourceMappingURL=types.js.map
|
||||
1
packages/discourse/dist/src/lib/markdown/types.js.map
vendored
Normal file
1
packages/discourse/dist/src/lib/markdown/types.js.map
vendored
Normal file
@ -0,0 +1 @@
|
||||
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../../src/lib/markdown/types.ts"],"names":[],"mappings":""}
|
||||
2
packages/discourse/dist/src/main.d.ts
vendored
Normal file
2
packages/discourse/dist/src/main.d.ts
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
#!/usr/bin/env node
|
||||
export {};
|
||||
14
packages/discourse/dist/src/main.js
vendored
Normal file
14
packages/discourse/dist/src/main.js
vendored
Normal file
@ -0,0 +1,14 @@
|
||||
#!/usr/bin/env node
|
||||
process.env['NODE_TLS_REJECT_UNAUTHORIZED'] = '0';
|
||||
import { defaults } from './_cli.js';
|
||||
defaults();
|
||||
import { cli } from './cli.js';
|
||||
const argv = cli.argv;
|
||||
if (argv.help) {
|
||||
cli.showHelp();
|
||||
process.exit();
|
||||
}
|
||||
else if (argv.v || argv.version) {
|
||||
process.exit();
|
||||
}
|
||||
//# sourceMappingURL=main.js.map
|
||||
1
packages/discourse/dist/src/main.js.map
vendored
Normal file
1
packages/discourse/dist/src/main.js.map
vendored
Normal file
@ -0,0 +1 @@
|
||||
{"version":3,"file":"main.js","sourceRoot":"","sources":["../../src/main.ts"],"names":[],"mappings":";AACA,OAAO,CAAC,GAAG,CAAC,8BAA8B,CAAC,GAAG,GAAG,CAAC;AAClD,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAAC,QAAQ,EAAE,CAAC;AACjD,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAA;AAC9B,MAAM,IAAI,GAAQ,GAAG,CAAC,IAAI,CAAC;AAE3B,IAAI,IAAI,CAAC,IAAI,EAAE;IACX,GAAG,CAAC,QAAQ,EAAE,CAAC;IACf,OAAO,CAAC,IAAI,EAAE,CAAC;CAClB;KAAM,IAAI,IAAI,CAAC,CAAC,IAAI,IAAI,CAAC,OAAO,EAAE;IAC/B,OAAO,CAAC,IAAI,EAAE,CAAC;CAClB"}
|
||||
3
packages/discourse/dist/src/options.d.ts
vendored
Normal file
3
packages/discourse/dist/src/options.d.ts
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
export * from './lib/index.js';
|
||||
import { IOptions } from './types.js';
|
||||
export declare const parse: (options: IOptions, argv: any) => IOptions;
|
||||
21
packages/discourse/dist/src/options.js
vendored
Normal file
21
packages/discourse/dist/src/options.js
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
import { resolveConfig } from '@polymech/core';
|
||||
export * from './lib/index.js';
|
||||
export const parse = (options, argv) => {
|
||||
for (const k in argv) {
|
||||
if (!(k in options.variables) && k !== '_'
|
||||
&& k !== '$0'
|
||||
&& k !== 'variables'
|
||||
&& k !== 'src'
|
||||
&& k !== 'format'
|
||||
&& k !== 'profile'
|
||||
&& k !== 'output') {
|
||||
options.variables[k] = argv[k];
|
||||
}
|
||||
}
|
||||
options.variables['cwd'] = options.variables['cwd'] ? options.variables['cwd'] : options.cwd;
|
||||
resolveConfig(options.variables);
|
||||
let variables = {};
|
||||
options.pathVariables = variables;
|
||||
return options;
|
||||
};
|
||||
//# sourceMappingURL=options.js.map
|
||||
1
packages/discourse/dist/src/options.js.map
vendored
Normal file
1
packages/discourse/dist/src/options.js.map
vendored
Normal file
@ -0,0 +1 @@
|
||||
{"version":3,"file":"options.js","sourceRoot":"","sources":["../../src/options.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAA;AAE9C,cAAc,gBAAgB,CAAA;AAK9B,MAAM,CAAC,MAAM,KAAK,GAAG,CAAC,OAAiB,EAAE,IAAS,EAAY,EAAE;IAE5D,KAAK,MAAM,CAAC,IAAI,IAAI,EAAE;QAClB,IAAI,CAAC,CAAC,CAAC,IAAI,OAAO,CAAC,SAAS,CAAC,IAAI,CAAC,KAAK,GAAG;eACnC,CAAC,KAAK,IAAI;eACV,CAAC,KAAK,WAAW;eACjB,CAAC,KAAK,KAAK;eACX,CAAC,KAAK,QAAQ;eACd,CAAC,KAAK,SAAS;eACf,CAAC,KAAK,QAAQ,EAAE;YACnB,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;SAClC;KACJ;IAED,OAAO,CAAC,SAAS,CAAC,KAAK,CAAC,GAAG,OAAO,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAA;IAC5F,aAAa,CAAC,OAAO,CAAC,SAAS,CAAC,CAAA;IAChC,IAAI,SAAS,GAAG,EAAE,CAAA;IAClB,OAAO,CAAC,aAAa,GAAG,SAAS,CAAA;IACjC,OAAO,OAAO,CAAA;AAClB,CAAC,CAAA"}
|
||||
157
packages/discourse/dist/src/types.d.ts
vendored
Normal file
157
packages/discourse/dist/src/types.d.ts
vendored
Normal file
@ -0,0 +1,157 @@
|
||||
import { PATH_INFO, SRC_VARIABLES } from '@polymech/commons';
|
||||
import { EDiscourseConfigKey } from './lib/discourse/constants.js';
|
||||
import { IProcessingNode } from '@polymech/fs/interfaces';
|
||||
export type TFindFilter = (path: string) => any;
|
||||
export interface Hash<T> {
|
||||
[id: string]: T;
|
||||
}
|
||||
export type IOptions = {
|
||||
src: string;
|
||||
id: string;
|
||||
cat: string;
|
||||
track: string;
|
||||
variables: Hash<any>;
|
||||
cwd: string;
|
||||
env: string;
|
||||
verb: string;
|
||||
debug: boolean;
|
||||
disabled: boolean;
|
||||
dry?: boolean;
|
||||
all?: boolean;
|
||||
stdout: boolean;
|
||||
pathVariables: Hash<any>;
|
||||
};
|
||||
/**
|
||||
* An enumeration to narrow a conflict resolve to a single item or for all following conflicts.
|
||||
*
|
||||
* @export
|
||||
* @enum {number}
|
||||
*/
|
||||
export declare enum EResolve {
|
||||
/**
|
||||
* Always will use the chose conflict settings for all following conflicts.
|
||||
*/
|
||||
ALWAYS = 0,
|
||||
/**
|
||||
* 'This' will use the conflict settings for a single conflict so the conflict callback will be triggered again for the next conflict.
|
||||
*/
|
||||
THIS = 1
|
||||
}
|
||||
/**
|
||||
* The possible modes to resolve a conflict during a sync
|
||||
*
|
||||
* @export
|
||||
* @enum {number}
|
||||
*/
|
||||
export declare enum EResolveMode {
|
||||
SKIP = 0,
|
||||
OVERWRITE = 1,
|
||||
IF_NEWER = 2,
|
||||
IF_SIZE_DIFFERS = 3,
|
||||
THROW = 4,
|
||||
RETRY = 5,
|
||||
ABORT = 6
|
||||
}
|
||||
export interface IConflictSettings {
|
||||
/**
|
||||
* How to resolve this conflict/error.
|
||||
*
|
||||
* @type {EResolveMode}
|
||||
* @memberOf IConflictSettings
|
||||
*/
|
||||
overwrite: EResolveMode;
|
||||
/**
|
||||
* The scope of this conflict resolver: always or this.
|
||||
*
|
||||
* @type {EResolve}
|
||||
* @memberOf IConflictSettings
|
||||
*/
|
||||
mode: EResolve;
|
||||
/**
|
||||
* Track the origin error type for this settings.
|
||||
*
|
||||
* @type {string}
|
||||
* @memberOf IConflictSettings
|
||||
*/
|
||||
error?: string;
|
||||
}
|
||||
export type EMergeConflictMode = 'theirs' | 'mine';
|
||||
export type EPostType = 'post' | 'reply';
|
||||
export type ISyncNodeReport = IProcessingNode & {};
|
||||
export interface IDiscoursePostBaseOptions {
|
||||
title?: string;
|
||||
id?: string;
|
||||
cat?: string | number;
|
||||
tags?: string;
|
||||
owner?: string | number;
|
||||
timestamp?: string | number | Date;
|
||||
uploadLocal?: boolean;
|
||||
uploadRemote?: boolean;
|
||||
yaml?: boolean;
|
||||
post_id?: number;
|
||||
topic_id?: number;
|
||||
type?: EPostType;
|
||||
user_name?: string;
|
||||
}
|
||||
export type IOptionsSync = IDiscoursePostBaseOptions & {
|
||||
debug?: boolean;
|
||||
verbose?: boolean;
|
||||
logLevel?: string;
|
||||
skip?: boolean;
|
||||
alt?: boolean;
|
||||
src?: string;
|
||||
verb: string;
|
||||
cache?: boolean;
|
||||
filter?: TFindFilter | string;
|
||||
config?: string | EDiscourseConfigKey;
|
||||
pathVariables?: Hash<any>;
|
||||
variables?: SRC_VARIABLES;
|
||||
repo?: string;
|
||||
root?: string;
|
||||
product_root?: string;
|
||||
srcInfo?: PATH_INFO;
|
||||
post_id?: number;
|
||||
topic_id?: number;
|
||||
};
|
||||
export type IOptionsSyncComponent = IOptionsSync & {
|
||||
format?: string;
|
||||
module?: string;
|
||||
plugins?: string;
|
||||
onCompiled?: () => void;
|
||||
onCompileDone?: () => void;
|
||||
cache?: boolean;
|
||||
skip?: boolean;
|
||||
};
|
||||
export interface IDBConfig {
|
||||
user: string;
|
||||
password: string;
|
||||
database: string;
|
||||
host: string;
|
||||
prefix: string;
|
||||
}
|
||||
export interface IDiscourseUser {
|
||||
id: number;
|
||||
username: string;
|
||||
name: string;
|
||||
avatar_template: string;
|
||||
active: boolean;
|
||||
admin: boolean;
|
||||
moderator: boolean;
|
||||
last_seen_at: any;
|
||||
last_emailed_at: string;
|
||||
created_at: string;
|
||||
last_seen_age: any;
|
||||
last_emailed_age: number;
|
||||
created_at_age: number;
|
||||
trust_level: number;
|
||||
manual_locked_trust_level: any;
|
||||
flag_level: number;
|
||||
title: any;
|
||||
time_read: number;
|
||||
staged: boolean;
|
||||
days_visited: number;
|
||||
posts_read_count: number;
|
||||
topics_entered: number;
|
||||
post_count: number;
|
||||
detail: any;
|
||||
}
|
||||
38
packages/discourse/dist/src/types.js
vendored
Normal file
38
packages/discourse/dist/src/types.js
vendored
Normal file
@ -0,0 +1,38 @@
|
||||
///////////////////////////////////////////////////////
|
||||
//
|
||||
// Sync Types
|
||||
//
|
||||
/**
|
||||
* An enumeration to narrow a conflict resolve to a single item or for all following conflicts.
|
||||
*
|
||||
* @export
|
||||
* @enum {number}
|
||||
*/
|
||||
export var EResolve;
|
||||
(function (EResolve) {
|
||||
/**
|
||||
* Always will use the chose conflict settings for all following conflicts.
|
||||
*/
|
||||
EResolve[EResolve["ALWAYS"] = 0] = "ALWAYS";
|
||||
/**
|
||||
* 'This' will use the conflict settings for a single conflict so the conflict callback will be triggered again for the next conflict.
|
||||
*/
|
||||
EResolve[EResolve["THIS"] = 1] = "THIS";
|
||||
})(EResolve = EResolve || (EResolve = {}));
|
||||
/**
|
||||
* The possible modes to resolve a conflict during a sync
|
||||
*
|
||||
* @export
|
||||
* @enum {number}
|
||||
*/
|
||||
export var EResolveMode;
|
||||
(function (EResolveMode) {
|
||||
EResolveMode[EResolveMode["SKIP"] = 0] = "SKIP";
|
||||
EResolveMode[EResolveMode["OVERWRITE"] = 1] = "OVERWRITE";
|
||||
EResolveMode[EResolveMode["IF_NEWER"] = 2] = "IF_NEWER";
|
||||
EResolveMode[EResolveMode["IF_SIZE_DIFFERS"] = 3] = "IF_SIZE_DIFFERS";
|
||||
EResolveMode[EResolveMode["THROW"] = 4] = "THROW";
|
||||
EResolveMode[EResolveMode["RETRY"] = 5] = "RETRY";
|
||||
EResolveMode[EResolveMode["ABORT"] = 6] = "ABORT";
|
||||
})(EResolveMode = EResolveMode || (EResolveMode = {}));
|
||||
//# sourceMappingURL=types.js.map
|
||||
1
packages/discourse/dist/src/types.js.map
vendored
Normal file
1
packages/discourse/dist/src/types.js.map
vendored
Normal file
@ -0,0 +1 @@
|
||||
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"AA6BA,uDAAuD;AACvD,EAAE;AACF,aAAa;AACb,EAAE;AAEF;;;;;GAKG;AACH,MAAM,CAAN,IAAY,QASX;AATD,WAAY,QAAQ;IAChB;;OAEG;IACH,2CAAM,CAAA;IACN;;OAEG;IACH,uCAAI,CAAA;AACR,CAAC,EATW,QAAQ,GAAR,QAAQ,KAAR,QAAQ,QASnB;AAED;;;;;GAKG;AACH,MAAM,CAAN,IAAY,YAQX;AARD,WAAY,YAAY;IACpB,+CAAQ,CAAA;IACR,yDAAS,CAAA;IACT,uDAAQ,CAAA;IACR,qEAAe,CAAA;IACf,iDAAK,CAAA;IACL,iDAAK,CAAA;IACL,iDAAK,CAAA;AACT,CAAC,EARW,YAAY,GAAZ,YAAY,KAAZ,YAAY,QAQvB"}
|
||||
4
packages/discourse/dist/tests/commons.d.ts
vendored
Normal file
4
packages/discourse/dist/tests/commons.d.ts
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
import { Discourser } from '../src/lib/discourse/index.js';
|
||||
import { IDiscourseConfig } from '@polymech/commons/types';
|
||||
export declare const getDiscourseConfig: () => IDiscourseConfig;
|
||||
export declare const getClient: () => Discourser;
|
||||
15
packages/discourse/dist/tests/commons.js
vendored
Normal file
15
packages/discourse/dist/tests/commons.js
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
process.env['NODE_TLS_REJECT_UNAUTHORIZED'] = '0';
|
||||
import { Discourser } from '../src/lib/discourse/index.js';
|
||||
export const getDiscourseConfig = () => {
|
||||
return {
|
||||
host: process.env.DISCOURSE_HOST || 'https://forum.polymech.info',
|
||||
key: process.env.DISCOURSE_API_KEY || 'e8b4adf4770b85cf94ea5e0aacb517ad25ddeb338efa3946559c06c2cdbab2e8',
|
||||
username: process.env.DISCOURSE_USERNAME || 'jerom',
|
||||
rateLimitConcurrency: 1
|
||||
};
|
||||
};
|
||||
export const getClient = () => {
|
||||
const config = getDiscourseConfig();
|
||||
return new Discourser(config);
|
||||
};
|
||||
//# sourceMappingURL=commons.js.map
|
||||
1
packages/discourse/dist/tests/commons.js.map
vendored
Normal file
1
packages/discourse/dist/tests/commons.js.map
vendored
Normal file
@ -0,0 +1 @@
|
||||
{"version":3,"file":"commons.js","sourceRoot":"","sources":["../../tests/commons.ts"],"names":[],"mappings":"AAAA,OAAO,CAAC,GAAG,CAAC,8BAA8B,CAAC,GAAG,GAAG,CAAC;AAClD,OAAO,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAA;AAG1D,MAAM,CAAC,MAAM,kBAAkB,GAAG,GAAqB,EAAE;IACrD,OAAO;QACH,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,6BAA6B;QACjE,GAAG,EAAE,OAAO,CAAC,GAAG,CAAC,iBAAiB,IAAI,kEAAkE;QACxG,QAAQ,EAAE,OAAO,CAAC,GAAG,CAAC,kBAAkB,IAAI,OAAO;QACnD,oBAAoB,EAAE,CAAC;KAC1B,CAAA;AACL,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,SAAS,GAAG,GAAG,EAAE;IAC1B,MAAM,MAAM,GAAG,kBAAkB,EAAE,CAAA;IACnC,OAAO,IAAI,UAAU,CAAC,MAAM,CAAC,CAAA;AACjC,CAAC,CAAA"}
|
||||
1
packages/discourse/dist/tests/posts.test.d.ts
vendored
Normal file
1
packages/discourse/dist/tests/posts.test.d.ts
vendored
Normal file
@ -0,0 +1 @@
|
||||
export {};
|
||||
16
packages/discourse/dist/tests/posts.test.js
vendored
Normal file
16
packages/discourse/dist/tests/posts.test.js
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { getClient } from './commons.js';
|
||||
describe('Discourse Posts', () => {
|
||||
const client = getClient();
|
||||
it('should fetch posts', async () => {
|
||||
try {
|
||||
const posts = await client.getPostItems();
|
||||
expect(posts).toBeDefined();
|
||||
expect(Array.isArray(posts)).toBe(true);
|
||||
}
|
||||
catch (e) {
|
||||
console.warn('Skipping test due to connection error or config', e);
|
||||
}
|
||||
});
|
||||
});
|
||||
//# sourceMappingURL=posts.test.js.map
|
||||
1
packages/discourse/dist/tests/posts.test.js.map
vendored
Normal file
1
packages/discourse/dist/tests/posts.test.js.map
vendored
Normal file
@ -0,0 +1 @@
|
||||
{"version":3,"file":"posts.test.js","sourceRoot":"","sources":["../../tests/posts.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAC7C,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AAExC,QAAQ,CAAC,iBAAiB,EAAE,GAAG,EAAE;IAC7B,MAAM,MAAM,GAAG,SAAS,EAAE,CAAA;IAE1B,EAAE,CAAC,oBAAoB,EAAE,KAAK,IAAI,EAAE;QAChC,IAAI;YACA,MAAM,KAAK,GAAG,MAAM,MAAM,CAAC,YAAY,EAAE,CAAA;YACzC,MAAM,CAAC,KAAK,CAAC,CAAC,WAAW,EAAE,CAAA;YAC3B,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;SAC1C;QAAC,OAAO,CAAC,EAAE;YACR,OAAO,CAAC,IAAI,CAAC,iDAAiD,EAAE,CAAC,CAAC,CAAA;SACrE;IACL,CAAC,CAAC,CAAA;AACN,CAAC,CAAC,CAAA"}
|
||||
1
packages/discourse/dist/tests/topics.test.d.ts
vendored
Normal file
1
packages/discourse/dist/tests/topics.test.d.ts
vendored
Normal file
@ -0,0 +1 @@
|
||||
export {};
|
||||
37
packages/discourse/dist/tests/topics.test.js
vendored
Normal file
37
packages/discourse/dist/tests/topics.test.js
vendored
Normal file
@ -0,0 +1,37 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { getClient } from './commons.js';
|
||||
describe('Discourse Topics', () => {
|
||||
const client = getClient();
|
||||
it('should fetch latest topics', async () => {
|
||||
try {
|
||||
const topics = await client.getTopicItems();
|
||||
expect(topics).toBeDefined();
|
||||
expect(Array.isArray(topics)).toBe(true);
|
||||
if (topics.length > 0) {
|
||||
expect(topics[0].id).toBeDefined();
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
console.warn('Skipping test due to connection error or config', e);
|
||||
}
|
||||
});
|
||||
it('should fetch a specific topic', async () => {
|
||||
try {
|
||||
// we need a valid ID, assume we have one from the previous list or a known one
|
||||
const topics = await client.getTopicItems({ page: 0 });
|
||||
if (topics && topics.length > 0) {
|
||||
const topicId = topics[0].id;
|
||||
const topic = await client.getTopic(topicId);
|
||||
expect(topic).toBeDefined();
|
||||
expect(topic.id).toBe(topicId);
|
||||
}
|
||||
else {
|
||||
console.warn('No topics found to test getTopic');
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
console.warn('Skipping test due to connection error or config', e);
|
||||
}
|
||||
}, 15000);
|
||||
});
|
||||
//# sourceMappingURL=topics.test.js.map
|
||||
1
packages/discourse/dist/tests/topics.test.js.map
vendored
Normal file
1
packages/discourse/dist/tests/topics.test.js.map
vendored
Normal file
@ -0,0 +1 @@
|
||||
{"version":3,"file":"topics.test.js","sourceRoot":"","sources":["../../tests/topics.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAC7C,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AAExC,QAAQ,CAAC,kBAAkB,EAAE,GAAG,EAAE;IAC9B,MAAM,MAAM,GAAG,SAAS,EAAE,CAAA;IAE1B,EAAE,CAAC,4BAA4B,EAAE,KAAK,IAAI,EAAE;QACxC,IAAI;YACA,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,aAAa,EAAE,CAAA;YAC3C,MAAM,CAAC,MAAM,CAAC,CAAC,WAAW,EAAE,CAAA;YAC5B,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;YACxC,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE;gBACnB,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,WAAW,EAAE,CAAA;aACrC;SACJ;QAAC,OAAO,CAAC,EAAE;YACR,OAAO,CAAC,IAAI,CAAC,iDAAiD,EAAE,CAAC,CAAC,CAAA;SACrE;IACL,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,+BAA+B,EAAE,KAAK,IAAI,EAAE;QAC3C,IAAI;YACA,+EAA+E;YAC/E,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,aAAa,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC,CAAA;YACtD,IAAI,MAAM,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE;gBAC7B,MAAM,OAAO,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAAA;gBAC5B,MAAM,KAAK,GAAG,MAAM,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAA;gBAC5C,MAAM,CAAC,KAAK,CAAC,CAAC,WAAW,EAAE,CAAA;gBAC3B,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;aACjC;iBAAM;gBACH,OAAO,CAAC,IAAI,CAAC,kCAAkC,CAAC,CAAA;aACnD;SACJ;QAAC,OAAO,CAAC,EAAE;YACR,OAAO,CAAC,IAAI,CAAC,iDAAiD,EAAE,CAAC,CAAC,CAAA;SACrE;IACL,CAAC,EAAE,KAAK,CAAC,CAAA;AACb,CAAC,CAAC,CAAA"}
|
||||
1
packages/discourse/dist/tests/users.test.d.ts
vendored
Normal file
1
packages/discourse/dist/tests/users.test.d.ts
vendored
Normal file
@ -0,0 +1 @@
|
||||
export {};
|
||||
28
packages/discourse/dist/tests/users.test.js
vendored
Normal file
28
packages/discourse/dist/tests/users.test.js
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { getClient } from './commons.js';
|
||||
describe('Discourse Users', () => {
|
||||
const client = getClient();
|
||||
it('should fetch active users', async () => {
|
||||
try {
|
||||
const users = await client.getUsers(0);
|
||||
expect(users).toBeDefined();
|
||||
// Depending on response structure, checking properties
|
||||
// expect(users).toHaveProperty('users')
|
||||
}
|
||||
catch (e) {
|
||||
console.warn('Skipping test due to connection error or config', e);
|
||||
}
|
||||
});
|
||||
it('should fetch a specific user by username', async () => {
|
||||
try {
|
||||
const username = 'system';
|
||||
const user = await client.getUserByUsername(username);
|
||||
expect(user).toBeDefined();
|
||||
expect(user.username.toLowerCase()).toBe(username);
|
||||
}
|
||||
catch (e) {
|
||||
console.warn('Skipping test due to connection error or config', e);
|
||||
}
|
||||
});
|
||||
});
|
||||
//# sourceMappingURL=users.test.js.map
|
||||
1
packages/discourse/dist/tests/users.test.js.map
vendored
Normal file
1
packages/discourse/dist/tests/users.test.js.map
vendored
Normal file
@ -0,0 +1 @@
|
||||
{"version":3,"file":"users.test.js","sourceRoot":"","sources":["../../tests/users.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AAC7C,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AAExC,QAAQ,CAAC,iBAAiB,EAAE,GAAG,EAAE;IAC7B,MAAM,MAAM,GAAG,SAAS,EAAE,CAAA;IAE1B,EAAE,CAAC,2BAA2B,EAAE,KAAK,IAAI,EAAE;QACvC,IAAI;YACA,MAAM,KAAK,GAAG,MAAM,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAA;YACtC,MAAM,CAAC,KAAK,CAAC,CAAC,WAAW,EAAE,CAAA;YAC3B,uDAAuD;YACvD,wCAAwC;SAC3C;QAAC,OAAO,CAAC,EAAE;YACR,OAAO,CAAC,IAAI,CAAC,iDAAiD,EAAE,CAAC,CAAC,CAAA;SACrE;IACL,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,0CAA0C,EAAE,KAAK,IAAI,EAAE;QACtD,IAAI;YACA,MAAM,QAAQ,GAAG,QAAQ,CAAA;YACzB,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,iBAAiB,CAAC,QAAQ,CAAC,CAAA;YACrD,MAAM,CAAC,IAAI,CAAC,CAAC,WAAW,EAAE,CAAA;YAC1B,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,WAAW,EAAE,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;SACrD;QAAC,OAAO,CAAC,EAAE;YACR,OAAO,CAAC,IAAI,CAAC,iDAAiD,EAAE,CAAC,CAAC,CAAA;SACrE;IACL,CAAC,CAAC,CAAA;AACN,CAAC,CAAC,CAAA"}
|
||||
1958
packages/discourse/package-lock.json
generated
1958
packages/discourse/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -18,10 +18,11 @@
|
||||
"@polymech/commons": "file:../commons",
|
||||
"@polymech/core": "file:../core",
|
||||
"@polymech/fs": "file:../fs",
|
||||
"@polymech/log": "file:../log",
|
||||
"@polymech/media": "file:../media",
|
||||
"@polymech/registry": "file:../registry",
|
||||
"@types/markdown-it": "^12.2.3",
|
||||
"@types/node": "^14.17.5",
|
||||
"@types/node": "^25.0.3",
|
||||
"@types/yargs": "^17.0.11",
|
||||
"axios": "^0.27.2",
|
||||
"bluebird": "^3.7.2",
|
||||
|
||||
@ -1,8 +1,3 @@
|
||||
import { sync as exists } from '@polymech/fs/exists'
|
||||
import { IOptions, IOptionsSync } from './types.js'
|
||||
import { logger } from './index.js'
|
||||
import * as path from 'path'
|
||||
|
||||
export const defaults = () => {
|
||||
// default command
|
||||
const DefaultCommand = 'info';
|
||||
@ -17,58 +12,3 @@ export const defaults = () => {
|
||||
console.error('Unhandled rejection, reason: ', reason);
|
||||
});
|
||||
}
|
||||
|
||||
export const sanitize = (argv: any): IOptionsSync | IOptions | boolean => {
|
||||
|
||||
let ret: any = {
|
||||
...argv
|
||||
}
|
||||
|
||||
ret.src = argv.src
|
||||
let srcInfo
|
||||
let variables = {}
|
||||
|
||||
/*
|
||||
if (ret.src) {
|
||||
ret.src = forward_slash(substitute(ret.alt,ret.src,variables))
|
||||
// in case a file with a glob pattern is provided, strip the glob
|
||||
// this is a special case, enabling shared scripts in Alt-Tap Salamand
|
||||
const glob_base = globBase(ret.src)
|
||||
const file = ret.src.replace(glob_base.glob, '').replace(/\/$/, '')
|
||||
|
||||
if(exists(file) && isFile(file)){
|
||||
ret.src = file
|
||||
}
|
||||
|
||||
srcInfo = pathInfo(resolve(ret.src, ret.alt, variables))
|
||||
|
||||
if (srcInfo && srcInfo.FILES && srcInfo.FILES.length) {
|
||||
ret.srcInfo = srcInfo
|
||||
for (const key in srcInfo) {
|
||||
if (Object.prototype.hasOwnProperty.call(srcInfo, key)) {
|
||||
variables['SRC_' + key] = srcInfo[key];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ret.src = resolve(ret.src, ret.alt, variables)
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
if (argv.cwd) {
|
||||
ret.cwd = path.resolve(argv.cwd);
|
||||
if (!exists((ret.cwd))) {
|
||||
logger.error(`Invalid working directory ${argv.cwd}`)
|
||||
}
|
||||
} else {
|
||||
ret.cwd = process.cwd()
|
||||
}
|
||||
|
||||
ret = {
|
||||
...ret,
|
||||
variables,
|
||||
srcInfo
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
4
packages/discourse/src/cli.ts
Normal file
4
packages/discourse/src/cli.ts
Normal file
@ -0,0 +1,4 @@
|
||||
import yargs from 'yargs'
|
||||
import { hideBin } from 'yargs/helpers'
|
||||
|
||||
export const cli = yargs(hideBin(process.argv))
|
||||
@ -1,19 +1,7 @@
|
||||
export * from './constants.js'
|
||||
export * from './types.js'
|
||||
export * from './lib/index.js'
|
||||
export * from './lib/oa/types.js'
|
||||
|
||||
import { logger as _logger } from '@polymech/core/debug'
|
||||
|
||||
import { MODULE_NAME } from './constants'
|
||||
import { ISettingsParam, Logger, ILogObject } from "tslog"
|
||||
export { Logger } from 'tslog'
|
||||
|
||||
let loggers = {};
|
||||
export const logger = _logger(MODULE_NAME)
|
||||
|
||||
import { substitute as _substitute, substituteAlt as _substituteAlt } from "@polymech/core/strings"
|
||||
|
||||
import { IObjectLiteral } from "@polymech/core"
|
||||
|
||||
export const substitute = (alt: boolean, template: string, vars: IObjectLiteral = {}) => alt ? _substituteAlt(template, vars) : _substitute(template, vars)
|
||||
|
||||
@ -4,9 +4,9 @@ import { CONFIG_DEFAULT } from '@polymech/commons'
|
||||
|
||||
import { IOSRConfig, IDiscourseConfig } from '@polymech/commons/types'
|
||||
|
||||
import { logger as _logger } from '../index.js'
|
||||
import { createLogger } from '@polymech/log'
|
||||
|
||||
export const logger: any = _logger(MODULE_NAME)
|
||||
const logger = createLogger(MODULE_NAME)
|
||||
|
||||
import { sync as write } from '@polymech/fs/write'
|
||||
import { sync as read } from '@polymech/fs/write'
|
||||
@ -758,7 +758,7 @@ export class Discourser {
|
||||
|
||||
|
||||
|
||||
async upload(userId, file): Promise<IDImage[]> {
|
||||
async upload(userId, file): Promise<any[]> {
|
||||
// fetch whole posts
|
||||
const url = `${this.host}/uploads.json`;
|
||||
|
||||
@ -1086,78 +1086,6 @@ export class Discourser {
|
||||
})
|
||||
}
|
||||
|
||||
/*
|
||||
{
|
||||
"bio_raw": "about me\n\ntest",
|
||||
"website": "https://shop.osr-plastic.org",
|
||||
"location": "Barcelona",
|
||||
"custom_fields": {
|
||||
"geo_location": {
|
||||
"lat": "9.4170689",
|
||||
"lon": "123.3351935",
|
||||
"address": "Santander, Cebu, Central Visayas, 6026, Philippines",
|
||||
"countrycode": "ph",
|
||||
"city": "Santander",
|
||||
"state": "Cebu",
|
||||
"country": "Philippines",
|
||||
"postalcode": "6026",
|
||||
"boundingbox": [
|
||||
"9.3630227",
|
||||
"9.491731",
|
||||
"123.2684005",
|
||||
"123.3642727"
|
||||
],
|
||||
"type": "administrative"
|
||||
}
|
||||
},
|
||||
"timezone": "Europe/Berlin",
|
||||
"default_calendar": "none_selected"
|
||||
}*/
|
||||
/*
|
||||
fetch("https://forum.osr-plastic.org/u/katharinaelleke.json", {
|
||||
"headers": {
|
||||
"content-type": "application/x-www-form-urlencoded; charset=UTF-8",
|
||||
"discourse-logged-in": "true",
|
||||
"discourse-present": "true",
|
||||
"sec-ch-ua": "\"Not.A/Brand\";v=\"8\", \"Chromium\";v=\"114\", \"Google Chrome\";v=\"114\"",
|
||||
"sec-ch-ua-mobile": "?0",
|
||||
"sec-ch-ua-platform": "\"Windows\"",
|
||||
"x-csrf-token": "0Nau_ylBzR68D5OvfxfOHYii6GI_7gSrr_Ci_9S8ZDjqjy5mavF_O_INAPD8xfCQHgJkDH4rdgS0kPfjMAMBFw",
|
||||
"x-requested-with": "XMLHttpRequest",
|
||||
"Referer": "https://forum.osr-plastic.org/u/katharinaelleke/preferences/profile",
|
||||
"Referrer-Policy": "strict-origin-when-cross-origin"
|
||||
},
|
||||
"body": "bio_raw=Curious%2C+serious+and+not+so+serious+%3Aslight_smile%3A+%0A&website=https%3A%2F%2Fwww.theflipflopi.com%2F&custom_fields%5Bnotify_me_when_followed%5D=false&custom_fields%5Bnotify_followed_user_when_followed%5D=false&custom_fields%5Bnotify_me_when_followed_replies%5D=false&custom_fields%5Bnotify_me_when_followed_creates_topic%5D=false&custom_fields%5Ballow_people_to_follow_me%5D=false&custom_fields%5Bgeo_location%5D%5Blat%5D=-2.294164&custom_fields%5Bgeo_location%5D%5Blon%5D=40.91501&custom_fields%5Bgeo_location%5D%5Baddress%5D=African+Corner%2C+Lamu%2C+Coast%2C+Kenya&custom_fields%5Bgeo_location%5D%5Bcountrycode%5D=ke&custom_fields%5Bgeo_location%5D%5Bcity%5D=&custom_fields%5Bgeo_location%5D%5Bstate%5D=Coast&custom_fields%5Bgeo_location%5D%5Bcountry%5D=Kenya&custom_fields%5Bgeo_location%5D%5Bpostalcode%5D=&custom_fields%5Bgeo_location%5D%5Bboundingbox%5D%5B%5D=-2.294164&custom_fields%5Bgeo_location%5D%5Bboundingbox%5D%5B%5D=-2.294164&custom_fields%5Bgeo_location%5D%5Bboundingbox%5D%5B%5D=40.91501&custom_fields%5Bgeo_location%5D%5Bboundingbox%5D%5B%5D=40.91501&custom_fields%5Bgeo_location%5D%5Btype%5D=&user_fields%5B1%5D=https%3A%2F%2Fshop.osr-plastic.org%2Fafrica%2F&user_fields%5B2%5D=https%3A%2F%2Fwww.theflipflopi.com%2F&user_fields%5B3%5D=https%3A%2F%2Fwww.instagram.com%2Fkatatungo%2F&user_fields%5B4%5D=unknown&user_fields%5B5%5D=unknown&profile_background_upload_url=https%3A%2F%2Fforum.osr-plastic.org%2Fuploads%2Fdefault%2Foriginal%2F2X%2F0%2F0875e1aea65f41172b9c4ad60b1a372cbaa72b2b.jpeg&card_background_upload_url=https%3A%2F%2Fforum.osr-plastic.org%2Fuploads%2Fdefault%2Foriginal%2F2X%2F3%2F357088c493bd0f66e651211853fb18beee2ec2e4.jpeg&timezone=Africa%2FNairobi&default_calendar=none_selected",
|
||||
"method": "PUT"
|
||||
});
|
||||
|
||||
|
||||
fetch("https://forum.osr-plastic.org/u/admin-osr.json", {
|
||||
"headers": {
|
||||
"accept-language": "en-GB,en-US;q=0.9,en;q=0.8",
|
||||
"content-type": "application/x-www-form-urlencoded; charset=UTF-8",
|
||||
"discourse-logged-in": "true",
|
||||
"discourse-present": "true",
|
||||
"sec-ch-ua": "\"Not/A)Brand\";v=\"99\", \"Google Chrome\";v=\"115\", \"Chromium\";v=\"115\"",
|
||||
"sec-ch-ua-mobile": "?0",
|
||||
"sec-ch-ua-platform": "\"Windows\"",
|
||||
"sec-fetch-dest": "empty",
|
||||
"sec-fetch-mode": "cors",
|
||||
"sec-fetch-site": "same-origin",
|
||||
"x-csrf-token": "gjd-DOi_nULFDhLyUh4igqFZENxL6B4rufR3dfbY9H9Soc-YDwOQS1SYSwJlj6f2f7BpDf5CO9y-zyOPBj-E1Q",
|
||||
"x-requested-with": "XMLHttpRequest",
|
||||
"cookie": "_ga=GA1.1.983567986.1691921410; _t=onvQ4UWFfLfjFsFkmebQdDot6JXW8%2FAWgFEtJUZ51AqXbTKnI%2BPF80usnZkhYqyYYW9Q%2B1fuMyEy5bDXc9AJpwwkcVIg%2Fu1DTE39y3nGdpXMiiVsmkMqBkyGA%2FvpvZUrM23CDS0xT%2B09VSSlNUbu8lZY27hYWXio4ETx7DRTmBk0xY52d3R6bcmtofynfxldP5KceF0APNtl9AV6iUhWloQ32CK6PmzpSFq4E3hstsS1WPTf7SwLGvESWm4tn3M4EQ2v4RHuLiHaAj%2F0--mv8Byxo5aHGcgKPO--%2F9eiy%2B24FCNmiLF3%2FpGZag%3D%3D; _ga_GVR8PEPG6C=GS1.1.1691940751.2.1.1691940775.0.0.0; _forum_session=bLbNekWpq6fkdK%2B19Nw6ZoKeEk9Ijp8LPfmQAjBiGK0TcnIYzMhZ5LJmLrxmG39e85SgUz%2F7NMI3NXZM9j%2F35eJuO3hxnUAkFvBNwjwwS9LGhDB4kB3ebAjjcBpoLPzUMWqyslQuR9RM8JcjZ%2FfX%2FWw0E61l9jhfrG0dr8Ds97IW7XedjKCmI7x7xinv10R2bRb51%2F72t2Z4dVG86wyCAtI5Spom0yjHoWPHwWBZ7VPYtOCmSBVygusuF%2FW5hpO9wgn1oqc0ZZ%2BXj%2FPiFc0FsADEcKYh3Q%3D%3D--xdEsXmADtTKkFGSc--x%2Fd9m02lRNk0uH%2BPxZTNwQ%3D%3D",
|
||||
"Referer": "https://forum.osr-plastic.org/u/admin-osr/preferences/profile",
|
||||
"Referrer-Policy": "strict-origin-when-cross-origin"
|
||||
},
|
||||
"body": "bio_raw=%23%23+Test%0A%0A%60%60%60js%0Aconst+t+%3D+0%0A%60%60%60%0A%0A%5Btest%5D(www.test.com)%0A%0A&website=www.test-site.com&location=test-loc&profile_background_upload_url=https%3A%2F%2Fforum.osr-plastic.org%2Fuploads%2Fdefault%2Foriginal%2F2X%2Fd%2Fd097423e34a6c677cdb0933d091fb84bcfbcec2e.jpeg&card_background_upload_url=https%3A%2F%2Fforum.osr-plastic.org%2Fuploads%2Fdefault%2Foriginal%2F2X%2Fb%2Fb4c30d1b981964fd6f936ebaac0f86ad4dc01209.png&timezone=Europe%2FBerlin&default_calendar=none_selected",
|
||||
"method": "PUT"
|
||||
});
|
||||
|
||||
|
||||
*/
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { RegExCallback } from './types'
|
||||
import { RegExCallback } from './types.js'
|
||||
export class Pattern {
|
||||
regex: RegExp;
|
||||
replacement: RegExCallback
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { Pattern } from './Pattern';
|
||||
import { Pattern } from './Pattern.js';
|
||||
|
||||
export class Rule {
|
||||
name: string;
|
||||
|
||||
@ -1,14 +1,12 @@
|
||||
import { Rule } from './Rule'
|
||||
import { Pattern } from './Pattern'
|
||||
import { Rule } from './Rule.js'
|
||||
import { Pattern } from './Pattern.js'
|
||||
|
||||
import { RMarkOptions } from './types'
|
||||
import { RMarkOptions } from './types.js'
|
||||
|
||||
export const RE_IMAGES: RegExp = /\!\[([^\]]+)\]\((\S+)\)/g
|
||||
export const RE_LINKS: RegExp = /\[([^\n]+)\]\(([^\n]+)\)/g
|
||||
|
||||
import * as markdown from 'markdown-it'
|
||||
import Token from "markdown-it/lib/token"
|
||||
import Renderer from "markdown-it/lib/renderer"
|
||||
import markdown from 'markdown-it'
|
||||
|
||||
const defaultRules: Rule[] = [
|
||||
new Rule('header', [
|
||||
@ -88,8 +86,8 @@ export class RMark {
|
||||
}
|
||||
}
|
||||
|
||||
export { Rule } from './Rule';
|
||||
export { Pattern } from './Pattern';
|
||||
export { Rule } from './Rule.js';
|
||||
export { Pattern } from './Pattern.js';
|
||||
|
||||
// export const find = (content:string, reg:RegExp) => content.match(reg)
|
||||
|
||||
@ -100,7 +98,7 @@ export const toHTML = (content) => {
|
||||
|
||||
const md = new markdown({
|
||||
html: true,
|
||||
breaks:true
|
||||
breaks: true
|
||||
})
|
||||
|
||||
return md.render(content)
|
||||
|
||||
@ -1,17 +1,7 @@
|
||||
#!/usr/bin/env node
|
||||
process.env['NODE_TLS_REJECT_UNAUTHORIZED'] = '0';
|
||||
|
||||
import { defaults } from './_cli'; defaults();
|
||||
import * as cli from 'yargs';
|
||||
import { register as registerInfo } from './commands/info.js'; registerInfo(cli)
|
||||
import { register as registerQuery } from './commands/query.js'; registerQuery(cli)
|
||||
|
||||
import { register as registerOAImport } from './commands/import-oa-users.js'; registerOAImport(cli)
|
||||
import { register as registerOAImportHowtos } from './commands/import-oa-howtos.js'; registerOAImportHowtos(cli)
|
||||
|
||||
import { register as registerSync } from './commands/sync-file.js'; registerSync(cli)
|
||||
import { register as registerSyncComponent } from './commands/sync-component.js'; registerSyncComponent(cli)
|
||||
|
||||
import { defaults } from './_cli.js'; defaults();
|
||||
import { cli } from './cli.js'
|
||||
const argv: any = cli.argv;
|
||||
|
||||
if (argv.help) {
|
||||
|
||||
17
packages/discourse/tests/commons.ts
Normal file
17
packages/discourse/tests/commons.ts
Normal file
@ -0,0 +1,17 @@
|
||||
process.env['NODE_TLS_REJECT_UNAUTHORIZED'] = '0';
|
||||
import { Discourser } from '../src/lib/discourse/index.js'
|
||||
import { IDiscourseConfig } from '@polymech/commons/types'
|
||||
|
||||
export const getDiscourseConfig = (): IDiscourseConfig => {
|
||||
return {
|
||||
host: process.env.DISCOURSE_HOST || 'https://forum.polymech.info',
|
||||
key: process.env.DISCOURSE_API_KEY || 'e8b4adf4770b85cf94ea5e0aacb517ad25ddeb338efa3946559c06c2cdbab2e8',
|
||||
username: process.env.DISCOURSE_USERNAME || 'jerom',
|
||||
rateLimitConcurrency: 1
|
||||
}
|
||||
}
|
||||
|
||||
export const getClient = () => {
|
||||
const config = getDiscourseConfig()
|
||||
return new Discourser(config)
|
||||
}
|
||||
16
packages/discourse/tests/posts.test.ts
Normal file
16
packages/discourse/tests/posts.test.ts
Normal file
@ -0,0 +1,16 @@
|
||||
import { describe, it, expect } from 'vitest'
|
||||
import { getClient } from './commons.js'
|
||||
|
||||
describe('Discourse Posts', () => {
|
||||
const client = getClient()
|
||||
|
||||
it('should fetch posts', async () => {
|
||||
try {
|
||||
const posts = await client.getPostItems()
|
||||
expect(posts).toBeDefined()
|
||||
expect(Array.isArray(posts)).toBe(true)
|
||||
} catch (e) {
|
||||
console.warn('Skipping test due to connection error or config', e)
|
||||
}
|
||||
})
|
||||
})
|
||||
36
packages/discourse/tests/topics.test.ts
Normal file
36
packages/discourse/tests/topics.test.ts
Normal file
@ -0,0 +1,36 @@
|
||||
import { describe, it, expect } from 'vitest'
|
||||
import { getClient } from './commons.js'
|
||||
|
||||
describe('Discourse Topics', () => {
|
||||
const client = getClient()
|
||||
|
||||
it('should fetch latest topics', async () => {
|
||||
try {
|
||||
const topics = await client.getTopicItems()
|
||||
expect(topics).toBeDefined()
|
||||
expect(Array.isArray(topics)).toBe(true)
|
||||
if (topics.length > 0) {
|
||||
expect(topics[0].id).toBeDefined()
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('Skipping test due to connection error or config', e)
|
||||
}
|
||||
})
|
||||
|
||||
it('should fetch a specific topic', async () => {
|
||||
try {
|
||||
// we need a valid ID, assume we have one from the previous list or a known one
|
||||
const topics = await client.getTopicItems({ page: 0 })
|
||||
if (topics && topics.length > 0) {
|
||||
const topicId = topics[0].id
|
||||
const topic = await client.getTopic(topicId)
|
||||
expect(topic).toBeDefined()
|
||||
expect(topic.id).toBe(topicId)
|
||||
} else {
|
||||
console.warn('No topics found to test getTopic')
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('Skipping test due to connection error or config', e)
|
||||
}
|
||||
}, 15000)
|
||||
})
|
||||
28
packages/discourse/tests/users.test.ts
Normal file
28
packages/discourse/tests/users.test.ts
Normal file
@ -0,0 +1,28 @@
|
||||
import { describe, it, expect } from 'vitest'
|
||||
import { getClient } from './commons.js'
|
||||
|
||||
describe('Discourse Users', () => {
|
||||
const client = getClient()
|
||||
|
||||
it('should fetch active users', async () => {
|
||||
try {
|
||||
const users = await client.getUsers(0)
|
||||
expect(users).toBeDefined()
|
||||
// Depending on response structure, checking properties
|
||||
// expect(users).toHaveProperty('users')
|
||||
} catch (e) {
|
||||
console.warn('Skipping test due to connection error or config', e)
|
||||
}
|
||||
})
|
||||
|
||||
it('should fetch a specific user by username', async () => {
|
||||
try {
|
||||
const username = 'system'
|
||||
const user = await client.getUserByUsername(username)
|
||||
expect(user).toBeDefined()
|
||||
expect(user.username.toLowerCase()).toBe(username)
|
||||
} catch (e) {
|
||||
console.warn('Skipping test due to connection error or config', e)
|
||||
}
|
||||
})
|
||||
})
|
||||
@ -1,7 +1,8 @@
|
||||
{
|
||||
"extends": "../typescript-config/base.json",
|
||||
"include": [
|
||||
"src/**/*.ts"
|
||||
"src/**/*.ts",
|
||||
"tests/**/*.ts"
|
||||
],
|
||||
"files": [
|
||||
"src/index.ts"
|
||||
|
||||
Loading…
Reference in New Issue
Block a user