(https://phineas.io)",
27 | "license": "ISC",
28 | "bugs": {
29 | "url": "https://github.com/hopinc/pika/issues"
30 | },
31 | "homepage": "https://github.com/hopinc/pika#readme",
32 | "devDependencies": {
33 | "@types/node": "^18.8.3",
34 | "benchmark": "^2.1.4",
35 | "prettier": "^2.7.1",
36 | "tsup": "^6.2.3",
37 | "tsx": "^3.10.1",
38 | "typescript": "^4.8.4"
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/impl/js/src/index.ts:
--------------------------------------------------------------------------------
1 | export {Pika, Pika as default} from './pika';
2 | export type {InferIds, InferPrefixes} from './pika';
3 |
--------------------------------------------------------------------------------
/impl/js/src/logger.ts:
--------------------------------------------------------------------------------
1 | const PREFIX = '[pika]';
2 |
3 | export const warn = (...args: unknown[]) => {
4 | console.warn(`${PREFIX}`, ...args);
5 | };
6 |
7 | export const error = (...args: unknown[]) => {
8 | console.error(`${PREFIX}`, ...args);
9 | };
10 |
--------------------------------------------------------------------------------
/impl/js/src/pika.ts:
--------------------------------------------------------------------------------
1 | import {randomBytes} from 'crypto';
2 | import {networkInterfaces} from 'os';
3 | import {error, warn} from './logger';
4 | import {DeconstructedSnowflake, EpochResolvable, Snowflake} from './snowflake';
5 |
6 | export interface PikaPrefixDefinition {
7 | prefix: P;
8 | description?: string;
9 | secure?: boolean;
10 | metadata?: Record;
11 | }
12 |
13 | export interface DecodedPika extends Omit {
14 | prefix: P;
15 |
16 | /**
17 | * The tail after the prefix, which is base64 encoding of the snowflake.
18 | *
19 | * However, if the pika is cryptographically secure, then the base64 decoded string value will start with an `s_` prefix,
20 | * followed by a cryptographically random string, then followed by another underscore and the Snowflake ID.
21 | */
22 | tail: string;
23 |
24 | /**
25 | * The snowfake that was generated for this ID
26 | */
27 | snowflake: bigint;
28 |
29 | /**
30 | * The ID this Pika was generated from.
31 | */
32 | nodeId: number;
33 |
34 | /**
35 | * A rolling number between 1 to 4096 to introduce entropy.
36 | * Allows for doing 4096 ids per ms per node.
37 | */
38 | seq: number;
39 |
40 | /**
41 | * The version of the pika encoding.
42 | */
43 | version: 1;
44 |
45 | /**
46 | * The definition for this prefix
47 | */
48 | prefixRecord: PikaPrefixDefinition;
49 |
50 | /**
51 | * @deprecated use `.prefixRecord` instead
52 | */
53 | prefix_record: PikaPrefixDefinition
;
54 | }
55 |
56 | export interface PikaInitializationOptions {
57 | epoch?: EpochResolvable;
58 | nodeId?: number;
59 | suppressPrefixWarnings?: boolean;
60 | disableLowercase?: boolean;
61 | }
62 |
63 | export const VALID_PREFIX = /^[a-z0-9_]+$/i;
64 | export const DEFAULT_EPOCH = 1640995200000n; // Jan 1 2022
65 |
66 | export type PrefixInit = V | PikaPrefixDefinition;
67 | export type LowercasePrefixInit = Lowercase extends V ? PrefixInit : PrefixInit>;
68 |
69 | export class InvalidPrefixError extends TypeError {
70 | constructor(prefix: string) {
71 | super(`invalid prefix; prefixes must be alphanumeric (a-z0-9_) and may include underscores; received: ${prefix}`);
72 | }
73 | }
74 |
75 | export class Pika {
76 | public readonly prefixes: Record> = {};
77 | readonly #snowflake: Snowflake;
78 | readonly #suppressPrefixWarnings: boolean;
79 |
80 | /**
81 | * The generated or passed in node ID for this Pika instance
82 | * @internal
83 | */
84 | #nodeId: bigint;
85 |
86 | /**
87 | * @param prefixes a list of PikaPrefixRecords to initialize pika with
88 | * @param opts misc. options to initialize pika with
89 | */
90 | constructor(prefixes: readonly LowercasePrefixInit[], {nodeId, ...opts}: PikaInitializationOptions = {}) {
91 | this.#nodeId = nodeId ? BigInt(nodeId) % 1024n : this.computeNodeId();
92 | this.#snowflake = new Snowflake(opts.epoch || DEFAULT_EPOCH, this.#nodeId);
93 | this.#suppressPrefixWarnings = opts.suppressPrefixWarnings ?? false;
94 |
95 | this.prefixes = prefixes.reduce((prefixes, definition) => {
96 | const prefix = typeof definition === 'string' ? definition : definition.prefix;
97 |
98 | if (!VALID_PREFIX.test(prefix)) {
99 | throw new InvalidPrefixError(prefix);
100 | }
101 |
102 | if (typeof definition === 'string') {
103 | return {
104 | ...prefixes,
105 | [definition]: {prefix},
106 | };
107 | }
108 |
109 | return {
110 | ...prefixes,
111 | [prefix]: definition,
112 | };
113 | }, {});
114 | }
115 |
116 | /**
117 | * Validates something that might be an ID is valid with our prefix set
118 | * @param maybeId the ID to validate
119 | * @param expectPrefix the prefix to expect
120 | * @returns
121 | */
122 | validate(maybeId: string, expectPrefix?: T): maybeId is `${T}_${string}` {
123 | if (typeof maybeId !== 'string') {
124 | return false;
125 | }
126 |
127 | const s = maybeId.split('_');
128 | const tail = s[s.length - 1];
129 | const prefix = s.slice(0, s.length - 1).join('_');
130 |
131 | if (!tail) {
132 | return false;
133 | }
134 |
135 | if (expectPrefix && prefix !== expectPrefix) {
136 | return false;
137 | }
138 |
139 | if (expectPrefix) {
140 | return prefix === expectPrefix;
141 | }
142 |
143 | return prefix in this.prefixes;
144 | }
145 |
146 | gen(prefix: Prefix): `${Prefix}_${string}` {
147 | if (!VALID_PREFIX.test(prefix)) {
148 | throw new InvalidPrefixError(prefix);
149 | }
150 |
151 | if (!this.prefixes[prefix] && !this.#suppressPrefixWarnings) {
152 | warn(
153 | `Unregistered prefix (${prefix}) was used. This can cause unknown behavior - see https://github.com/hopinc/pika/tree/main/impl/js for details.`,
154 | );
155 | }
156 |
157 | const snowflake = this.#snowflake.gen();
158 |
159 | return `${prefix.toLowerCase()}_${Buffer.from(
160 | (this.prefixes[prefix]?.secure ? `s_${randomBytes(16).toString('hex')}_` : '') + snowflake,
161 | ).toString('base64url')}` as `${Prefix}_${string}`;
162 | }
163 |
164 | /**
165 | * Gen a Snowflake, if you really need one
166 | */
167 | public genSnowflake() {
168 | return this.#snowflake.gen();
169 | }
170 |
171 | public decode(id: string): DecodedPika {
172 | try {
173 | const s = id.split('_');
174 | const tail = s[s.length - 1];
175 | const prefix = s.slice(0, s.length - 1).join('_') as Prefixes;
176 |
177 | const decodedTail = Buffer.from(tail, 'base64').toString();
178 | const sf = decodedTail.split('_').pop();
179 |
180 | if (!sf) {
181 | throw Error('attempted to decode invalid pika; tail was corrupt');
182 | }
183 |
184 | const {id: snowflake, ...v} = this.#snowflake.deconstruct(sf);
185 |
186 | return {
187 | prefix,
188 | tail,
189 | prefix_record: this.prefixes[prefix],
190 | prefixRecord: this.prefixes[prefix],
191 | snowflake,
192 | version: 1,
193 | ...v,
194 | };
195 | } catch (e: unknown) {
196 | error('Failed to decode ID', id);
197 | throw e;
198 | }
199 | }
200 |
201 | /**
202 | * Derives this machine's node ID from the MAC address of the first
203 | * public network interface it finds
204 | * @returns The computed node ID (0-1023)
205 | */
206 | private computeNodeId(): bigint {
207 | try {
208 | const interfaces = Object.values(networkInterfaces());
209 | const firstValidInterface = interfaces.filter(iface => iface && iface[0].mac !== '00:00:00:00:00:00')[0];
210 |
211 | if (!firstValidInterface) {
212 | throw new Error('no valid mac address found');
213 | }
214 |
215 | const mac = firstValidInterface[0].mac;
216 |
217 | return BigInt(parseInt(mac.split(':').join(''), 16) % 1024);
218 | } catch (e) {
219 | warn('Failed to compute node ID, falling back to 0. Error:\n', e);
220 | return 0n;
221 | }
222 | }
223 | }
224 |
225 | export type InferPrefixes> = T extends Pika ? P : never;
226 | export type InferIds> = T extends Pika ? `${P}_${string}` : never;
227 |
--------------------------------------------------------------------------------
/impl/js/src/snowflake.ts:
--------------------------------------------------------------------------------
1 | export type EpochResolvable = number | bigint | Date;
2 |
3 | export interface SnowflakeGenOptions {
4 | timestamp?: EpochResolvable;
5 | }
6 |
7 | export interface DeconstructedSnowflake {
8 | id: bigint;
9 | timestamp: bigint;
10 | nodeId: number;
11 | seq: number;
12 | epoch: bigint;
13 | }
14 |
15 | /**
16 | * A class for generating and deconstructing snowflakes.
17 | *
18 | * Pika has put it's own spin on Twitter snowflakes to simplify deployment
19 | * and setup. Instead of having a separate worker and process ID, we have
20 | * one node ID that takes up the 10 bits these fields would usually use.
21 | *
22 | * A node ID is computed by taking the MAC address of the first available
23 | * public interface on the device, then calculating the modulo against
24 | * 1024 (10b)
25 | *
26 | * If we have a snowflake `963584775274749952n` we can represent it as binary:
27 | * ```
28 | * 64 22 12 0
29 | * 000011010101111101011111011110000011001010 0001000101 000000000000
30 | * number of ms since epoch node id sequence
31 | * ```
32 | */
33 | export class Snowflake {
34 | /**
35 | * Snowflakes generated are derived from this epoch
36 | * @internal
37 | */
38 | #epoch: bigint;
39 |
40 | /**
41 | * Passed in node ID for this Snowflake instance
42 | * @internal
43 | */
44 | #nodeId: bigint;
45 |
46 | /**
47 | * Current sequence number (0-4095)
48 | * @internal
49 | */
50 | #seq = 0n;
51 |
52 | /**
53 | * Last timestamp of the last time the sequence was exhausted
54 | * @internal
55 | */
56 | #lastSequenceExhaustion: number = 0;
57 |
58 | /**
59 | * @param epoch the base epoch to use
60 | * @param nodeId optionally pass a static node identifier (0-1023)
61 | */
62 | constructor(epoch: EpochResolvable, nodeId: number | bigint) {
63 | this.#epoch = this.normalizeEpoch(epoch);
64 | this.#nodeId = BigInt(nodeId);
65 | }
66 |
67 | public get nodeId(): number {
68 | return Number(this.nodeId);
69 | }
70 |
71 | public gen({timestamp = Date.now()}: SnowflakeGenOptions = {}): string {
72 | const nTimestamp = this.normalizeEpoch(timestamp);
73 |
74 | if (this.#seq === 4095n && timestamp === this.#lastSequenceExhaustion) {
75 | // purposely blocking
76 | while (Date.now() - timestamp < 1) {
77 | continue;
78 | }
79 | }
80 |
81 | this.#seq = this.#seq >= 4095n ? 0n : this.#seq + 1n;
82 | if (this.#seq === 4095n) this.#lastSequenceExhaustion = Date.now();
83 |
84 | return (
85 | ((nTimestamp - this.#epoch) << 22n) | // millis since epoch
86 | ((this.#nodeId & 0b1111111111n) << 12n) |
87 | this.#seq
88 | ).toString();
89 | }
90 |
91 | public deconstruct(id: string | bigint): DeconstructedSnowflake {
92 | const bigIntId = BigInt(id);
93 |
94 | return {
95 | id: bigIntId,
96 | timestamp: (bigIntId >> 22n) + this.#epoch,
97 | nodeId: Number((bigIntId >> 12n) & 0b1111111111n),
98 | seq: Number(bigIntId & 0b111111111111n),
99 | epoch: this.#epoch,
100 | };
101 | }
102 |
103 | private normalizeEpoch(epoch: EpochResolvable): bigint {
104 | return BigInt(epoch instanceof Date ? epoch.getTime() : epoch);
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/impl/js/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "moduleResolution": "node",
4 | "target": "ES2020",
5 | "lib": ["esnext"],
6 | "module": "CommonJS",
7 | "outDir": "./dist/",
8 | "strict": true,
9 | "pretty": true,
10 | "declaration": true,
11 | "skipLibCheck": true,
12 | "noEmitOnError": false,
13 | "stripInternal": true,
14 | "noUnusedLocals": true,
15 | "isolatedModules": true,
16 | "esModuleInterop": true,
17 | "noImplicitReturns": true,
18 | "downlevelIteration": true,
19 | "noUnusedParameters": true,
20 | "experimentalDecorators": true,
21 | "importsNotUsedAsValues": "error",
22 | "forceConsistentCasingInFileNames": true,
23 | "allowJs": false,
24 | "removeComments": false
25 | },
26 | "exclude": ["node_modules", "dist"]
27 | }
28 |
--------------------------------------------------------------------------------
/impl/js/yarn.lock:
--------------------------------------------------------------------------------
1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
2 | # yarn lockfile v1
3 |
4 |
5 | "@esbuild-kit/cjs-loader@^2.4.0":
6 | version "2.4.0"
7 | resolved "https://registry.yarnpkg.com/@esbuild-kit/cjs-loader/-/cjs-loader-2.4.0.tgz#643c4b2855a18f31cd983794536d4ff64d3b410d"
8 | integrity sha512-DBBCiHPgL2B/elUpvCDhNHXnlZQ9sfO2uyt1OJyAXKT41beQEFY4OxZ6gwS+ZesRCbZ6JV8M7GEyOPkjv8kdIw==
9 | dependencies:
10 | "@esbuild-kit/core-utils" "^3.0.0"
11 | get-tsconfig "^4.2.0"
12 |
13 | "@esbuild-kit/core-utils@^3.0.0":
14 | version "3.0.0"
15 | resolved "https://registry.yarnpkg.com/@esbuild-kit/core-utils/-/core-utils-3.0.0.tgz#e0f8463a32b4a9c9b456a7f9c31a5e038c8d2c19"
16 | integrity sha512-TXmwH9EFS3DC2sI2YJWJBgHGhlteK0Xyu1VabwetMULfm3oYhbrsWV5yaSr2NTWZIgDGVLHbRf0inxbjXqAcmQ==
17 | dependencies:
18 | esbuild "~0.15.10"
19 | source-map-support "^0.5.21"
20 |
21 | "@esbuild-kit/esm-loader@^2.5.0":
22 | version "2.5.0"
23 | resolved "https://registry.yarnpkg.com/@esbuild-kit/esm-loader/-/esm-loader-2.5.0.tgz#af208eb9e0449038e7f35957ec51b7e70135e116"
24 | integrity sha512-ySs0qOsiwj+hsgZM9/MniGdvfa9/WzqfFuIia8/5gSUPeIQIX2/tG91QakxPFOR35VFiwTB7wCiHtiS6dc6SkA==
25 | dependencies:
26 | "@esbuild-kit/core-utils" "^3.0.0"
27 | get-tsconfig "^4.2.0"
28 |
29 | "@esbuild/android-arm@0.15.10":
30 | version "0.15.10"
31 | resolved "https://registry.yarnpkg.com/@esbuild/android-arm/-/android-arm-0.15.10.tgz#a5f9432eb221afc243c321058ef25fe899886892"
32 | integrity sha512-FNONeQPy/ox+5NBkcSbYJxoXj9GWu8gVGJTVmUyoOCKQFDTrHVKgNSzChdNt0I8Aj/iKcsDf2r9BFwv+FSNUXg==
33 |
34 | "@esbuild/linux-loong64@0.15.10":
35 | version "0.15.10"
36 | resolved "https://registry.yarnpkg.com/@esbuild/linux-loong64/-/linux-loong64-0.15.10.tgz#78a42897c2cf8db9fd5f1811f7590393b77774c7"
37 | integrity sha512-w0Ou3Z83LOYEkwaui2M8VwIp+nLi/NA60lBLMvaJ+vXVMcsARYdEzLNE7RSm4+lSg4zq4d7fAVuzk7PNQ5JFgg==
38 |
39 | "@nodelib/fs.scandir@2.1.5":
40 | version "2.1.5"
41 | resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5"
42 | integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==
43 | dependencies:
44 | "@nodelib/fs.stat" "2.0.5"
45 | run-parallel "^1.1.9"
46 |
47 | "@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2":
48 | version "2.0.5"
49 | resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b"
50 | integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==
51 |
52 | "@nodelib/fs.walk@^1.2.3":
53 | version "1.2.8"
54 | resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a"
55 | integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==
56 | dependencies:
57 | "@nodelib/fs.scandir" "2.1.5"
58 | fastq "^1.6.0"
59 |
60 | "@types/node@^18.8.3":
61 | version "18.8.5"
62 | resolved "https://registry.yarnpkg.com/@types/node/-/node-18.8.5.tgz#6a31f820c1077c3f8ce44f9e203e68a176e8f59e"
63 | integrity sha512-Bq7G3AErwe5A/Zki5fdD3O6+0zDChhg671NfPjtIcbtzDNZTv4NPKMRFr7gtYPG7y+B8uTiNK4Ngd9T0FTar6Q==
64 |
65 | any-promise@^1.0.0:
66 | version "1.3.0"
67 | resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f"
68 | integrity sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==
69 |
70 | anymatch@~3.1.2:
71 | version "3.1.2"
72 | resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716"
73 | integrity sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==
74 | dependencies:
75 | normalize-path "^3.0.0"
76 | picomatch "^2.0.4"
77 |
78 | array-union@^2.1.0:
79 | version "2.1.0"
80 | resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d"
81 | integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==
82 |
83 | balanced-match@^1.0.0:
84 | version "1.0.2"
85 | resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
86 | integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
87 |
88 | benchmark@^2.1.4:
89 | version "2.1.4"
90 | resolved "https://registry.yarnpkg.com/benchmark/-/benchmark-2.1.4.tgz#09f3de31c916425d498cc2ee565a0ebf3c2a5629"
91 | integrity sha512-l9MlfN4M1K/H2fbhfMy3B7vJd6AGKJVQn2h6Sg/Yx+KckoUA7ewS5Vv6TjSq18ooE1kS9hhAlQRH3AkXIh/aOQ==
92 | dependencies:
93 | lodash "^4.17.4"
94 | platform "^1.3.3"
95 |
96 | binary-extensions@^2.0.0:
97 | version "2.2.0"
98 | resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d"
99 | integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==
100 |
101 | brace-expansion@^1.1.7:
102 | version "1.1.11"
103 | resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
104 | integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==
105 | dependencies:
106 | balanced-match "^1.0.0"
107 | concat-map "0.0.1"
108 |
109 | braces@^3.0.2, braces@~3.0.2:
110 | version "3.0.2"
111 | resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107"
112 | integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==
113 | dependencies:
114 | fill-range "^7.0.1"
115 |
116 | buffer-from@^1.0.0:
117 | version "1.1.2"
118 | resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5"
119 | integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==
120 |
121 | bundle-require@^3.1.0:
122 | version "3.1.0"
123 | resolved "https://registry.yarnpkg.com/bundle-require/-/bundle-require-3.1.0.tgz#e07256ff02c72cd3a665afa84ce930d111ae4252"
124 | integrity sha512-IIXtAO7fKcwPHNPt9kY/WNVJqy7NDy6YqJvv6ENH0TOZoJ+yjpEsn1w40WKZbR2ibfu5g1rfgJTvmFHpm5aOMA==
125 | dependencies:
126 | load-tsconfig "^0.2.0"
127 |
128 | cac@^6.7.12:
129 | version "6.7.14"
130 | resolved "https://registry.yarnpkg.com/cac/-/cac-6.7.14.tgz#804e1e6f506ee363cb0e3ccbb09cad5dd9870959"
131 | integrity sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==
132 |
133 | chokidar@^3.5.1:
134 | version "3.5.3"
135 | resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd"
136 | integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==
137 | dependencies:
138 | anymatch "~3.1.2"
139 | braces "~3.0.2"
140 | glob-parent "~5.1.2"
141 | is-binary-path "~2.1.0"
142 | is-glob "~4.0.1"
143 | normalize-path "~3.0.0"
144 | readdirp "~3.6.0"
145 | optionalDependencies:
146 | fsevents "~2.3.2"
147 |
148 | commander@^4.0.0:
149 | version "4.1.1"
150 | resolved "https://registry.yarnpkg.com/commander/-/commander-4.1.1.tgz#9fd602bd936294e9e9ef46a3f4d6964044b18068"
151 | integrity sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==
152 |
153 | concat-map@0.0.1:
154 | version "0.0.1"
155 | resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
156 | integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==
157 |
158 | cross-spawn@^7.0.3:
159 | version "7.0.3"
160 | resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6"
161 | integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==
162 | dependencies:
163 | path-key "^3.1.0"
164 | shebang-command "^2.0.0"
165 | which "^2.0.1"
166 |
167 | debug@^4.3.1:
168 | version "4.3.4"
169 | resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865"
170 | integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==
171 | dependencies:
172 | ms "2.1.2"
173 |
174 | dir-glob@^3.0.1:
175 | version "3.0.1"
176 | resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f"
177 | integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==
178 | dependencies:
179 | path-type "^4.0.0"
180 |
181 | esbuild-android-64@0.15.10:
182 | version "0.15.10"
183 | resolved "https://registry.yarnpkg.com/esbuild-android-64/-/esbuild-android-64-0.15.10.tgz#8a59a84acbf2eca96996cadc35642cf055c494f0"
184 | integrity sha512-UI7krF8OYO1N7JYTgLT9ML5j4+45ra3amLZKx7LO3lmLt1Ibn8t3aZbX5Pu4BjWiqDuJ3m/hsvhPhK/5Y/YpnA==
185 |
186 | esbuild-android-arm64@0.15.10:
187 | version "0.15.10"
188 | resolved "https://registry.yarnpkg.com/esbuild-android-arm64/-/esbuild-android-arm64-0.15.10.tgz#f453851dc1d8c5409a38cf7613a33852faf4915d"
189 | integrity sha512-EOt55D6xBk5O05AK8brXUbZmoFj4chM8u3riGflLa6ziEoVvNjRdD7Cnp82NHQGfSHgYR06XsPI8/sMuA/cUwg==
190 |
191 | esbuild-darwin-64@0.15.10:
192 | version "0.15.10"
193 | resolved "https://registry.yarnpkg.com/esbuild-darwin-64/-/esbuild-darwin-64-0.15.10.tgz#778bd29c8186ff47b176c8af58c08cf0fb8e6b86"
194 | integrity sha512-hbDJugTicqIm+WKZgp208d7FcXcaK8j2c0l+fqSJ3d2AzQAfjEYDRM3Z2oMeqSJ9uFxyj/muSACLdix7oTstRA==
195 |
196 | esbuild-darwin-arm64@0.15.10:
197 | version "0.15.10"
198 | resolved "https://registry.yarnpkg.com/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.15.10.tgz#b30bbefb46dc3c5d4708b0435e52f6456578d6df"
199 | integrity sha512-M1t5+Kj4IgSbYmunf2BB6EKLkWUq+XlqaFRiGOk8bmBapu9bCDrxjf4kUnWn59Dka3I27EiuHBKd1rSO4osLFQ==
200 |
201 | esbuild-freebsd-64@0.15.10:
202 | version "0.15.10"
203 | resolved "https://registry.yarnpkg.com/esbuild-freebsd-64/-/esbuild-freebsd-64-0.15.10.tgz#ab301c5f6ded5110dbdd611140bef1a7c2e99236"
204 | integrity sha512-KMBFMa7C8oc97nqDdoZwtDBX7gfpolkk6Bcmj6YFMrtCMVgoU/x2DI1p74DmYl7CSS6Ppa3xgemrLrr5IjIn0w==
205 |
206 | esbuild-freebsd-arm64@0.15.10:
207 | version "0.15.10"
208 | resolved "https://registry.yarnpkg.com/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.15.10.tgz#a5b09b867a6ff49110f52343b6f12265db63d43f"
209 | integrity sha512-m2KNbuCX13yQqLlbSojFMHpewbn8wW5uDS6DxRpmaZKzyq8Dbsku6hHvh2U+BcLwWY4mpgXzFUoENEf7IcioGg==
210 |
211 | esbuild-linux-32@0.15.10:
212 | version "0.15.10"
213 | resolved "https://registry.yarnpkg.com/esbuild-linux-32/-/esbuild-linux-32-0.15.10.tgz#5282fe9915641caf9c8070e4ba2c3e16d358f837"
214 | integrity sha512-guXrwSYFAvNkuQ39FNeV4sNkNms1bLlA5vF1H0cazZBOLdLFIny6BhT+TUbK/hdByMQhtWQ5jI9VAmPKbVPu1w==
215 |
216 | esbuild-linux-64@0.15.10:
217 | version "0.15.10"
218 | resolved "https://registry.yarnpkg.com/esbuild-linux-64/-/esbuild-linux-64-0.15.10.tgz#f3726e85a00149580cb19f8abfabcbb96f5d52bb"
219 | integrity sha512-jd8XfaSJeucMpD63YNMO1JCrdJhckHWcMv6O233bL4l6ogQKQOxBYSRP/XLWP+6kVTu0obXovuckJDcA0DKtQA==
220 |
221 | esbuild-linux-arm64@0.15.10:
222 | version "0.15.10"
223 | resolved "https://registry.yarnpkg.com/esbuild-linux-arm64/-/esbuild-linux-arm64-0.15.10.tgz#2f0056e9d5286edb0185b56655caa8c574d8dbe7"
224 | integrity sha512-GByBi4fgkvZFTHFDYNftu1DQ1GzR23jws0oWyCfhnI7eMOe+wgwWrc78dbNk709Ivdr/evefm2PJiUBMiusS1A==
225 |
226 | esbuild-linux-arm@0.15.10:
227 | version "0.15.10"
228 | resolved "https://registry.yarnpkg.com/esbuild-linux-arm/-/esbuild-linux-arm-0.15.10.tgz#40a9270da3c8ffa32cf72e24a79883e323dff08d"
229 | integrity sha512-6N8vThLL/Lysy9y4Ex8XoLQAlbZKUyExCWyayGi2KgTBelKpPgj6RZnUaKri0dHNPGgReJriKVU6+KDGQwn10A==
230 |
231 | esbuild-linux-mips64le@0.15.10:
232 | version "0.15.10"
233 | resolved "https://registry.yarnpkg.com/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.15.10.tgz#90ce1c4ee0202edb4ac69807dea77f7e5804abc4"
234 | integrity sha512-BxP+LbaGVGIdQNJUNF7qpYjEGWb0YyHVSKqYKrn+pTwH/SiHUxFyJYSP3pqkku61olQiSBnSmWZ+YUpj78Tw7Q==
235 |
236 | esbuild-linux-ppc64le@0.15.10:
237 | version "0.15.10"
238 | resolved "https://registry.yarnpkg.com/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.15.10.tgz#782837ae7bd5b279178106c9dd801755a21fabdf"
239 | integrity sha512-LoSQCd6498PmninNgqd/BR7z3Bsk/mabImBWuQ4wQgmQEeanzWd5BQU2aNi9mBURCLgyheuZS6Xhrw5luw3OkQ==
240 |
241 | esbuild-linux-riscv64@0.15.10:
242 | version "0.15.10"
243 | resolved "https://registry.yarnpkg.com/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.15.10.tgz#d7420d806ece5174f24f4634303146f915ab4207"
244 | integrity sha512-Lrl9Cr2YROvPV4wmZ1/g48httE8z/5SCiXIyebiB5N8VT7pX3t6meI7TQVHw/wQpqP/AF4SksDuFImPTM7Z32Q==
245 |
246 | esbuild-linux-s390x@0.15.10:
247 | version "0.15.10"
248 | resolved "https://registry.yarnpkg.com/esbuild-linux-s390x/-/esbuild-linux-s390x-0.15.10.tgz#21fdf0cb3494a7fb520a71934e4dffce67fe47be"
249 | integrity sha512-ReP+6q3eLVVP2lpRrvl5EodKX7EZ1bS1/z5j6hsluAlZP5aHhk6ghT6Cq3IANvvDdscMMCB4QEbI+AjtvoOFpA==
250 |
251 | esbuild-netbsd-64@0.15.10:
252 | version "0.15.10"
253 | resolved "https://registry.yarnpkg.com/esbuild-netbsd-64/-/esbuild-netbsd-64-0.15.10.tgz#6c06b3107e3df53de381e6299184d4597db0440f"
254 | integrity sha512-iGDYtJCMCqldMskQ4eIV+QSS/CuT7xyy9i2/FjpKvxAuCzrESZXiA1L64YNj6/afuzfBe9i8m/uDkFHy257hTw==
255 |
256 | esbuild-openbsd-64@0.15.10:
257 | version "0.15.10"
258 | resolved "https://registry.yarnpkg.com/esbuild-openbsd-64/-/esbuild-openbsd-64-0.15.10.tgz#4daef5f5d8e74bbda53b65160029445d582570cf"
259 | integrity sha512-ftMMIwHWrnrYnvuJQRJs/Smlcb28F9ICGde/P3FUTCgDDM0N7WA0o9uOR38f5Xe2/OhNCgkjNeb7QeaE3cyWkQ==
260 |
261 | esbuild-sunos-64@0.15.10:
262 | version "0.15.10"
263 | resolved "https://registry.yarnpkg.com/esbuild-sunos-64/-/esbuild-sunos-64-0.15.10.tgz#5fe7bef267a02f322fd249a8214d0274937388a7"
264 | integrity sha512-mf7hBL9Uo2gcy2r3rUFMjVpTaGpFJJE5QTDDqUFf1632FxteYANffDZmKbqX0PfeQ2XjUDE604IcE7OJeoHiyg==
265 |
266 | esbuild-windows-32@0.15.10:
267 | version "0.15.10"
268 | resolved "https://registry.yarnpkg.com/esbuild-windows-32/-/esbuild-windows-32-0.15.10.tgz#48e3dde25ab0135579a288b30ab6ddef6d1f0b28"
269 | integrity sha512-ttFVo+Cg8b5+qHmZHbEc8Vl17kCleHhLzgT8X04y8zudEApo0PxPg9Mz8Z2cKH1bCYlve1XL8LkyXGFjtUYeGg==
270 |
271 | esbuild-windows-64@0.15.10:
272 | version "0.15.10"
273 | resolved "https://registry.yarnpkg.com/esbuild-windows-64/-/esbuild-windows-64-0.15.10.tgz#387a9515bef3fee502d277a5d0a2db49a4ecda05"
274 | integrity sha512-2H0gdsyHi5x+8lbng3hLbxDWR7mKHWh5BXZGKVG830KUmXOOWFE2YKJ4tHRkejRduOGDrBvHBriYsGtmTv3ntA==
275 |
276 | esbuild-windows-arm64@0.15.10:
277 | version "0.15.10"
278 | resolved "https://registry.yarnpkg.com/esbuild-windows-arm64/-/esbuild-windows-arm64-0.15.10.tgz#5a6fcf2fa49e895949bf5495cf088ab1b43ae879"
279 | integrity sha512-S+th4F+F8VLsHLR0zrUcG+Et4hx0RKgK1eyHc08kztmLOES8BWwMiaGdoW9hiXuzznXQ0I/Fg904MNbr11Nktw==
280 |
281 | esbuild@^0.15.1, esbuild@~0.15.10:
282 | version "0.15.10"
283 | resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.15.10.tgz#85c2f8446e9b1fe04fae68daceacba033eedbd42"
284 | integrity sha512-N7wBhfJ/E5fzn/SpNgX+oW2RLRjwaL8Y0ezqNqhjD6w0H2p0rDuEz2FKZqpqLnO8DCaWumKe8dsC/ljvVSSxng==
285 | optionalDependencies:
286 | "@esbuild/android-arm" "0.15.10"
287 | "@esbuild/linux-loong64" "0.15.10"
288 | esbuild-android-64 "0.15.10"
289 | esbuild-android-arm64 "0.15.10"
290 | esbuild-darwin-64 "0.15.10"
291 | esbuild-darwin-arm64 "0.15.10"
292 | esbuild-freebsd-64 "0.15.10"
293 | esbuild-freebsd-arm64 "0.15.10"
294 | esbuild-linux-32 "0.15.10"
295 | esbuild-linux-64 "0.15.10"
296 | esbuild-linux-arm "0.15.10"
297 | esbuild-linux-arm64 "0.15.10"
298 | esbuild-linux-mips64le "0.15.10"
299 | esbuild-linux-ppc64le "0.15.10"
300 | esbuild-linux-riscv64 "0.15.10"
301 | esbuild-linux-s390x "0.15.10"
302 | esbuild-netbsd-64 "0.15.10"
303 | esbuild-openbsd-64 "0.15.10"
304 | esbuild-sunos-64 "0.15.10"
305 | esbuild-windows-32 "0.15.10"
306 | esbuild-windows-64 "0.15.10"
307 | esbuild-windows-arm64 "0.15.10"
308 |
309 | execa@^5.0.0:
310 | version "5.1.1"
311 | resolved "https://registry.yarnpkg.com/execa/-/execa-5.1.1.tgz#f80ad9cbf4298f7bd1d4c9555c21e93741c411dd"
312 | integrity sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==
313 | dependencies:
314 | cross-spawn "^7.0.3"
315 | get-stream "^6.0.0"
316 | human-signals "^2.1.0"
317 | is-stream "^2.0.0"
318 | merge-stream "^2.0.0"
319 | npm-run-path "^4.0.1"
320 | onetime "^5.1.2"
321 | signal-exit "^3.0.3"
322 | strip-final-newline "^2.0.0"
323 |
324 | fast-glob@^3.2.9:
325 | version "3.2.12"
326 | resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.12.tgz#7f39ec99c2e6ab030337142da9e0c18f37afae80"
327 | integrity sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==
328 | dependencies:
329 | "@nodelib/fs.stat" "^2.0.2"
330 | "@nodelib/fs.walk" "^1.2.3"
331 | glob-parent "^5.1.2"
332 | merge2 "^1.3.0"
333 | micromatch "^4.0.4"
334 |
335 | fastq@^1.6.0:
336 | version "1.13.0"
337 | resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.13.0.tgz#616760f88a7526bdfc596b7cab8c18938c36b98c"
338 | integrity sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==
339 | dependencies:
340 | reusify "^1.0.4"
341 |
342 | fill-range@^7.0.1:
343 | version "7.0.1"
344 | resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40"
345 | integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==
346 | dependencies:
347 | to-regex-range "^5.0.1"
348 |
349 | fs.realpath@^1.0.0:
350 | version "1.0.0"
351 | resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
352 | integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==
353 |
354 | fsevents@~2.3.2:
355 | version "2.3.2"
356 | resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a"
357 | integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==
358 |
359 | get-stream@^6.0.0:
360 | version "6.0.1"
361 | resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.1.tgz#a262d8eef67aced57c2852ad6167526a43cbf7b7"
362 | integrity sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==
363 |
364 | get-tsconfig@^4.2.0:
365 | version "4.2.0"
366 | resolved "https://registry.yarnpkg.com/get-tsconfig/-/get-tsconfig-4.2.0.tgz#ff368dd7104dab47bf923404eb93838245c66543"
367 | integrity sha512-X8u8fREiYOE6S8hLbq99PeykTDoLVnxvF4DjWKJmz9xy2nNRdUcV8ZN9tniJFeKyTU3qnC9lL8n4Chd6LmVKHg==
368 |
369 | glob-parent@^5.1.2, glob-parent@~5.1.2:
370 | version "5.1.2"
371 | resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4"
372 | integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==
373 | dependencies:
374 | is-glob "^4.0.1"
375 |
376 | glob@7.1.6:
377 | version "7.1.6"
378 | resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6"
379 | integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==
380 | dependencies:
381 | fs.realpath "^1.0.0"
382 | inflight "^1.0.4"
383 | inherits "2"
384 | minimatch "^3.0.4"
385 | once "^1.3.0"
386 | path-is-absolute "^1.0.0"
387 |
388 | globby@^11.0.3:
389 | version "11.1.0"
390 | resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b"
391 | integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==
392 | dependencies:
393 | array-union "^2.1.0"
394 | dir-glob "^3.0.1"
395 | fast-glob "^3.2.9"
396 | ignore "^5.2.0"
397 | merge2 "^1.4.1"
398 | slash "^3.0.0"
399 |
400 | human-signals@^2.1.0:
401 | version "2.1.0"
402 | resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0"
403 | integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==
404 |
405 | ignore@^5.2.0:
406 | version "5.2.0"
407 | resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.0.tgz#6d3bac8fa7fe0d45d9f9be7bac2fc279577e345a"
408 | integrity sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==
409 |
410 | inflight@^1.0.4:
411 | version "1.0.6"
412 | resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9"
413 | integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==
414 | dependencies:
415 | once "^1.3.0"
416 | wrappy "1"
417 |
418 | inherits@2:
419 | version "2.0.4"
420 | resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
421 | integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
422 |
423 | is-binary-path@~2.1.0:
424 | version "2.1.0"
425 | resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09"
426 | integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==
427 | dependencies:
428 | binary-extensions "^2.0.0"
429 |
430 | is-extglob@^2.1.1:
431 | version "2.1.1"
432 | resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2"
433 | integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==
434 |
435 | is-glob@^4.0.1, is-glob@~4.0.1:
436 | version "4.0.3"
437 | resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084"
438 | integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==
439 | dependencies:
440 | is-extglob "^2.1.1"
441 |
442 | is-number@^7.0.0:
443 | version "7.0.0"
444 | resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b"
445 | integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==
446 |
447 | is-stream@^2.0.0:
448 | version "2.0.1"
449 | resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077"
450 | integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==
451 |
452 | isexe@^2.0.0:
453 | version "2.0.0"
454 | resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
455 | integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==
456 |
457 | joycon@^3.0.1:
458 | version "3.1.1"
459 | resolved "https://registry.yarnpkg.com/joycon/-/joycon-3.1.1.tgz#bce8596d6ae808f8b68168f5fc69280996894f03"
460 | integrity sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==
461 |
462 | lilconfig@^2.0.5:
463 | version "2.0.6"
464 | resolved "https://registry.yarnpkg.com/lilconfig/-/lilconfig-2.0.6.tgz#32a384558bd58af3d4c6e077dd1ad1d397bc69d4"
465 | integrity sha512-9JROoBW7pobfsx+Sq2JsASvCo6Pfo6WWoUW79HuB1BCoBXD4PLWJPqDF6fNj67pqBYTbAHkE57M1kS/+L1neOg==
466 |
467 | lines-and-columns@^1.1.6:
468 | version "1.2.4"
469 | resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632"
470 | integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==
471 |
472 | load-tsconfig@^0.2.0:
473 | version "0.2.3"
474 | resolved "https://registry.yarnpkg.com/load-tsconfig/-/load-tsconfig-0.2.3.tgz#08af3e7744943caab0c75f8af7f1703639c3ef1f"
475 | integrity sha512-iyT2MXws+dc2Wi6o3grCFtGXpeMvHmJqS27sMPGtV2eUu4PeFnG+33I8BlFK1t1NWMjOpcx9bridn5yxLDX2gQ==
476 |
477 | lodash.sortby@^4.7.0:
478 | version "4.7.0"
479 | resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438"
480 | integrity sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==
481 |
482 | lodash@^4.17.4:
483 | version "4.17.21"
484 | resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
485 | integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
486 |
487 | merge-stream@^2.0.0:
488 | version "2.0.0"
489 | resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60"
490 | integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==
491 |
492 | merge2@^1.3.0, merge2@^1.4.1:
493 | version "1.4.1"
494 | resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae"
495 | integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==
496 |
497 | micromatch@^4.0.4:
498 | version "4.0.5"
499 | resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6"
500 | integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==
501 | dependencies:
502 | braces "^3.0.2"
503 | picomatch "^2.3.1"
504 |
505 | mimic-fn@^2.1.0:
506 | version "2.1.0"
507 | resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b"
508 | integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==
509 |
510 | minimatch@^3.0.4:
511 | version "3.1.2"
512 | resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b"
513 | integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==
514 | dependencies:
515 | brace-expansion "^1.1.7"
516 |
517 | ms@2.1.2:
518 | version "2.1.2"
519 | resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
520 | integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
521 |
522 | mz@^2.7.0:
523 | version "2.7.0"
524 | resolved "https://registry.yarnpkg.com/mz/-/mz-2.7.0.tgz#95008057a56cafadc2bc63dde7f9ff6955948e32"
525 | integrity sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==
526 | dependencies:
527 | any-promise "^1.0.0"
528 | object-assign "^4.0.1"
529 | thenify-all "^1.0.0"
530 |
531 | normalize-path@^3.0.0, normalize-path@~3.0.0:
532 | version "3.0.0"
533 | resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65"
534 | integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==
535 |
536 | npm-run-path@^4.0.1:
537 | version "4.0.1"
538 | resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea"
539 | integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==
540 | dependencies:
541 | path-key "^3.0.0"
542 |
543 | object-assign@^4.0.1:
544 | version "4.1.1"
545 | resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
546 | integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==
547 |
548 | once@^1.3.0:
549 | version "1.4.0"
550 | resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
551 | integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==
552 | dependencies:
553 | wrappy "1"
554 |
555 | onetime@^5.1.2:
556 | version "5.1.2"
557 | resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e"
558 | integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==
559 | dependencies:
560 | mimic-fn "^2.1.0"
561 |
562 | path-is-absolute@^1.0.0:
563 | version "1.0.1"
564 | resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
565 | integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==
566 |
567 | path-key@^3.0.0, path-key@^3.1.0:
568 | version "3.1.1"
569 | resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375"
570 | integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==
571 |
572 | path-type@^4.0.0:
573 | version "4.0.0"
574 | resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b"
575 | integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==
576 |
577 | picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.3.1:
578 | version "2.3.1"
579 | resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42"
580 | integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==
581 |
582 | pirates@^4.0.1:
583 | version "4.0.5"
584 | resolved "https://registry.yarnpkg.com/pirates/-/pirates-4.0.5.tgz#feec352ea5c3268fb23a37c702ab1699f35a5f3b"
585 | integrity sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==
586 |
587 | platform@^1.3.3:
588 | version "1.3.6"
589 | resolved "https://registry.yarnpkg.com/platform/-/platform-1.3.6.tgz#48b4ce983164b209c2d45a107adb31f473a6e7a7"
590 | integrity sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg==
591 |
592 | postcss-load-config@^3.0.1:
593 | version "3.1.4"
594 | resolved "https://registry.yarnpkg.com/postcss-load-config/-/postcss-load-config-3.1.4.tgz#1ab2571faf84bb078877e1d07905eabe9ebda855"
595 | integrity sha512-6DiM4E7v4coTE4uzA8U//WhtPwyhiim3eyjEMFCnUpzbrkK9wJHgKDT2mR+HbtSrd/NubVaYTOpSpjUl8NQeRg==
596 | dependencies:
597 | lilconfig "^2.0.5"
598 | yaml "^1.10.2"
599 |
600 | prettier@^2.7.1:
601 | version "2.7.1"
602 | resolved "https://registry.yarnpkg.com/prettier/-/prettier-2.7.1.tgz#e235806850d057f97bb08368a4f7d899f7760c64"
603 | integrity sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g==
604 |
605 | punycode@^2.1.0:
606 | version "2.1.1"
607 | resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec"
608 | integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==
609 |
610 | queue-microtask@^1.2.2:
611 | version "1.2.3"
612 | resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243"
613 | integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==
614 |
615 | readdirp@~3.6.0:
616 | version "3.6.0"
617 | resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7"
618 | integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==
619 | dependencies:
620 | picomatch "^2.2.1"
621 |
622 | resolve-from@^5.0.0:
623 | version "5.0.0"
624 | resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69"
625 | integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==
626 |
627 | reusify@^1.0.4:
628 | version "1.0.4"
629 | resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76"
630 | integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==
631 |
632 | rollup@^2.74.1:
633 | version "2.79.1"
634 | resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.79.1.tgz#bedee8faef7c9f93a2647ac0108748f497f081c7"
635 | integrity sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw==
636 | optionalDependencies:
637 | fsevents "~2.3.2"
638 |
639 | run-parallel@^1.1.9:
640 | version "1.2.0"
641 | resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee"
642 | integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==
643 | dependencies:
644 | queue-microtask "^1.2.2"
645 |
646 | shebang-command@^2.0.0:
647 | version "2.0.0"
648 | resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea"
649 | integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==
650 | dependencies:
651 | shebang-regex "^3.0.0"
652 |
653 | shebang-regex@^3.0.0:
654 | version "3.0.0"
655 | resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172"
656 | integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==
657 |
658 | signal-exit@^3.0.3:
659 | version "3.0.7"
660 | resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9"
661 | integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==
662 |
663 | slash@^3.0.0:
664 | version "3.0.0"
665 | resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634"
666 | integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==
667 |
668 | source-map-support@^0.5.21:
669 | version "0.5.21"
670 | resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f"
671 | integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==
672 | dependencies:
673 | buffer-from "^1.0.0"
674 | source-map "^0.6.0"
675 |
676 | source-map@0.8.0-beta.0:
677 | version "0.8.0-beta.0"
678 | resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.8.0-beta.0.tgz#d4c1bb42c3f7ee925f005927ba10709e0d1d1f11"
679 | integrity sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==
680 | dependencies:
681 | whatwg-url "^7.0.0"
682 |
683 | source-map@^0.6.0:
684 | version "0.6.1"
685 | resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
686 | integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
687 |
688 | strip-final-newline@^2.0.0:
689 | version "2.0.0"
690 | resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad"
691 | integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==
692 |
693 | sucrase@^3.20.3:
694 | version "3.28.0"
695 | resolved "https://registry.yarnpkg.com/sucrase/-/sucrase-3.28.0.tgz#7fd8b3118d2155fcdf291088ab77fa6eefd63c4c"
696 | integrity sha512-TK9600YInjuiIhVM3729rH4ZKPOsGeyXUwY+Ugu9eilNbdTFyHr6XcAGYbRVZPDgWj6tgI7bx95aaJjHnbffag==
697 | dependencies:
698 | commander "^4.0.0"
699 | glob "7.1.6"
700 | lines-and-columns "^1.1.6"
701 | mz "^2.7.0"
702 | pirates "^4.0.1"
703 | ts-interface-checker "^0.1.9"
704 |
705 | thenify-all@^1.0.0:
706 | version "1.6.0"
707 | resolved "https://registry.yarnpkg.com/thenify-all/-/thenify-all-1.6.0.tgz#1a1918d402d8fc3f98fbf234db0bcc8cc10e9726"
708 | integrity sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==
709 | dependencies:
710 | thenify ">= 3.1.0 < 4"
711 |
712 | "thenify@>= 3.1.0 < 4":
713 | version "3.3.1"
714 | resolved "https://registry.yarnpkg.com/thenify/-/thenify-3.3.1.tgz#8932e686a4066038a016dd9e2ca46add9838a95f"
715 | integrity sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==
716 | dependencies:
717 | any-promise "^1.0.0"
718 |
719 | to-regex-range@^5.0.1:
720 | version "5.0.1"
721 | resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4"
722 | integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==
723 | dependencies:
724 | is-number "^7.0.0"
725 |
726 | tr46@^1.0.1:
727 | version "1.0.1"
728 | resolved "https://registry.yarnpkg.com/tr46/-/tr46-1.0.1.tgz#a8b13fd6bfd2489519674ccde55ba3693b706d09"
729 | integrity sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==
730 | dependencies:
731 | punycode "^2.1.0"
732 |
733 | tree-kill@^1.2.2:
734 | version "1.2.2"
735 | resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.2.2.tgz#4ca09a9092c88b73a7cdc5e8a01b507b0790a0cc"
736 | integrity sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==
737 |
738 | ts-interface-checker@^0.1.9:
739 | version "0.1.13"
740 | resolved "https://registry.yarnpkg.com/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz#784fd3d679722bc103b1b4b8030bcddb5db2a699"
741 | integrity sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==
742 |
743 | tsup@^6.2.3:
744 | version "6.2.3"
745 | resolved "https://registry.yarnpkg.com/tsup/-/tsup-6.2.3.tgz#87f57b2e53d49f1c1ab89aba21fed96aaab0ec9f"
746 | integrity sha512-J5Pu2Dx0E1wlpIEsVFv9ryzP1pZ1OYsJ2cBHZ7GrKteytNdzaSz5hmLX7/nAxtypq+jVkVvA79d7S83ETgHQ5w==
747 | dependencies:
748 | bundle-require "^3.1.0"
749 | cac "^6.7.12"
750 | chokidar "^3.5.1"
751 | debug "^4.3.1"
752 | esbuild "^0.15.1"
753 | execa "^5.0.0"
754 | globby "^11.0.3"
755 | joycon "^3.0.1"
756 | postcss-load-config "^3.0.1"
757 | resolve-from "^5.0.0"
758 | rollup "^2.74.1"
759 | source-map "0.8.0-beta.0"
760 | sucrase "^3.20.3"
761 | tree-kill "^1.2.2"
762 |
763 | tsx@^3.10.1:
764 | version "3.10.1"
765 | resolved "https://registry.yarnpkg.com/tsx/-/tsx-3.10.1.tgz#4ffb1229077f648bbf46c0ed2b098f4cd8dd6d6d"
766 | integrity sha512-Gh6xoW4xrdnLs6hYZydVHIQtrgmbZ/DbnJoLsYoI8MxhKAIyu8R7RyF0D5qg9UKi74Nmr4iSlijdz7Q43IGLyQ==
767 | dependencies:
768 | "@esbuild-kit/cjs-loader" "^2.4.0"
769 | "@esbuild-kit/core-utils" "^3.0.0"
770 | "@esbuild-kit/esm-loader" "^2.5.0"
771 | optionalDependencies:
772 | fsevents "~2.3.2"
773 |
774 | typescript@^4.8.4:
775 | version "4.8.4"
776 | resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.8.4.tgz#c464abca159669597be5f96b8943500b238e60e6"
777 | integrity sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ==
778 |
779 | webidl-conversions@^4.0.2:
780 | version "4.0.2"
781 | resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad"
782 | integrity sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==
783 |
784 | whatwg-url@^7.0.0:
785 | version "7.1.0"
786 | resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-7.1.0.tgz#c2c492f1eca612988efd3d2266be1b9fc6170d06"
787 | integrity sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==
788 | dependencies:
789 | lodash.sortby "^4.7.0"
790 | tr46 "^1.0.1"
791 | webidl-conversions "^4.0.2"
792 |
793 | which@^2.0.1:
794 | version "2.0.2"
795 | resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1"
796 | integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==
797 | dependencies:
798 | isexe "^2.0.0"
799 |
800 | wrappy@1:
801 | version "1.0.2"
802 | resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
803 | integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==
804 |
805 | yaml@^1.10.2:
806 | version "1.10.2"
807 | resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b"
808 | integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==
809 |
--------------------------------------------------------------------------------
/impl/rs/.format.toml:
--------------------------------------------------------------------------------
1 | format_code_in_doc_comments = true
2 | hex_literal_case = "Upper"
3 | imports_granularity = "Module"
4 | newline_style = "Unix"
5 | normalize_comments = true
6 | normalize_doc_attributes = true
7 | reorder_impl_items = true
8 | group_imports = "StdExternalCrate"
9 | use_field_init_shorthand = true
10 | use_try_shorthand = true
11 | wrap_comments = true
12 |
--------------------------------------------------------------------------------
/impl/rs/.gitignore:
--------------------------------------------------------------------------------
1 | target/
--------------------------------------------------------------------------------
/impl/rs/Cargo.lock:
--------------------------------------------------------------------------------
1 | # This file is automatically @generated by Cargo.
2 | # It is not intended for manual editing.
3 | version = 3
4 |
5 | [[package]]
6 | name = "autocfg"
7 | version = "1.1.0"
8 | source = "registry+https://github.com/rust-lang/crates.io-index"
9 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
10 |
11 | [[package]]
12 | name = "bitflags"
13 | version = "1.3.2"
14 | source = "registry+https://github.com/rust-lang/crates.io-index"
15 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
16 |
17 | [[package]]
18 | name = "cc"
19 | version = "1.0.73"
20 | source = "registry+https://github.com/rust-lang/crates.io-index"
21 | checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11"
22 |
23 | [[package]]
24 | name = "cfg-if"
25 | version = "1.0.0"
26 | source = "registry+https://github.com/rust-lang/crates.io-index"
27 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
28 |
29 | [[package]]
30 | name = "getrandom"
31 | version = "0.2.7"
32 | source = "registry+https://github.com/rust-lang/crates.io-index"
33 | checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6"
34 | dependencies = [
35 | "cfg-if",
36 | "libc",
37 | "wasi",
38 | ]
39 |
40 | [[package]]
41 | name = "hex"
42 | version = "0.4.3"
43 | source = "registry+https://github.com/rust-lang/crates.io-index"
44 | checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
45 |
46 | [[package]]
47 | name = "libc"
48 | version = "0.2.127"
49 | source = "registry+https://github.com/rust-lang/crates.io-index"
50 | checksum = "505e71a4706fa491e9b1b55f51b95d4037d0821ee40131190475f692b35b009b"
51 |
52 | [[package]]
53 | name = "mac_address"
54 | version = "1.1.3"
55 | source = "registry+https://github.com/rust-lang/crates.io-index"
56 | checksum = "df1d1bc1084549d60725ccc53a2bfa07f67fe4689fda07b05a36531f2988104a"
57 | dependencies = [
58 | "nix",
59 | "winapi",
60 | ]
61 |
62 | [[package]]
63 | name = "memoffset"
64 | version = "0.6.5"
65 | source = "registry+https://github.com/rust-lang/crates.io-index"
66 | checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce"
67 | dependencies = [
68 | "autocfg",
69 | ]
70 |
71 | [[package]]
72 | name = "nix"
73 | version = "0.23.1"
74 | source = "registry+https://github.com/rust-lang/crates.io-index"
75 | checksum = "9f866317acbd3a240710c63f065ffb1e4fd466259045ccb504130b7f668f35c6"
76 | dependencies = [
77 | "bitflags",
78 | "cc",
79 | "cfg-if",
80 | "libc",
81 | "memoffset",
82 | ]
83 |
84 | [[package]]
85 | name = "pika"
86 | version = "0.1.3"
87 | dependencies = [
88 | "hex",
89 | "mac_address",
90 | "rand",
91 | ]
92 |
93 | [[package]]
94 | name = "ppv-lite86"
95 | version = "0.2.16"
96 | source = "registry+https://github.com/rust-lang/crates.io-index"
97 | checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872"
98 |
99 | [[package]]
100 | name = "rand"
101 | version = "0.8.5"
102 | source = "registry+https://github.com/rust-lang/crates.io-index"
103 | checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
104 | dependencies = [
105 | "libc",
106 | "rand_chacha",
107 | "rand_core",
108 | ]
109 |
110 | [[package]]
111 | name = "rand_chacha"
112 | version = "0.3.1"
113 | source = "registry+https://github.com/rust-lang/crates.io-index"
114 | checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
115 | dependencies = [
116 | "ppv-lite86",
117 | "rand_core",
118 | ]
119 |
120 | [[package]]
121 | name = "rand_core"
122 | version = "0.6.3"
123 | source = "registry+https://github.com/rust-lang/crates.io-index"
124 | checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7"
125 | dependencies = [
126 | "getrandom",
127 | ]
128 |
129 | [[package]]
130 | name = "wasi"
131 | version = "0.11.0+wasi-snapshot-preview1"
132 | source = "registry+https://github.com/rust-lang/crates.io-index"
133 | checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
134 |
135 | [[package]]
136 | name = "winapi"
137 | version = "0.3.9"
138 | source = "registry+https://github.com/rust-lang/crates.io-index"
139 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
140 | dependencies = [
141 | "winapi-i686-pc-windows-gnu",
142 | "winapi-x86_64-pc-windows-gnu",
143 | ]
144 |
145 | [[package]]
146 | name = "winapi-i686-pc-windows-gnu"
147 | version = "0.4.0"
148 | source = "registry+https://github.com/rust-lang/crates.io-index"
149 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
150 |
151 | [[package]]
152 | name = "winapi-x86_64-pc-windows-gnu"
153 | version = "0.4.0"
154 | source = "registry+https://github.com/rust-lang/crates.io-index"
155 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
156 |
--------------------------------------------------------------------------------
/impl/rs/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "pika"
3 | version = "0.1.3"
4 | edition = "2021"
5 | readme="./README.md"
6 | description="Pika ID implementation in Rust"
7 | license = "MPL-2.0"
8 |
9 | [dependencies]
10 | hex = "0.4.3"
11 | mac_address = "1.1.3"
12 | rand = "0.8.5"
13 |
--------------------------------------------------------------------------------
/impl/rs/README.md:
--------------------------------------------------------------------------------
1 | # pika rs
2 |
3 | Rust implementation for Pika
4 |
5 | ## Install
6 |
7 | ```toml
8 | [dependencies]
9 | pika = "0.1"
10 | ```
11 |
12 | ## Basic Usage
13 |
14 | ```rs
15 | let prefixes = [
16 | PrefixRecord {
17 | prefix: "user".to_string(),
18 | description: Some("User ID".to_string()),
19 | secure: false,
20 | },
21 | PrefixRecord {
22 | prefix: "sk".to_string(),
23 | description: Some("Secret Key".to_string()),
24 | secure: true,
25 | }
26 | ];
27 |
28 | let mut pika = Pika::new(
29 | prefixes.to_vec(),
30 | InitOptions {
31 | epoch: Some(1_650_153_600_000),
32 | node_id: None,
33 | disable_lowercase: Some(true),
34 | },
35 | );
36 |
37 | pika.gen("user").unwrap();
38 | // => user_Mzc5ODk1NTI4NzgxMTY4NjQ
39 |
40 | pika.gen("sk").unwrap()
41 | // => sk_c19iMGI0NTM4ZjU3ZThjYTIyZThjNjNlMTgwOTg5MWMyM18zODA2NTE5MjcwNDc5NDYyNA
42 | ```
43 |
44 | ## Node IDs
45 |
46 | By default, Node IDs are calculated by finding the MAC address of the first public network interface device, then calculating the modulo against 1024.
47 |
48 | This works well for smaller systems, but if you have a lot of nodes generating Snowflakes, then collision is possible. In this case, you should create an internal singleton service which keeps a rolling count of the assigned node IDs - from 1 to 1023. Then, services that generate Pikas should call this service to get assigned a node ID.
49 |
50 | You can then pass in the node ID when initializing Pika like this:
51 |
52 | ```rs
53 | let mut pika = Pika::new(
54 | prefixes.to_vec(),
55 | InitOptions {
56 | epoch: Some(1_650_153_600_000),
57 | node_id: custom_node_id,
58 | disable_lowercase: Some(true),
59 | },
60 | );
61 | ```
62 |
--------------------------------------------------------------------------------
/impl/rs/src/base64.rs:
--------------------------------------------------------------------------------
1 | const BASE64_CHARS: [u8; 64] = [
2 | b'A', b'B', b'C', b'D', b'E', b'F', b'G', b'H', b'I', b'J', b'K', b'L', b'M', b'N', b'O', b'P',
3 | b'Q', b'R', b'S', b'T', b'U', b'V', b'W', b'X', b'Y', b'Z', b'a', b'b', b'c', b'd', b'e', b'f',
4 | b'g', b'h', b'i', b'j', b'k', b'l', b'm', b'n', b'o', b'p', b'q', b'r', b's', b't', b'u', b'v',
5 | b'w', b'x', b'y', b'z', b'0', b'1', b'2', b'3', b'4', b'5', b'6', b'7', b'8', b'9', b'+', b'/'
6 | ];
7 |
8 | // Base64 encode a slice of bytes
9 | pub fn base64_encode(input: String) -> String {
10 | let mut result = String::new();
11 | let mut buffer: u32 = 0;
12 | let mut bits_left = 0;
13 |
14 | for byte in input.into_bytes() {
15 | buffer = (buffer << 8) | u32::from(byte);
16 | bits_left += 8;
17 |
18 | while bits_left >= 6 {
19 | bits_left -= 6;
20 | let index = ((buffer >> bits_left) & 0b11_1111) as usize;
21 | result.push(BASE64_CHARS[index] as char);
22 | }
23 | }
24 |
25 | if bits_left > 0 {
26 | buffer <<= 6 - bits_left;
27 | let index = (buffer & 0b11_1111) as usize;
28 | result.push(BASE64_CHARS[index] as char);
29 | }
30 |
31 | while result.len() % 4 != 0 {
32 | result.push('=');
33 | }
34 |
35 | result
36 | }
37 |
38 | // Base64 decode a string into a vector of bytes
39 | #[allow(clippy::cast_possible_truncation)]
40 | pub fn base64_decode(encoded: &str) -> Option> {
41 | let mut decoded = Vec::new();
42 | let mut padding = 0;
43 | let mut buffer = 0;
44 | let mut bits = 0;
45 |
46 | for c in encoded.chars() {
47 | let value = match BASE64_CHARS.iter().position(|&x| x == c as u8) {
48 | Some(v) => v as u32,
49 | None if c == '=' => {
50 | padding += 1;
51 | continue;
52 | }
53 | None => return None,
54 | };
55 |
56 | buffer = (buffer << 6) | value;
57 | bits += 6;
58 |
59 | if bits >= 8 {
60 | bits -= 8;
61 | decoded.push((buffer >> bits) as u8);
62 | buffer &= (1 << bits) - 1;
63 | }
64 | }
65 |
66 | if bits >= 6 || padding > 2 || (padding > 0 && bits > 0) {
67 | return None;
68 | }
69 |
70 | Some(decoded)
71 | }
--------------------------------------------------------------------------------
/impl/rs/src/lib.rs:
--------------------------------------------------------------------------------
1 | #![allow(
2 | clippy::missing_panics_doc,
3 | clippy::missing_errors_doc,
4 | clippy::must_use_candidate,
5 | clippy::module_name_repetitions
6 | )]
7 |
8 | pub mod pika;
9 | pub mod snowflake;
10 | mod base64;
11 | mod utils;
12 |
--------------------------------------------------------------------------------
/impl/rs/src/pika.rs:
--------------------------------------------------------------------------------
1 | use std::io::Error;
2 |
3 | use rand::rngs::OsRng;
4 | use rand::RngCore;
5 |
6 | use crate::base64::{base64_decode, base64_encode};
7 | use crate::snowflake::{self, Snowflake};
8 |
9 | #[derive(Clone, Debug)]
10 | pub struct PrefixRecord {
11 | pub prefix: String,
12 | pub description: Option,
13 | pub secure: bool,
14 | }
15 |
16 | #[derive(Debug)]
17 | pub struct DecodedPika {
18 | pub prefix: String,
19 | pub tail: String,
20 | pub snowflake: u64,
21 | pub node_id: u32,
22 | pub timestamp: u64,
23 | pub epoch: u64,
24 | pub seq: u64,
25 | pub version: i8,
26 | pub prefix_record: PrefixRecord,
27 | }
28 |
29 | #[derive(Debug, Clone)]
30 | pub struct Pika {
31 | pub prefixes: Vec,
32 | pub epoch: u64,
33 | pub node_id: u32,
34 | pub disable_lowercase: Option,
35 | snowflake: Snowflake,
36 | }
37 |
38 | #[derive(Default)] // default implementation was identical to std::default::Default
39 | pub struct InitOptions {
40 | pub epoch: Option,
41 | pub node_id: Option,
42 | pub disable_lowercase: Option,
43 | }
44 |
45 | pub const DEFAULT_EPOCH: u64 = 1_640_995_200_000;
46 |
47 | impl Pika {
48 | pub fn new(prefixes: Vec, options: &InitOptions) -> Self {
49 | let epoch = options.epoch.unwrap_or(DEFAULT_EPOCH);
50 | let node_id = options.node_id.unwrap_or_else(Self::compute_node_id);
51 |
52 | Self {
53 | prefixes,
54 | epoch,
55 | node_id,
56 | disable_lowercase: options.disable_lowercase,
57 | snowflake: snowflake::Snowflake::new_with_nodeid(epoch, node_id),
58 | }
59 | }
60 |
61 | #[allow(clippy::cast_possible_truncation)]
62 | fn compute_node_id() -> u32 {
63 | let res = mac_address::get_mac_address().unwrap();
64 | let first_mac = res.unwrap().to_string();
65 |
66 | let first_mac = u64::from_str_radix(&first_mac.replace(':', ""), 16).unwrap();
67 |
68 | // should lower the chance of collisions
69 | (first_mac % 1024) as u32
70 | }
71 |
72 | pub fn deconstruct(&self, id: &str) -> DecodedPika {
73 | let s = id.split('_').collect::>();
74 |
75 | let prefix = s[..s.len() - 1].join("_");
76 |
77 | let tail = s[s.len() - 1].to_string();
78 |
79 | let prefix_record = self.prefixes.iter().find(|x| x.prefix == prefix);
80 |
81 | let decoded_tail = base64_decode(&tail).unwrap();
82 |
83 | let decoded_tail = &String::from_utf8_lossy(&decoded_tail).to_string();
84 | let sf = decoded_tail.split('_').collect::>();
85 | let sf = sf[sf.len() - 1].to_string();
86 |
87 | let snowflake = self.snowflake.decode(&sf);
88 |
89 | DecodedPika {
90 | prefix,
91 | tail,
92 | snowflake: sf.parse::().unwrap(),
93 | node_id: self.node_id,
94 | timestamp: snowflake.timestamp,
95 | epoch: self.epoch,
96 | seq: snowflake.seq,
97 | version: 1,
98 | prefix_record: prefix_record.unwrap().clone(),
99 | }
100 | }
101 |
102 | pub fn gen(&mut self, prefix: &str) -> Result {
103 | let valid_prefix = prefix
104 | .chars()
105 | .all(|c| c.is_ascii_alphanumeric() || c == '_')
106 | && prefix.len() <= 32
107 | && !prefix.is_empty();
108 |
109 | assert!(valid_prefix, "Invalid prefix: {prefix}");
110 |
111 | let prefix_record = self.prefixes.iter().find(|x| x.prefix == prefix);
112 |
113 | assert!(prefix_record.is_some(), "Prefix not found: {prefix}");
114 |
115 | let snowflake = self.snowflake.gen();
116 |
117 | let id = if prefix_record.unwrap().secure {
118 | let mut bytes = [0u8; 16];
119 | OsRng.fill_bytes(&mut bytes);
120 |
121 | let hex_string = hex::encode(bytes);
122 |
123 | format!(
124 | "{}_{}",
125 | prefix,
126 | base64_encode(format!("_s_{}_{}", hex_string, snowflake.as_str()))
127 | )
128 | } else {
129 | format!("{}_{}", prefix, base64_encode(snowflake).replace('=', ""))
130 | };
131 |
132 | Ok(id)
133 | }
134 | }
135 |
136 | #[cfg(test)]
137 | mod tests {
138 | use super::{InitOptions, Pika, PrefixRecord};
139 |
140 | #[test]
141 | fn init_pika() {
142 | let prefixes = [
143 | PrefixRecord {
144 | prefix: "test".to_string(),
145 | description: Some("test".to_string()),
146 | secure: false,
147 | },
148 | PrefixRecord {
149 | prefix: "s_test".to_string(),
150 | description: Some("test".to_string()),
151 | secure: true,
152 | },
153 | ];
154 |
155 | let mut pika = Pika::new(
156 | prefixes.to_vec(),
157 | &InitOptions {
158 | epoch: Some(1_650_153_600_000),
159 | node_id: None,
160 | disable_lowercase: Some(true),
161 | },
162 | );
163 |
164 | let id = pika.gen("test").unwrap();
165 | let deconstructed = pika.deconstruct(&id);
166 |
167 | let s_id = pika.gen("s_test").unwrap();
168 |
169 | let s_deconstructed = pika.deconstruct(&s_id);
170 |
171 | assert_eq!(deconstructed.node_id, Pika::compute_node_id());
172 | assert_eq!(deconstructed.seq, 0);
173 | assert_eq!(deconstructed.version, 1);
174 | assert_eq!(deconstructed.epoch, 1_650_153_600_000);
175 |
176 | assert_eq!(s_deconstructed.node_id, Pika::compute_node_id());
177 | assert_eq!(s_deconstructed.seq, 1);
178 | assert_eq!(s_deconstructed.version, 1);
179 | assert_eq!(s_deconstructed.epoch, 1_650_153_600_000);
180 | }
181 |
182 | #[test]
183 | fn init_pika_with_nodeid() {
184 | let prefixes = [PrefixRecord {
185 | prefix: "test".to_string(),
186 | description: Some("test".to_string()),
187 | secure: false,
188 | }];
189 |
190 | let mut pika = Pika::new(
191 | prefixes.to_vec(),
192 | &InitOptions {
193 | epoch: Some(1_650_153_600_000),
194 | node_id: Some(622),
195 | disable_lowercase: Some(true),
196 | },
197 | );
198 |
199 | let id = pika.gen("test").unwrap();
200 | let deconstructed = pika.deconstruct(&id);
201 |
202 | assert_eq!(deconstructed.node_id, 622);
203 | assert_eq!(deconstructed.seq, 0);
204 | assert_eq!(deconstructed.version, 1);
205 | assert_eq!(deconstructed.epoch, 1_650_153_600_000);
206 | }
207 | }
208 |
--------------------------------------------------------------------------------
/impl/rs/src/snowflake.rs:
--------------------------------------------------------------------------------
1 | use crate::utils::now_timestamp;
2 |
3 | #[derive(Clone, Debug)]
4 | pub struct Snowflake {
5 | epoch: u64,
6 | node_id: u32,
7 | seq: u32,
8 | last_sequence_exhaustion: u64,
9 | }
10 |
11 | #[derive(Clone, Debug)]
12 | pub struct DecodedSnowflake {
13 | pub id: u64,
14 | pub timestamp: u64,
15 | pub node_id: u32,
16 | pub seq: u64,
17 | pub epoch: u64,
18 | }
19 |
20 | impl Snowflake {
21 | pub fn new_with_nodeid(epoch: u64, node_id: u32) -> Self {
22 | Self {
23 | epoch,
24 | node_id,
25 | seq: 0,
26 | last_sequence_exhaustion: 0,
27 | }
28 | }
29 |
30 | #[inline]
31 | pub fn gen(&mut self) -> String {
32 | self.gen_with_ts(now_timestamp())
33 | }
34 |
35 | pub fn gen_with_ts(&mut self, timestamp: u64) -> String {
36 | if self.seq >= 4095 && timestamp == self.last_sequence_exhaustion {
37 | while now_timestamp() - timestamp < 1 {
38 | continue;
39 | }
40 | }
41 |
42 | let sf = ((timestamp - self.epoch) << 22)
43 | | (u64::from(self.node_id) << 12)
44 | | u64::from(self.seq);
45 |
46 | self.seq = if self.seq >= 4095 { 0 } else { self.seq + 1 };
47 |
48 | if self.seq == 4095 {
49 | self.last_sequence_exhaustion = timestamp;
50 | }
51 |
52 | sf.to_string()
53 | }
54 |
55 | pub fn decode(&self, sf: &str) -> DecodedSnowflake {
56 | let sf = sf.parse::().unwrap();
57 |
58 | let timestamp = (sf >> 22) + self.epoch;
59 | let node_id = (sf >> 12) & 0b11_1111_1111;
60 | let seq = sf & 0b1111_1111_1111;
61 |
62 | DecodedSnowflake {
63 | id: sf,
64 | timestamp,
65 | node_id: node_id as u32,
66 | seq,
67 | epoch: self.epoch,
68 | }
69 | }
70 | }
71 |
72 | mod test {
73 | #[test]
74 | fn generate_snowflake() {
75 | // if the node_id >= 1024 it will go to 0?
76 | let mut sf = super::Snowflake::new_with_nodeid(650_153_600_000, 1023);
77 | let snowflake = sf.gen();
78 |
79 | let deconstruct = sf.decode(snowflake.as_str());
80 |
81 | assert_eq!(deconstruct.epoch, 650_153_600_000);
82 | assert_eq!(deconstruct.node_id, 1023);
83 | }
84 |
85 | #[test]
86 | fn generate_snowflakes() {
87 | let mut sf = super::Snowflake::new_with_nodeid(650_153_600_000, 1023);
88 |
89 | // when the seq is 4096, the next snowflake will be 0
90 | let snowflakes: Vec = (0..4096).map(|_| sf.gen()).collect();
91 | let last_snowflake = sf.gen();
92 |
93 | for (sequence, snowflake) in snowflakes.iter().enumerate() {
94 | let deconstruct = sf.decode(snowflake.as_str());
95 |
96 | assert_eq!(deconstruct.seq, sequence as u64);
97 | }
98 |
99 | let deconstruct = sf.decode(last_snowflake.as_str());
100 | assert_eq!(deconstruct.seq, 0);
101 | }
102 | }
103 |
--------------------------------------------------------------------------------
/impl/rs/src/utils.rs:
--------------------------------------------------------------------------------
1 | use std::time::{SystemTime, UNIX_EPOCH};
2 |
3 | #[allow(clippy::cast_possible_truncation)]
4 | pub fn now_timestamp() -> u64 {
5 | SystemTime::now()
6 | .duration_since(UNIX_EPOCH)
7 | .unwrap()
8 | .as_millis() as u64
9 | }
10 |
--------------------------------------------------------------------------------