@unbound-app/api 2.2.0 → 3.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@unbound-app/api",
3
- "version": "2.2.0",
3
+ "version": "3.1.0",
4
4
  "author": {
5
5
  "name": "Mario P.",
6
6
  "email": "contact@marioparaschiv.com",
@@ -45,7 +45,7 @@
45
45
  "./storage": "./src/storage.d.ts",
46
46
  "./sync": "./src/sync.d.ts",
47
47
  "./testing": "./testing/index.ts",
48
- "./testing/icu-parse": "./testing/icu-parse.ts",
48
+ "./testing/fakes": "./testing/fakes.ts",
49
49
  "./testing/preset": "./testing/preset.ts",
50
50
  "./themes": "./src/themes.d.ts",
51
51
  "./toasts": "./src/toasts.d.ts",
@@ -0,0 +1,59 @@
1
+ // Auto-generated by generate-plugin-sdk.ts - DO NOT EDIT
2
+
3
+ /** The folder, at the plugin root and beside the built bundle, that holds a plugin's images. */
4
+ export const ASSETS_DIRECTORY = 'assets';
5
+
6
+ /** The image types a plugin ships: the formats React Native's `Image` decodes. An SVG is a component, not an asset. */
7
+ export const ASSET_TYPES = ['png', 'jpg', 'webp', 'gif'] as const;
8
+
9
+ /** One of {@link ASSET_TYPES}. */
10
+ export type AssetType = (typeof ASSET_TYPES)[number];
11
+
12
+ /** The pixel densities a plugin image can ship at, as its `@2x` and `@3x` file suffixes name them. */
13
+ export const ASSET_SCALES = ['1', '2', '3'] as const;
14
+
15
+ /** Path segments joined by `/`, each starting with a letter, digit, `_` or `-`, so no name can leave its folder. */
16
+ export const ASSET_NAME_PATTERN = '^[\\w-][\\w.-]*(?:/[\\w-][\\w.-]*)*$';
17
+
18
+ const ASSET_NAME = new RegExp(ASSET_NAME_PATTERN);
19
+
20
+ /**
21
+ * @description Tells whether `value` can name a plugin asset: path segments without `..`, at most 200 characters.
22
+ * @param value The untrusted value.
23
+ * @returns Whether it is a safe asset name.
24
+ */
25
+ // UNKNOWN: a name read from a manifest or a file path.
26
+ export function isAssetName(value: unknown): value is string {
27
+ return typeof value === 'string' && value.length <= 200 && ASSET_NAME.test(value);
28
+ }
29
+
30
+ /**
31
+ * @description Tells whether `value` is one of {@link ASSET_TYPES}.
32
+ * @param value The untrusted value.
33
+ * @returns Whether a plugin can ship an image of this type.
34
+ */
35
+ // UNKNOWN: a type read from a manifest or a file extension.
36
+ export function isAssetType(value: unknown): value is AssetType {
37
+ return ASSET_TYPES.some((type) => type === value);
38
+ }
39
+
40
+ /**
41
+ * @description Names an image file beside an addon's bundle. Files are named by content, so equal images share one.
42
+ * @param main The manifest's `main`, such as `index.js`.
43
+ * @param sha256 The file's SHA-256.
44
+ * @param type The file's type.
45
+ * @returns The file's path relative to the manifest, such as `assets/<sha256>.png`.
46
+ */
47
+ export function getAssetPath(main: string, sha256: string, type: AssetType): string {
48
+ return `${main.slice(0, main.lastIndexOf('/') + 1)}${ASSETS_DIRECTORY}/${sha256}.${type}`;
49
+ }
50
+
51
+ export default {
52
+ ASSETS_DIRECTORY,
53
+ ASSET_TYPES,
54
+ ASSET_SCALES,
55
+ ASSET_NAME_PATTERN,
56
+ isAssetName,
57
+ isAssetType,
58
+ getAssetPath,
59
+ };
@@ -0,0 +1,50 @@
1
+ // Auto-generated by generate-plugin-sdk.ts - DO NOT EDIT
2
+
3
+ /** The Discord locale every plugin ships in its bundle, and the source of truth for its message keys. */
4
+ export const SOURCE_LOCALE = 'en-US';
5
+
6
+ /** The folder, beside a plugin's `src/` and beside its built bundle, that holds one JSON file per locale. */
7
+ export const LOCALES_DIRECTORY = 'i18n';
8
+
9
+ /** A Discord locale code, such as `de`, `en-GB` or `es-419`. It names a file, so it holds no path characters. */
10
+ export const LOCALE_CODE_PATTERN = '^[a-z]{2,3}(?:-[A-Za-z0-9]{2,8})*$';
11
+
12
+ const LOCALE_CODE = new RegExp(LOCALE_CODE_PATTERN);
13
+
14
+ /**
15
+ * @description Tells whether `value` is a locale code that can name a locale file.
16
+ * @param value The untrusted value.
17
+ * @returns Whether it is a path-safe locale code.
18
+ */
19
+ // UNKNOWN: a code read from a manifest or a file name.
20
+ export function isLocaleCode(value: unknown): value is string {
21
+ return typeof value === 'string' && value.length <= 35 && LOCALE_CODE.test(value);
22
+ }
23
+
24
+ /**
25
+ * @description Names the folder of locale files beside an addon's bundle.
26
+ * @param main The manifest's `main`, such as `index.js`.
27
+ * @returns The folder's path relative to the manifest, such as `i18n`.
28
+ */
29
+ export function getLocalesDirectory(main: string): string {
30
+ return `${main.slice(0, main.lastIndexOf('/') + 1)}${LOCALES_DIRECTORY}`;
31
+ }
32
+
33
+ /**
34
+ * @description Names a locale file beside an addon's bundle, as the build emits it and the client downloads it.
35
+ * @param main The manifest's `main`, such as `index.js`.
36
+ * @param code The locale code.
37
+ * @returns The file's path relative to the manifest, such as `i18n/de.json`.
38
+ */
39
+ export function getLocalePath(main: string, code: string): string {
40
+ return `${getLocalesDirectory(main)}/${code}.json`;
41
+ }
42
+
43
+ export default {
44
+ SOURCE_LOCALE,
45
+ LOCALES_DIRECTORY,
46
+ LOCALE_CODE_PATTERN,
47
+ isLocaleCode,
48
+ getLocalesDirectory,
49
+ getLocalePath,
50
+ };
@@ -1,3 +1,5 @@
1
+ // Auto-generated by generate-plugin-sdk.ts - DO NOT EDIT
2
+
1
3
  /** The branches of a `plural`, `selectordinal` or `select` argument, by selector, such as `one` or `=0`. */
2
4
  export type IcuBranches = Map<string, IcuNode[]>;
3
5
 
@@ -0,0 +1,37 @@
1
+ // Auto-generated by generate-plugin-sdk.ts - DO NOT EDIT
2
+
3
+ /** The key whose own descriptor marks a lazy metro handle, read without resolving it. */
4
+ export const LAZY_MARKER = '__METRO_LAZY__';
5
+
6
+ /** The props each `metro.common` member is found by, as the client's lookups find them. */
7
+ export const COMMON_PROPS = {
8
+ Reanimated: ['useAnimatedStyle', 'useSharedValue'],
9
+ Gestures: ['PanGestureHandler'],
10
+ Clipboard: ['setString', 'getString', 'setImage', 'getImage'],
11
+ MarkdownParser: ['parse', 'parseToAST', 'reactParserFor'],
12
+ SimpleMarkdown: ['parserFor', 'outputFor', 'defaultRules'],
13
+ Screens: ['FullWindowOverlay'],
14
+ SVG: ['Svg', 'Path'],
15
+ Commands: ['getBuiltInCommands'],
16
+ Moment: ['isMoment'],
17
+ Util: ['inspect'],
18
+ Assets: ['registerAsset'],
19
+ ImageSources: ['getImageSourcesFromImageProps'],
20
+ Clyde: ['createBotMessage'],
21
+ Constants: ['Fonts', 'Endpoints'],
22
+ Radius: ['Radius'],
23
+ Theme: ['colors', 'internal'],
24
+ REST: ['getAPIBaseURL'],
25
+ i18n: ['getLanguages', 'getSystemLocale', 'intl'],
26
+ } satisfies Record<string, string[]>;
27
+
28
+ /** The props each `metro.api` member is found by, as the client's lookups find them. */
29
+ export const API_PROPS = {
30
+ Messages: ['sendMessage', 'receiveMessage'],
31
+ Profiles: ['showUserProfile'],
32
+ AsyncUsers: ['fetchProfile'],
33
+ Invites: ['acceptInviteAndTransitionToInviteChannel'],
34
+ OAuth2: ['openOAuth2Modal'],
35
+ ActionSheets: ['openLazy', 'hideActionSheet'],
36
+ UserProfiles: ['getUserProfileActionSheetKey', 'showUserProfileActionSheetPostConnection'],
37
+ } satisfies Record<string, string[]>;
@@ -0,0 +1,88 @@
1
+ // Auto-generated by generate-plugin-sdk.ts - DO NOT EDIT
2
+
3
+ import type { PredicateResult } from '../_types';
4
+
5
+ /** A node in an object graph, such as a React tree. Discord's trees carry no static types, so a predicate reads them unchecked. */
6
+ // oxlint-disable-next-line typescript/no-explicit-any -- tree nodes are whatever Discord built
7
+ export type TreeNode = any;
8
+
9
+ /** Tests one node of a tree; a truthy result is a match. */
10
+ export type TreePredicate = (node: TreeNode) => PredicateResult;
11
+
12
+ /** How a tree search descends. */
13
+ export type TreeSearchOptions = {
14
+ /** The only keys descended into; every own key when omitted. Array items are always walked. */
15
+ walkable?: string[];
16
+ /** Keys never descended into. */
17
+ ignore?: string[];
18
+ /** How many levels below the root to descend. Defaults to 100. */
19
+ maxDepth?: number;
20
+ };
21
+
22
+ /**
23
+ * @description Walks an object graph depth-first from its root, testing every non-nullish value once, and collects matches.
24
+ * @param root The node to start from; it is tested too.
25
+ * @param predicate Tests each node.
26
+ * @param options How the search descends.
27
+ * @param all Whether to collect every match rather than stop at the first.
28
+ * @returns The matches, in visiting order.
29
+ */
30
+ function walkTree(
31
+ root: TreeNode,
32
+ predicate: TreePredicate,
33
+ options: TreeSearchOptions,
34
+ all: boolean,
35
+ ): TreeNode[] {
36
+ const { walkable, ignore = [], maxDepth = 100 } = options;
37
+ const seen = new WeakSet<object>();
38
+ const found: TreeNode[] = [];
39
+
40
+ // Returns `true` once the search should stop.
41
+ function visit(node: TreeNode, depth: number): boolean {
42
+ if (node === null || node === undefined) {
43
+ return false;
44
+ }
45
+
46
+ const isObject = typeof node === 'object';
47
+
48
+ if (isObject) {
49
+ if (seen.has(node)) {
50
+ return false;
51
+ }
52
+
53
+ seen.add(node);
54
+ }
55
+
56
+ if (predicate(node)) {
57
+ found.push(node);
58
+
59
+ if (!all) {
60
+ return true;
61
+ }
62
+ }
63
+
64
+ if (!isObject || depth >= maxDepth) {
65
+ return false;
66
+ }
67
+
68
+ const keys = Array.isArray(node) ? node.keys() : (walkable ?? Object.keys(node));
69
+
70
+ for (const key of keys) {
71
+ if (typeof key === 'string' && ignore.includes(key)) {
72
+ continue;
73
+ }
74
+
75
+ if (visit(node[key], depth + 1)) {
76
+ return true;
77
+ }
78
+ }
79
+
80
+ return false;
81
+ }
82
+
83
+ visit(root, 0);
84
+
85
+ return found;
86
+ }
87
+
88
+ export default walkTree;