ts-server-lib 0.0.17
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/LICENSE +1 -0
- package/README.md +8 -0
- package/db/TSMongo.d.ts +103 -0
- package/db/TSMongo.js +483 -0
- package/db/TSRQW.d.ts +271 -0
- package/db/TSRQW.js +699 -0
- package/db/TSRedis.d.ts +174 -0
- package/db/TSRedis.js +602 -0
- package/package.json +85 -0
- package/ussd/TSUssdMenu.d.ts +139 -0
- package/ussd/TSUssdMenu.js +364 -0
- package/ussd/TSUssdScreen.d.ts +58 -0
- package/ussd/TSUssdScreen.js +218 -0
- package/ussd/index.d.ts +3 -0
- package/ussd/index.js +19 -0
- package/ussd/providers/AfricasTalking.d.ts +3 -0
- package/ussd/providers/AfricasTalking.js +17 -0
- package/ussd/providers/AirtelDRC.d.ts +9 -0
- package/ussd/providers/AirtelDRC.js +31 -0
- package/ussd/providers/OrangeDRC.d.ts +5 -0
- package/ussd/providers/OrangeDRC.js +213 -0
- package/ussd/providers/VodacomDRC.d.ts +9 -0
- package/ussd/providers/VodacomDRC.js +48 -0
- package/ussd/providers/_.d.ts +55 -0
- package/ussd/providers/_.js +83 -0
- package/ussd/providers/index.d.ts +13 -0
- package/ussd/providers/index.js +56 -0
- package/utils/TSFile.d.ts +36 -0
- package/utils/TSFile.js +244 -0
- package/utils/TSHash.d.ts +20 -0
- package/utils/TSHash.js +75 -0
- package/utils/TSRequest.d.ts +39 -0
- package/utils/TSRequest.js +256 -0
- package/utils/TSStub.d.ts +159 -0
- package/utils/TSStub.js +296 -0
- package/utils/abort.d.ts +18 -0
- package/utils/abort.js +97 -0
- package/utils/mime.json +11358 -0
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.TSRequest = void 0;
|
|
37
|
+
exports.request = request;
|
|
38
|
+
const http = __importStar(require("http"));
|
|
39
|
+
const https = __importStar(require("https"));
|
|
40
|
+
const querystring_1 = require("querystring");
|
|
41
|
+
const fast_xml_parser_1 = require("fast-xml-parser");
|
|
42
|
+
const abort_1 = require("./abort");
|
|
43
|
+
class TSRequest {
|
|
44
|
+
static async form(url, options = {}, debug) {
|
|
45
|
+
options.headers = {
|
|
46
|
+
...{ 'Accept': '*/*', 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' }, ...(options.headers || {})
|
|
47
|
+
};
|
|
48
|
+
options.acceptAll = debug;
|
|
49
|
+
options.bcb = JSON.parse;
|
|
50
|
+
if (options.body !== null && typeof options.body === 'object' && !Buffer.isBuffer(options.body)) {
|
|
51
|
+
options.body = (0, querystring_1.stringify)(options.body);
|
|
52
|
+
}
|
|
53
|
+
const start = new Date();
|
|
54
|
+
return new Promise((resolve, reject) => TSRequest.raw(url, options).then(response => {
|
|
55
|
+
if (debug) {
|
|
56
|
+
console.log(`TSRequest:[${Number(new Date()) - Number(start)} ms][${url}]`, JSON.stringify({ REQUEST: options, RESPONSE: response }, null, 2));
|
|
57
|
+
}
|
|
58
|
+
if (response) {
|
|
59
|
+
resolve(response);
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
resolve(response);
|
|
63
|
+
}
|
|
64
|
+
}).catch(error => {
|
|
65
|
+
error.took = `TSRequest:[${Number(new Date()) - Number(start)} ms][${url}]`;
|
|
66
|
+
error.options = options;
|
|
67
|
+
reject(error);
|
|
68
|
+
}));
|
|
69
|
+
}
|
|
70
|
+
static async xml(url, options = {}, debug) {
|
|
71
|
+
const start = new Date();
|
|
72
|
+
options.headers = {
|
|
73
|
+
...{ 'Accept': 'text/xml', 'Content-Type': 'text/xml; charset=UTF-8' }, ...(options.headers || {})
|
|
74
|
+
};
|
|
75
|
+
options.acceptAll = debug;
|
|
76
|
+
if (options.body !== null && typeof options.body === 'object' && !Buffer.isBuffer(options.body)) {
|
|
77
|
+
options.body = new fast_xml_parser_1.XMLBuilder({ ignoreAttributes: false, attributeNamePrefix: '', attributesGroupName: '$', textNodeName: '_' }).build(options.body);
|
|
78
|
+
}
|
|
79
|
+
if (options.body != null && options.body !== '') {
|
|
80
|
+
Buffer.byteLength(typeof options.body === 'string' ? options.body : String(options.body));
|
|
81
|
+
}
|
|
82
|
+
return new Promise((resolve, reject) => TSRequest.raw(url, options).then((response) => {
|
|
83
|
+
if (debug) {
|
|
84
|
+
console.log(`TSRequest:[${Number(new Date()) - Number(start)} ms][${url}]`, JSON.stringify({ REQUEST: options, RESPONSE: response }, null, 2));
|
|
85
|
+
}
|
|
86
|
+
try {
|
|
87
|
+
resolve(new fast_xml_parser_1.XMLParser({ ignoreAttributes: false, attributeNamePrefix: '', attributesGroupName: '$', textNodeName: '_', isArray: (_n, jPath, _l, isAttr) => !isAttr && String(jPath).includes('.') }).parse(String(response ?? '')));
|
|
88
|
+
}
|
|
89
|
+
catch (error) {
|
|
90
|
+
reject(error);
|
|
91
|
+
}
|
|
92
|
+
}).catch(error => {
|
|
93
|
+
error.took = `TSRequest:[${Number(new Date()) - Number(start)} ms][${url}]`;
|
|
94
|
+
error.options = options;
|
|
95
|
+
reject(error);
|
|
96
|
+
}));
|
|
97
|
+
}
|
|
98
|
+
static async json(url, options = {}, debug) {
|
|
99
|
+
options.headers = {
|
|
100
|
+
...{ 'Accept': 'application/json', 'Content-Type': 'application/json; charset=UTF-8' }, ...(options.headers || {})
|
|
101
|
+
};
|
|
102
|
+
options.acceptAll = debug;
|
|
103
|
+
options.bcb = (response) => {
|
|
104
|
+
try {
|
|
105
|
+
return JSON.parse(response);
|
|
106
|
+
}
|
|
107
|
+
catch (e) {
|
|
108
|
+
console.error(e);
|
|
109
|
+
return response;
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
if (options.body !== null && typeof options.body === 'object' && !Buffer.isBuffer(options.body)) {
|
|
113
|
+
options.body = JSON.stringify(options.body);
|
|
114
|
+
}
|
|
115
|
+
const start = new Date();
|
|
116
|
+
return new Promise((resolve, reject) => TSRequest.raw(url, options).then(response => {
|
|
117
|
+
if (debug) {
|
|
118
|
+
console.log(`TSRequest:[${Number(new Date()) - Number(start)} ms][${url}]`, JSON.stringify({ REQUEST: options, RESPONSE: response }, null, 2));
|
|
119
|
+
}
|
|
120
|
+
if (response && response.data) {
|
|
121
|
+
resolve(response.data);
|
|
122
|
+
}
|
|
123
|
+
else {
|
|
124
|
+
resolve(response);
|
|
125
|
+
}
|
|
126
|
+
}).catch(error => {
|
|
127
|
+
error.took = `TSRequest:[${Number(new Date()) - Number(start)} ms][${url}]`;
|
|
128
|
+
error.options = options;
|
|
129
|
+
reject(error);
|
|
130
|
+
}));
|
|
131
|
+
}
|
|
132
|
+
static async raw(url, options = {}) {
|
|
133
|
+
const signal = options.signal;
|
|
134
|
+
if (signal?.aborted) {
|
|
135
|
+
return Promise.reject((0, abort_1.abortError)());
|
|
136
|
+
}
|
|
137
|
+
return new Promise((resolve, reject) => {
|
|
138
|
+
let settled = false;
|
|
139
|
+
let req;
|
|
140
|
+
let onAbort;
|
|
141
|
+
const cleanupAbort = () => {
|
|
142
|
+
if (signal && onAbort) {
|
|
143
|
+
signal.removeEventListener('abort', onAbort);
|
|
144
|
+
}
|
|
145
|
+
};
|
|
146
|
+
const finish = (action, value, err) => {
|
|
147
|
+
if (settled) {
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
settled = true;
|
|
151
|
+
cleanupAbort();
|
|
152
|
+
if (action === 'resolve') {
|
|
153
|
+
resolve(value);
|
|
154
|
+
}
|
|
155
|
+
else {
|
|
156
|
+
reject(err);
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
if (signal) {
|
|
160
|
+
onAbort = () => {
|
|
161
|
+
const abortErr = (0, abort_1.abortError)();
|
|
162
|
+
try {
|
|
163
|
+
req?.destroy(abortErr);
|
|
164
|
+
}
|
|
165
|
+
catch {
|
|
166
|
+
/* ignore */
|
|
167
|
+
}
|
|
168
|
+
finish('reject', undefined, abortErr);
|
|
169
|
+
};
|
|
170
|
+
signal.addEventListener('abort', onAbort, { once: true });
|
|
171
|
+
}
|
|
172
|
+
const params = TSRequest.url(url);
|
|
173
|
+
const optsForRequest = { ...options };
|
|
174
|
+
delete optsForRequest.signal;
|
|
175
|
+
Object.assign(params, optsForRequest);
|
|
176
|
+
if (!params.method) {
|
|
177
|
+
params.method = 'get';
|
|
178
|
+
}
|
|
179
|
+
if (params.search) {
|
|
180
|
+
params.path = params.path + params.search;
|
|
181
|
+
}
|
|
182
|
+
if (params.qs) {
|
|
183
|
+
params.path = params.path + (params.path.indexOf('?') > -1 ? '&' : '?') + (0, querystring_1.stringify)(params.qs);
|
|
184
|
+
}
|
|
185
|
+
try {
|
|
186
|
+
req = (params.protocol === 'https:' ? https : http).request(params, res => {
|
|
187
|
+
const statusCode = res.statusCode ?? 0;
|
|
188
|
+
if (!params.acceptAll && (statusCode < 200 || statusCode >= 300)) {
|
|
189
|
+
res.resume();
|
|
190
|
+
finish('reject', undefined, TSRequest.error('response', statusCode, { statusMessage: res.statusMessage, headers: res.headers }));
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
const chunks = [];
|
|
194
|
+
res.on('data', chunk => chunks.push(chunk));
|
|
195
|
+
res.on('end', () => {
|
|
196
|
+
try {
|
|
197
|
+
const buffer = Buffer.concat(chunks).toString();
|
|
198
|
+
const body = params.bcb
|
|
199
|
+
? { kind: 'success', status: statusCode, message: res.statusMessage, headers: res.headers, data: params.bcb(buffer) }
|
|
200
|
+
: buffer;
|
|
201
|
+
finish('resolve', body);
|
|
202
|
+
}
|
|
203
|
+
catch (e) {
|
|
204
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
205
|
+
finish('reject', undefined, TSRequest.error('parse', !params.acceptAll ? 109 : statusCode, { statusMessage: res.statusMessage, headers: res.headers, error: msg, body: chunks }));
|
|
206
|
+
}
|
|
207
|
+
});
|
|
208
|
+
});
|
|
209
|
+
req.on('error', e => {
|
|
210
|
+
finish('reject', undefined, TSRequest.error('error', 545, e.message));
|
|
211
|
+
});
|
|
212
|
+
req.on('timeout', () => {
|
|
213
|
+
const error = TSRequest.error('timeout', 563, 'Request destroyed');
|
|
214
|
+
req?.destroy(error);
|
|
215
|
+
finish('reject', undefined, error);
|
|
216
|
+
});
|
|
217
|
+
if (options.body) {
|
|
218
|
+
req.write(options.body);
|
|
219
|
+
}
|
|
220
|
+
req.end();
|
|
221
|
+
}
|
|
222
|
+
catch (e) {
|
|
223
|
+
const err = e instanceof Error ? e : new Error(String(e));
|
|
224
|
+
finish('reject', undefined, err);
|
|
225
|
+
}
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
static error(kind, status, message) {
|
|
229
|
+
return new Error(JSON.stringify({ kind: kind, status: status, message: message }));
|
|
230
|
+
}
|
|
231
|
+
static url(url) {
|
|
232
|
+
const m = /^(https?:)\/\/(([^:/?#]*)(?::([0-9]+))?)([/]{0,1}[^?#]*)(\?[^#]*|)(#.*|)$/.exec(url);
|
|
233
|
+
return m ? {
|
|
234
|
+
href: url, protocol: m[1], host: m[2], hostname: m[3], port: m[4], path: m[5], pathname: m[5], search: m[6], hash: m[7]
|
|
235
|
+
} : {};
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
exports.TSRequest = TSRequest;
|
|
239
|
+
function request(url, { body = undefined, qs = undefined, debug = undefined, ...options } = {}, kind = 'json') {
|
|
240
|
+
if (!options) {
|
|
241
|
+
options = {};
|
|
242
|
+
}
|
|
243
|
+
if (body) {
|
|
244
|
+
options.body = body;
|
|
245
|
+
}
|
|
246
|
+
else if (qs) {
|
|
247
|
+
options.qs = qs;
|
|
248
|
+
options.method = 'get';
|
|
249
|
+
}
|
|
250
|
+
options.rejectUnauthorized = false;
|
|
251
|
+
options.timeout = options.timeout || 18000;
|
|
252
|
+
return new Promise(resolve => TSRequest[kind](url, options, debug).catch((error) => {
|
|
253
|
+
console.error(error);
|
|
254
|
+
resolve({});
|
|
255
|
+
}).then(data => resolve(data)));
|
|
256
|
+
}
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TSStub — generic driver stub utility for all at-prefixed provider packages.
|
|
3
|
+
*
|
|
4
|
+
* Provides the shared scaffolding every stub needs:
|
|
5
|
+
* - Latency injection (fixed, random range, or abortable timeout)
|
|
6
|
+
* - Error injection (network error, timeout, rate-limit exceeded)
|
|
7
|
+
* - Outcome simulation (success / failure / pending / processing)
|
|
8
|
+
* - Retry-N-then-succeed counter
|
|
9
|
+
* - Webhook signature validation
|
|
10
|
+
* - In-memory store with deterministic ID generation
|
|
11
|
+
* - Rate-limit capability declaration
|
|
12
|
+
* - OAuth token / session simulation (atsocial)
|
|
13
|
+
* - Exchange rate table lookup (atexchange)
|
|
14
|
+
* - Capability matrix helper (atsocial)
|
|
15
|
+
* - Health status simulation (atkyc, others)
|
|
16
|
+
* - Config field extractor (normalise simulate* + latency from raw config)
|
|
17
|
+
* - Noop links factory
|
|
18
|
+
*
|
|
19
|
+
* Each at-package stub only needs to handle its domain-specific response shapes.
|
|
20
|
+
* TSStub has zero dependencies on any at-package — purely generic infrastructure.
|
|
21
|
+
*/
|
|
22
|
+
export interface TSStubLatencyConfig {
|
|
23
|
+
/** Fixed artificial latency in ms. Ignored when `latencyMsMin`/`latencyMsMax` are set. */
|
|
24
|
+
latencyMs?: number;
|
|
25
|
+
/** Lower bound for randomised latency (ms). Pair with `latencyMsMax`. */
|
|
26
|
+
latencyMsMin?: number;
|
|
27
|
+
/** Upper bound for randomised latency (ms). Pair with `latencyMsMin`. */
|
|
28
|
+
latencyMsMax?: number;
|
|
29
|
+
/** Hang exactly this long then throw AbortError — simulates provider timeout. */
|
|
30
|
+
simulateTimeoutMs?: number;
|
|
31
|
+
}
|
|
32
|
+
export interface TSStubConfig extends TSStubLatencyConfig {
|
|
33
|
+
/** Throw a generic network error immediately — tests circuit breakers and retry logic. */
|
|
34
|
+
simulateNetworkError?: boolean;
|
|
35
|
+
/** Return a rate-limit exceeded response instead of processing the request. */
|
|
36
|
+
simulateRateLimitExceeded?: boolean;
|
|
37
|
+
/**
|
|
38
|
+
* Domain-specific outcome token. Stubs pass this to `TSStub.outcome(cfg, fallback)`.
|
|
39
|
+
* Common values: 'completed' | 'pending' | 'failed' | 'processing' | 'delivered' | 'bounced'
|
|
40
|
+
*/
|
|
41
|
+
simulateOutcome?: string;
|
|
42
|
+
/** Failure / bounce reason text. Default: 'simulated failure'. */
|
|
43
|
+
simulateFailureReason?: string;
|
|
44
|
+
/**
|
|
45
|
+
* Fail the first N calls (per key), then succeed.
|
|
46
|
+
* Useful for testing retry policies end-to-end.
|
|
47
|
+
* Requires a `TSStubRetryCounter` per stub instance.
|
|
48
|
+
*/
|
|
49
|
+
simulateFailFirstN?: number;
|
|
50
|
+
/** Signature value this stub accepts on `parseWebhook`. Default: 'stub-signature'. */
|
|
51
|
+
simulateWebhookSignature?: string;
|
|
52
|
+
/** Declared rate limit (req/sec) surfaced via `asyncContract.capabilities`. */
|
|
53
|
+
simulateRateLimitPerSecond?: number;
|
|
54
|
+
/** Declared burst capacity above the steady-state rate. */
|
|
55
|
+
simulateRateLimitBurst?: number;
|
|
56
|
+
/** Synthetic health status for stubs that implement `checkHealth()`. Default: 'healthy'. */
|
|
57
|
+
simulateHealthStatus?: 'healthy' | 'degraded' | 'unhealthy';
|
|
58
|
+
/** Prefix for generated fake access tokens. Default: 'stub-token'. */
|
|
59
|
+
simulateTokenPrefix?: string;
|
|
60
|
+
/** OAuth callback base URL returned by `buildAuthorizeUrl`. Default: 'https://stub.local/callback'. */
|
|
61
|
+
simulateCallbackBase?: string;
|
|
62
|
+
}
|
|
63
|
+
export interface TSStubStore<T> {
|
|
64
|
+
get(id: string): T | undefined;
|
|
65
|
+
set(id: string, value: T): void;
|
|
66
|
+
delete(id: string): boolean;
|
|
67
|
+
list(): T[];
|
|
68
|
+
entries(): Array<[string, T]>;
|
|
69
|
+
clear(): void;
|
|
70
|
+
/** Generate a sequential deterministic ID: `<prefix>-001`, `<prefix>-002`, ... */
|
|
71
|
+
nextId(prefix?: string): string;
|
|
72
|
+
size: number;
|
|
73
|
+
}
|
|
74
|
+
export interface TSStubRetryCounter {
|
|
75
|
+
/** Returns true (should fail) for the first `n` calls on `key`, then false. */
|
|
76
|
+
shouldFail(key: string, n: number): boolean;
|
|
77
|
+
/** Reset call counts — call in test beforeEach. */
|
|
78
|
+
reset(key?: string): void;
|
|
79
|
+
}
|
|
80
|
+
export declare class TSStubRateLimitError extends Error {
|
|
81
|
+
readonly code = "STUB_RATE_LIMIT_EXCEEDED";
|
|
82
|
+
readonly retryAfterMs: number;
|
|
83
|
+
constructor(retryAfterMs?: number);
|
|
84
|
+
}
|
|
85
|
+
export declare class TSStub {
|
|
86
|
+
/**
|
|
87
|
+
* Delay by `cfg.latencyMs` (fixed) or a uniform random value in
|
|
88
|
+
* `[cfg.latencyMsMin, cfg.latencyMsMax]`. Abortable via `signal`.
|
|
89
|
+
* If `cfg.simulateTimeoutMs` is set, waits that long then throws AbortError.
|
|
90
|
+
*/
|
|
91
|
+
static maybeDelay(cfg: TSStubLatencyConfig, signal?: AbortSignal): Promise<void>;
|
|
92
|
+
/**
|
|
93
|
+
* Call at the start of every stub method. Throws a network error or rate-limit
|
|
94
|
+
* error when the corresponding simulate flag is set.
|
|
95
|
+
* Also honours `simulateTimeoutMs` via `maybeDelay`.
|
|
96
|
+
*/
|
|
97
|
+
static maybeInjectError(cfg: TSStubConfig, signal?: AbortSignal): Promise<void>;
|
|
98
|
+
/**
|
|
99
|
+
* Return `cfg.simulateOutcome ?? fallback`, cast to T.
|
|
100
|
+
* Domain stubs pass their expected outcome union as T.
|
|
101
|
+
*/
|
|
102
|
+
static outcome<T extends string>(cfg: Pick<TSStubConfig, 'simulateOutcome'>, fallback: T): T;
|
|
103
|
+
static isFailed(cfg: Pick<TSStubConfig, 'simulateOutcome'>): boolean;
|
|
104
|
+
static isPending(cfg: Pick<TSStubConfig, 'simulateOutcome'>): boolean;
|
|
105
|
+
static failureReason(cfg: Pick<TSStubConfig, 'simulateFailureReason'>, fallback?: string): string;
|
|
106
|
+
/** Factory — create one per stub instance; call `reset()` in test teardown. */
|
|
107
|
+
static createRetryCounter(): TSStubRetryCounter;
|
|
108
|
+
/**
|
|
109
|
+
* Check retry counter. Returns true when the call should fail.
|
|
110
|
+
* Usage inside a stub method:
|
|
111
|
+
* if (retry.shouldFail(operationKey, cfg.simulateFailFirstN ?? 0)) throw ...
|
|
112
|
+
*/
|
|
113
|
+
static shouldFail(counter: TSStubRetryCounter, key: string, cfg: Pick<TSStubConfig, 'simulateFailFirstN'>): boolean;
|
|
114
|
+
/**
|
|
115
|
+
* Validate webhook signature against `cfg.simulateWebhookSignature`.
|
|
116
|
+
* Returns true when signatures match (or when incoming equals the default stub value).
|
|
117
|
+
*/
|
|
118
|
+
static validateSignature(incoming: string | undefined, cfg: Pick<TSStubConfig, 'simulateWebhookSignature'>, defaultSignature?: string): boolean;
|
|
119
|
+
static rateLimitCapabilities(cfg: Pick<TSStubConfig, 'simulateRateLimitPerSecond' | 'simulateRateLimitBurst'>): {
|
|
120
|
+
rateLimitPerSecond?: number;
|
|
121
|
+
rateLimitBurst?: number;
|
|
122
|
+
};
|
|
123
|
+
static healthStatus(cfg: Pick<TSStubConfig, 'simulateHealthStatus'>): 'healthy' | 'degraded' | 'unhealthy';
|
|
124
|
+
/** Factory — create one store per entity type in the stub constructor. */
|
|
125
|
+
static createStore<T>(): TSStubStore<T>;
|
|
126
|
+
/** Generate a random stub ID: `stub-<prefix>-<8 hex chars>`. */
|
|
127
|
+
static id(prefix?: string): string;
|
|
128
|
+
/**
|
|
129
|
+
* Create a sequential ID generator: returns a function that yields
|
|
130
|
+
* `stub-<prefix>-001`, `stub-<prefix>-002`, ... (resets per call to this factory).
|
|
131
|
+
*/
|
|
132
|
+
static seqId(prefix?: string): () => string;
|
|
133
|
+
static fakeToken(cfg?: Pick<TSStubConfig, 'simulateTokenPrefix'>): {
|
|
134
|
+
accessToken: string;
|
|
135
|
+
refreshToken: string;
|
|
136
|
+
expiresAt: number;
|
|
137
|
+
};
|
|
138
|
+
static fakeAuthUrl(cfg?: Pick<TSStubConfig, 'simulateCallbackBase'>, params?: Record<string, string>): string;
|
|
139
|
+
/**
|
|
140
|
+
* Look up `from → to` in a rate table, then try inverse, then fall back to `defaultRate`.
|
|
141
|
+
* Same logic as atexchange `Stub` driver.
|
|
142
|
+
*/
|
|
143
|
+
static resolveRate(from: string, to: string, table?: Record<string, number>, defaultRate?: number): number;
|
|
144
|
+
/**
|
|
145
|
+
* Build a capability map for packages like atsocial that expose a fixed set of keys.
|
|
146
|
+
* All keys default to `'unsupported'`; `supported` overrides per key.
|
|
147
|
+
*/
|
|
148
|
+
static capabilities<K extends string>(allKeys: readonly K[], supported?: Partial<Record<K, 'supported' | 'requires_review' | 'unsupported'>>): Record<K, 'supported' | 'requires_review' | 'unsupported'>;
|
|
149
|
+
/**
|
|
150
|
+
* Extract all TSStubConfig fields from a raw config object.
|
|
151
|
+
* Used in `normalizeDriverConfig` in each at-package stub.
|
|
152
|
+
*/
|
|
153
|
+
static extractConfig(raw: Record<string, unknown>): Partial<TSStubConfig>;
|
|
154
|
+
/**
|
|
155
|
+
* Noop links object — used as the `protected links` property in stub drivers.
|
|
156
|
+
* scheme defaults to 'stub'.
|
|
157
|
+
*/
|
|
158
|
+
static noopLinks(scheme?: string): Record<string, Record<string, string>>;
|
|
159
|
+
}
|