@koolbase/core 11.4.0 → 12.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/aggregate.d.ts +115 -0
- package/dist/cjs/aggregate.js +26 -0
- package/dist/cjs/auth-errors.d.ts +8 -0
- package/dist/cjs/auth-errors.js +27 -14
- package/dist/cjs/auth.d.ts +130 -7
- package/dist/cjs/auth.js +252 -10
- package/dist/cjs/database-errors.d.ts +46 -0
- package/dist/cjs/database-errors.js +71 -1
- package/dist/cjs/database.d.ts +23 -0
- package/dist/cjs/database.js +64 -0
- package/dist/cjs/device-metadata.js +7 -1
- package/dist/cjs/fiscal.d.ts +114 -0
- package/dist/cjs/fiscal.js +167 -0
- package/dist/cjs/index.d.ts +3 -0
- package/dist/cjs/index.js +8 -1
- package/dist/cjs/types.d.ts +58 -0
- package/dist/esm/aggregate.d.ts +115 -0
- package/dist/esm/aggregate.js +25 -0
- package/dist/esm/auth-errors.d.ts +8 -0
- package/dist/esm/auth-errors.js +25 -13
- package/dist/esm/auth.d.ts +130 -7
- package/dist/esm/auth.js +253 -11
- package/dist/esm/database-errors.d.ts +46 -0
- package/dist/esm/database-errors.js +66 -0
- package/dist/esm/database.d.ts +23 -0
- package/dist/esm/database.js +64 -0
- package/dist/esm/device-metadata.js +7 -1
- package/dist/esm/fiscal.d.ts +114 -0
- package/dist/esm/fiscal.js +159 -0
- package/dist/esm/index.d.ts +3 -0
- package/dist/esm/index.js +2 -0
- package/dist/esm/types.d.ts +58 -0
- package/package.json +1 -1
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.KoolbaseFiscal = exports.KoolbaseFiscalError = void 0;
|
|
4
|
+
exports.isFiscalized = isFiscalized;
|
|
5
|
+
exports.isPending = isPending;
|
|
6
|
+
exports.decodeFiscalResponse = decodeFiscalResponse;
|
|
7
|
+
const errors_js_1 = require("./errors.js");
|
|
8
|
+
const KNOWN_STATUSES = [
|
|
9
|
+
'created', 'blocked', 'queued', 'submitting',
|
|
10
|
+
'retrying', 'fiscalized', 'attention', 'voided',
|
|
11
|
+
];
|
|
12
|
+
function parseStatus(raw) {
|
|
13
|
+
return typeof raw === 'string' && KNOWN_STATUSES.includes(raw)
|
|
14
|
+
? raw
|
|
15
|
+
: 'unknown';
|
|
16
|
+
}
|
|
17
|
+
/** True once the authority has certified the document. */
|
|
18
|
+
function isFiscalized(r) {
|
|
19
|
+
return r.status === 'fiscalized';
|
|
20
|
+
}
|
|
21
|
+
/** True while delivery to the authority is still in progress. */
|
|
22
|
+
function isPending(r) {
|
|
23
|
+
return r.status === 'queued'
|
|
24
|
+
|| r.status === 'submitting'
|
|
25
|
+
|| r.status === 'retrying';
|
|
26
|
+
}
|
|
27
|
+
/** A fiscal request that failed. Carries the HTTP status where there was one. */
|
|
28
|
+
class KoolbaseFiscalError extends errors_js_1.KoolbaseError {
|
|
29
|
+
constructor(message, statusCode) {
|
|
30
|
+
super(message, 'fiscal_failed');
|
|
31
|
+
this.statusCode = statusCode;
|
|
32
|
+
this.name = 'KoolbaseFiscalError';
|
|
33
|
+
Object.setPrototypeOf(this, KoolbaseFiscalError.prototype);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
exports.KoolbaseFiscalError = KoolbaseFiscalError;
|
|
37
|
+
function resultFromJson(json) {
|
|
38
|
+
const numbers = json.numbers;
|
|
39
|
+
return {
|
|
40
|
+
intentId: String(json.intent_id ?? ''),
|
|
41
|
+
status: parseStatus(json.status),
|
|
42
|
+
blockedReason: json.blocked_reason,
|
|
43
|
+
numbers: numbers ?? undefined,
|
|
44
|
+
sealedAt: json.sealed_at,
|
|
45
|
+
certification: json.certification,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Classify a response. Status and body in, result or error out.
|
|
50
|
+
*
|
|
51
|
+
* Exported for testability, and because auth handling belongs with the
|
|
52
|
+
* session hook rather than here — the same split the Flutter SDK makes.
|
|
53
|
+
*/
|
|
54
|
+
async function decodeFiscalResponse(status, body) {
|
|
55
|
+
let parsed;
|
|
56
|
+
try {
|
|
57
|
+
parsed = JSON.parse(body);
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
throw new KoolbaseFiscalError(`fiscal: unreadable response (${status})`, status);
|
|
61
|
+
}
|
|
62
|
+
if ((status >= 200 && status < 300) || status === 202) {
|
|
63
|
+
return resultFromJson(parsed);
|
|
64
|
+
}
|
|
65
|
+
throw new KoolbaseFiscalError(parsed.error ?? 'fiscal: request failed', status);
|
|
66
|
+
}
|
|
67
|
+
// ─── KoolbaseFiscal ──────────────────────────────────────────────────────────
|
|
68
|
+
/**
|
|
69
|
+
* Koolbase Fiscal — submitting sales to a tax authority.
|
|
70
|
+
*
|
|
71
|
+
* Jurisdiction-neutral: the SDK moves payloads and reads back whatever the
|
|
72
|
+
* authority certified. Ghana's GRA E-VAT is the first jurisdiction; nothing
|
|
73
|
+
* here names it.
|
|
74
|
+
*/
|
|
75
|
+
class KoolbaseFiscal {
|
|
76
|
+
constructor(config, getToken, onSessionExpired) {
|
|
77
|
+
this.config = config;
|
|
78
|
+
this.getToken = getToken;
|
|
79
|
+
this.onSessionExpired = onSessionExpired;
|
|
80
|
+
}
|
|
81
|
+
async buildHeaders() {
|
|
82
|
+
const token = await this.getToken();
|
|
83
|
+
return {
|
|
84
|
+
'Content-Type': 'application/json',
|
|
85
|
+
'x-api-key': this.config.publicKey,
|
|
86
|
+
...(token ? { Authorization: `Bearer ${token}` } : {}),
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
async decode(res) {
|
|
90
|
+
if (res.status === 401 || res.status === 403) {
|
|
91
|
+
await this.onSessionExpired?.();
|
|
92
|
+
throw new errors_js_1.KoolbaseUnauthenticatedError(`fiscal: unauthenticated (${res.status})`);
|
|
93
|
+
}
|
|
94
|
+
return decodeFiscalResponse(res.status, await res.text());
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Submit a sale for fiscalization.
|
|
98
|
+
*
|
|
99
|
+
* The sale is durably recorded the moment this returns; fiscal numbers and
|
|
100
|
+
* the authority's certification follow asynchronously — poll `status()`
|
|
101
|
+
* with the same `clientRef` to watch it.
|
|
102
|
+
*
|
|
103
|
+
* **On timeout, do not resubmit blindly.** The sale may have fiscalized
|
|
104
|
+
* anyway: the request reached the server and the response was lost. Poll
|
|
105
|
+
* `status()` with the same `clientRef` instead, which is idempotent and
|
|
106
|
+
* tells you what actually happened.
|
|
107
|
+
*
|
|
108
|
+
* @param clientRef your own reference for this sale; the idempotency key
|
|
109
|
+
* and the handle you poll and render receipts by.
|
|
110
|
+
*/
|
|
111
|
+
async submit(params) {
|
|
112
|
+
const controller = new AbortController();
|
|
113
|
+
const timer = setTimeout(() => controller.abort(), params.timeoutMs ?? 30000);
|
|
114
|
+
try {
|
|
115
|
+
const res = await fetch(`${this.config.baseUrl}/v1/sdk/fiscal/submit`, {
|
|
116
|
+
method: 'POST',
|
|
117
|
+
headers: await this.buildHeaders(),
|
|
118
|
+
signal: controller.signal,
|
|
119
|
+
body: JSON.stringify({
|
|
120
|
+
device_id: params.deviceId,
|
|
121
|
+
client_ref: params.clientRef,
|
|
122
|
+
payload: params.payload,
|
|
123
|
+
}),
|
|
124
|
+
});
|
|
125
|
+
return await this.decode(res);
|
|
126
|
+
}
|
|
127
|
+
catch (e) {
|
|
128
|
+
if (e instanceof Error && e.name === 'AbortError') {
|
|
129
|
+
throw new KoolbaseFiscalError('fiscal submit timed out — the sale may still fiscalize; '
|
|
130
|
+
+ 'poll status with the same clientRef');
|
|
131
|
+
}
|
|
132
|
+
throw e;
|
|
133
|
+
}
|
|
134
|
+
finally {
|
|
135
|
+
clearTimeout(timer);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Read an intent's current state by the reference it was submitted under.
|
|
140
|
+
*
|
|
141
|
+
* This is the receipt-rendering read: once the status is `fiscalized`, the
|
|
142
|
+
* `certification` carries what the receipt must show — for Ghana, the
|
|
143
|
+
* signature, receipt number and QR verification URL.
|
|
144
|
+
*/
|
|
145
|
+
async status(params) {
|
|
146
|
+
const controller = new AbortController();
|
|
147
|
+
const timer = setTimeout(() => controller.abort(), params.timeoutMs ?? 15000);
|
|
148
|
+
try {
|
|
149
|
+
const q = new URLSearchParams({
|
|
150
|
+
device_id: params.deviceId,
|
|
151
|
+
client_ref: params.clientRef,
|
|
152
|
+
});
|
|
153
|
+
const res = await fetch(`${this.config.baseUrl}/v1/sdk/fiscal/status?${q.toString()}`, { method: 'GET', headers: await this.buildHeaders(), signal: controller.signal });
|
|
154
|
+
return await this.decode(res);
|
|
155
|
+
}
|
|
156
|
+
catch (e) {
|
|
157
|
+
if (e instanceof Error && e.name === 'AbortError') {
|
|
158
|
+
throw new KoolbaseFiscalError('fiscal status timed out');
|
|
159
|
+
}
|
|
160
|
+
throw e;
|
|
161
|
+
}
|
|
162
|
+
finally {
|
|
163
|
+
clearTimeout(timer);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
exports.KoolbaseFiscal = KoolbaseFiscal;
|
package/dist/cjs/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export * from './types.js';
|
|
2
2
|
export * from './errors.js';
|
|
3
|
+
export * from './aggregate.js';
|
|
3
4
|
export * from './conflict.js';
|
|
4
5
|
export * from './pending-write.js';
|
|
5
6
|
export * from './function-errors.js';
|
|
@@ -13,6 +14,8 @@ export { KoolbaseFunctions } from './functions.js';
|
|
|
13
14
|
export { KoolbaseRealtime } from './realtime.js';
|
|
14
15
|
export { KoolbaseStorage } from './storage.js';
|
|
15
16
|
export { KoolbaseAnalytics } from './analytics.js';
|
|
17
|
+
export { KoolbaseFiscal, KoolbaseFiscalError, decodeFiscalResponse, isFiscalized, isPending } from './fiscal.js';
|
|
18
|
+
export type { FiscalIntentResult, FiscalStatus } from './fiscal.js';
|
|
16
19
|
export { KoolbaseMessaging } from './messaging.js';
|
|
17
20
|
export type { RegisterTokenOptions } from './messaging.js';
|
|
18
21
|
export { getOrCreateDeviceId } from './device-id.js';
|
package/dist/cjs/index.js
CHANGED
|
@@ -19,9 +19,10 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
19
19
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
20
20
|
};
|
|
21
21
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
22
|
-
exports.memoryPlatform = exports.getPlatform = exports.setPlatform = exports.RestoreResult = exports.koolbaseSdkVersion = exports.getOrCreateDeviceId = exports.KoolbaseMessaging = exports.KoolbaseAnalytics = exports.KoolbaseStorage = exports.KoolbaseRealtime = exports.KoolbaseFunctions = exports.KoolbaseFlags = exports.KoolbaseDatabase = exports.KoolbaseAuth = void 0;
|
|
22
|
+
exports.memoryPlatform = exports.getPlatform = exports.setPlatform = exports.RestoreResult = exports.koolbaseSdkVersion = exports.getOrCreateDeviceId = exports.KoolbaseMessaging = exports.isPending = exports.isFiscalized = exports.decodeFiscalResponse = exports.KoolbaseFiscalError = exports.KoolbaseFiscal = exports.KoolbaseAnalytics = exports.KoolbaseStorage = exports.KoolbaseRealtime = exports.KoolbaseFunctions = exports.KoolbaseFlags = exports.KoolbaseDatabase = exports.KoolbaseAuth = void 0;
|
|
23
23
|
__exportStar(require("./types.js"), exports);
|
|
24
24
|
__exportStar(require("./errors.js"), exports);
|
|
25
|
+
__exportStar(require("./aggregate.js"), exports);
|
|
25
26
|
__exportStar(require("./conflict.js"), exports);
|
|
26
27
|
__exportStar(require("./pending-write.js"), exports);
|
|
27
28
|
__exportStar(require("./function-errors.js"), exports);
|
|
@@ -42,6 +43,12 @@ var storage_js_1 = require("./storage.js");
|
|
|
42
43
|
Object.defineProperty(exports, "KoolbaseStorage", { enumerable: true, get: function () { return storage_js_1.KoolbaseStorage; } });
|
|
43
44
|
var analytics_js_1 = require("./analytics.js");
|
|
44
45
|
Object.defineProperty(exports, "KoolbaseAnalytics", { enumerable: true, get: function () { return analytics_js_1.KoolbaseAnalytics; } });
|
|
46
|
+
var fiscal_js_1 = require("./fiscal.js");
|
|
47
|
+
Object.defineProperty(exports, "KoolbaseFiscal", { enumerable: true, get: function () { return fiscal_js_1.KoolbaseFiscal; } });
|
|
48
|
+
Object.defineProperty(exports, "KoolbaseFiscalError", { enumerable: true, get: function () { return fiscal_js_1.KoolbaseFiscalError; } });
|
|
49
|
+
Object.defineProperty(exports, "decodeFiscalResponse", { enumerable: true, get: function () { return fiscal_js_1.decodeFiscalResponse; } });
|
|
50
|
+
Object.defineProperty(exports, "isFiscalized", { enumerable: true, get: function () { return fiscal_js_1.isFiscalized; } });
|
|
51
|
+
Object.defineProperty(exports, "isPending", { enumerable: true, get: function () { return fiscal_js_1.isPending; } });
|
|
45
52
|
var messaging_js_1 = require("./messaging.js");
|
|
46
53
|
Object.defineProperty(exports, "KoolbaseMessaging", { enumerable: true, get: function () { return messaging_js_1.KoolbaseMessaging; } });
|
|
47
54
|
var device_id_js_1 = require("./device-id.js");
|
package/dist/cjs/types.d.ts
CHANGED
|
@@ -88,6 +88,64 @@ export type SignUpResult = {
|
|
|
88
88
|
user: KoolbaseUser;
|
|
89
89
|
session: null;
|
|
90
90
|
};
|
|
91
|
+
/**
|
|
92
|
+
* One active session — somewhere the user is signed in.
|
|
93
|
+
*
|
|
94
|
+
* Token hashes are never included; the server excludes them deliberately.
|
|
95
|
+
*/
|
|
96
|
+
export interface KoolbaseSessionInfo {
|
|
97
|
+
id: string;
|
|
98
|
+
/** Where it signed in from, when the server captured it. */
|
|
99
|
+
ip?: string;
|
|
100
|
+
/** What signed in. A browser sets this; native SDKs send their own. */
|
|
101
|
+
userAgent?: string;
|
|
102
|
+
/** A per-install label, where the SDK supplied one. */
|
|
103
|
+
deviceLabel?: string;
|
|
104
|
+
createdAt: string;
|
|
105
|
+
expiresAt: string;
|
|
106
|
+
/**
|
|
107
|
+
* True for the session making the request.
|
|
108
|
+
*
|
|
109
|
+
* A device list needs this: without it a user cannot tell which row is the
|
|
110
|
+
* phone in their hand, and "sign out everywhere else" has nothing to
|
|
111
|
+
* exclude.
|
|
112
|
+
*/
|
|
113
|
+
isCurrent: boolean;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* One thing that happened to this account.
|
|
117
|
+
*
|
|
118
|
+
* The server sanitizes these: `eventData` carries only the fields allowed
|
|
119
|
+
* for that event type, so a lockout can say which address was attempted
|
|
120
|
+
* while a successful login carries nothing extra.
|
|
121
|
+
*/
|
|
122
|
+
export interface KoolbaseAuditEvent {
|
|
123
|
+
id: string;
|
|
124
|
+
/**
|
|
125
|
+
* What happened. One of: auth.login.success, auth.login.failed,
|
|
126
|
+
* auth.account.locked, auth.account.unlocked, auth.session.created,
|
|
127
|
+
* auth.session.refreshed, auth.session.revoked, auth.password.changed,
|
|
128
|
+
* auth.user.verified.
|
|
129
|
+
*
|
|
130
|
+
* A string rather than a union: the server may add one, and an app that
|
|
131
|
+
* stops compiling because it did would be worse than one that shows an
|
|
132
|
+
* event it does not recognise.
|
|
133
|
+
*/
|
|
134
|
+
eventType: string;
|
|
135
|
+
occurredAt: string;
|
|
136
|
+
ip?: string;
|
|
137
|
+
userAgent?: string;
|
|
138
|
+
/** Whatever that event type is allowed to carry. Often empty. */
|
|
139
|
+
eventData: Record<string, unknown>;
|
|
140
|
+
}
|
|
141
|
+
/** A page of audit events, and how many there are in total. */
|
|
142
|
+
export interface KoolbaseAuditPage {
|
|
143
|
+
events: KoolbaseAuditEvent[];
|
|
144
|
+
/** Across all pages, so an app knows whether to offer "show more". */
|
|
145
|
+
total: number;
|
|
146
|
+
limit: number;
|
|
147
|
+
offset: number;
|
|
148
|
+
}
|
|
91
149
|
export interface ResendVerificationResult {
|
|
92
150
|
alreadyVerified: boolean;
|
|
93
151
|
/** When the link in the email stops working. Null when nothing was sent. */
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Asking "how many" and "how much" over a collection, correctly.
|
|
3
|
+
*
|
|
4
|
+
* Correct means the whole authorized set — never the first page — with the
|
|
5
|
+
* caller's read rule applied inside the query, and never a bare number:
|
|
6
|
+
* collections are schemaless, so a total that quietly excluded three
|
|
7
|
+
* malformed rows would be a wrong number that looks right. Every result
|
|
8
|
+
* carries its accounting.
|
|
9
|
+
*
|
|
10
|
+
* ```ts
|
|
11
|
+
* const r = await Koolbase.db.aggregate({
|
|
12
|
+
* collection: 'orders',
|
|
13
|
+
* groupBy: { field: 'created_at', bucket: 'month', timezone: 'Africa/Accra' },
|
|
14
|
+
* measures: [{ aggregate: 'sum', field: 'total', as: 'revenue' }],
|
|
15
|
+
* });
|
|
16
|
+
*
|
|
17
|
+
* for (const g of r.groups) {
|
|
18
|
+
* console.log(g.category, g.values.revenue);
|
|
19
|
+
* }
|
|
20
|
+
* if (r.accounting.revenue.skipped > 0) {
|
|
21
|
+
* // some orders had a total that was not a number — say so
|
|
22
|
+
* }
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
/** A record filter, AND-ed on top of the read rule. */
|
|
26
|
+
export interface KoolbaseAggregateFilter {
|
|
27
|
+
field: string;
|
|
28
|
+
op: 'eq' | 'neq' | 'in' | 'gt' | 'gte' | 'lt' | 'lte';
|
|
29
|
+
value: unknown;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* The category axis: a field's value, or a calendar bucket of a timestamp
|
|
33
|
+
* field.
|
|
34
|
+
*
|
|
35
|
+
* A bucket REQUIRES a timezone, and the union makes omitting it a type
|
|
36
|
+
* error. "Midnight" means nothing without one — Africa/Accra and UTC
|
|
37
|
+
* disagree about which day a 23:30 sale belongs to — and the API refuses a
|
|
38
|
+
* bucket that arrives without one.
|
|
39
|
+
*/
|
|
40
|
+
export type KoolbaseGroupBy = {
|
|
41
|
+
field: string;
|
|
42
|
+
bucket?: never;
|
|
43
|
+
timezone?: never;
|
|
44
|
+
} | {
|
|
45
|
+
field: string;
|
|
46
|
+
bucket: 'day' | 'week' | 'month' | 'year';
|
|
47
|
+
timezone: string;
|
|
48
|
+
};
|
|
49
|
+
/**
|
|
50
|
+
* One number per group.
|
|
51
|
+
*
|
|
52
|
+
* `count` counts RECORDS, so it takes no field and skips nothing. The others
|
|
53
|
+
* read a field, and a record whose value is missing or not a number is
|
|
54
|
+
* skipped and reported in the result's accounting.
|
|
55
|
+
*/
|
|
56
|
+
export type KoolbaseMeasure = {
|
|
57
|
+
aggregate: 'count';
|
|
58
|
+
field?: never;
|
|
59
|
+
as: string;
|
|
60
|
+
} | {
|
|
61
|
+
aggregate: 'sum' | 'avg' | 'min' | 'max';
|
|
62
|
+
field: string;
|
|
63
|
+
as: string;
|
|
64
|
+
};
|
|
65
|
+
/** One aggregation request. */
|
|
66
|
+
export interface KoolbaseAggregateRequest {
|
|
67
|
+
collection: string;
|
|
68
|
+
/** AND-ed record filters on top of the read rule. */
|
|
69
|
+
where?: KoolbaseAggregateFilter[];
|
|
70
|
+
/** Absent means one group: the measures over everything. */
|
|
71
|
+
groupBy?: KoolbaseGroupBy;
|
|
72
|
+
/** At least one. */
|
|
73
|
+
measures: KoolbaseMeasure[];
|
|
74
|
+
}
|
|
75
|
+
/** One category and its numbers. */
|
|
76
|
+
export interface KoolbaseAggregateGroup {
|
|
77
|
+
/** Empty string for the single group when there was no groupBy. */
|
|
78
|
+
category: string;
|
|
79
|
+
/** Keyed by each measure's `as`. Null where a group had nothing to measure. */
|
|
80
|
+
values: Record<string, number | null>;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* How a measure arrived at its number.
|
|
84
|
+
*
|
|
85
|
+
* `skipped` is not an error. It is what you need to decide whether the
|
|
86
|
+
* number means what it appears to mean — a revenue total over 900 of 903
|
|
87
|
+
* orders is a different fact from one over all of them, and only this says
|
|
88
|
+
* which you are looking at.
|
|
89
|
+
*/
|
|
90
|
+
export interface KoolbaseAggregateAccounting {
|
|
91
|
+
counted: number;
|
|
92
|
+
skipped: number;
|
|
93
|
+
/** Present when skipped > 0. */
|
|
94
|
+
skippedReason?: 'missing' | 'not_numeric' | 'mixed';
|
|
95
|
+
}
|
|
96
|
+
/** The whole answer, including what could not be counted. */
|
|
97
|
+
export interface KoolbaseAggregateResult {
|
|
98
|
+
/**
|
|
99
|
+
* One per category, sorted — or exactly one with an empty category when
|
|
100
|
+
* there was no groupBy.
|
|
101
|
+
*/
|
|
102
|
+
groups: KoolbaseAggregateGroup[];
|
|
103
|
+
/** Records that matched the read rule and filters: the denominator. */
|
|
104
|
+
matched: number;
|
|
105
|
+
/** Per measure name, keyed by its `as`. */
|
|
106
|
+
accounting: Record<string, KoolbaseAggregateAccounting>;
|
|
107
|
+
/**
|
|
108
|
+
* True when the groupBy produced more categories than allowed and the
|
|
109
|
+
* whole request was refused.
|
|
110
|
+
*
|
|
111
|
+
* `groups` is EMPTY when this is set: a partial set drawn as the whole
|
|
112
|
+
* would be exactly the wrong number this API exists to prevent.
|
|
113
|
+
*/
|
|
114
|
+
tooManyGroups: boolean;
|
|
115
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Asking "how many" and "how much" over a collection, correctly.
|
|
3
|
+
*
|
|
4
|
+
* Correct means the whole authorized set — never the first page — with the
|
|
5
|
+
* caller's read rule applied inside the query, and never a bare number:
|
|
6
|
+
* collections are schemaless, so a total that quietly excluded three
|
|
7
|
+
* malformed rows would be a wrong number that looks right. Every result
|
|
8
|
+
* carries its accounting.
|
|
9
|
+
*
|
|
10
|
+
* ```ts
|
|
11
|
+
* const r = await Koolbase.db.aggregate({
|
|
12
|
+
* collection: 'orders',
|
|
13
|
+
* groupBy: { field: 'created_at', bucket: 'month', timezone: 'Africa/Accra' },
|
|
14
|
+
* measures: [{ aggregate: 'sum', field: 'total', as: 'revenue' }],
|
|
15
|
+
* });
|
|
16
|
+
*
|
|
17
|
+
* for (const g of r.groups) {
|
|
18
|
+
* console.log(g.category, g.values.revenue);
|
|
19
|
+
* }
|
|
20
|
+
* if (r.accounting.revenue.skipped > 0) {
|
|
21
|
+
* // some orders had a total that was not a number — say so
|
|
22
|
+
* }
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
export {};
|
|
@@ -101,6 +101,14 @@ export declare class NetworkError extends KoolbaseAuthError {
|
|
|
101
101
|
export declare class InvalidPhoneNumberError extends KoolbaseAuthError {
|
|
102
102
|
constructor();
|
|
103
103
|
}
|
|
104
|
+
/**
|
|
105
|
+
* The project has switched off signing in with an emailed code — 403
|
|
106
|
+
* `email_code_disabled`. Applies to every address alike, so it reveals
|
|
107
|
+
* nothing about which accounts exist.
|
|
108
|
+
*/
|
|
109
|
+
export declare class EmailCodeDisabledError extends KoolbaseAuthError {
|
|
110
|
+
constructor();
|
|
111
|
+
}
|
|
104
112
|
export declare class OtpExpiredError extends KoolbaseAuthError {
|
|
105
113
|
constructor();
|
|
106
114
|
}
|
package/dist/esm/auth-errors.js
CHANGED
|
@@ -21,14 +21,14 @@ export class InvalidCredentialsError extends KoolbaseAuthError {
|
|
|
21
21
|
}
|
|
22
22
|
export class EmailAlreadyInUseError extends KoolbaseAuthError {
|
|
23
23
|
constructor() {
|
|
24
|
-
super('Email is already in use', '
|
|
24
|
+
super('Email is already in use', 'email_in_use');
|
|
25
25
|
this.name = 'EmailAlreadyInUseError';
|
|
26
26
|
Object.setPrototypeOf(this, EmailAlreadyInUseError.prototype);
|
|
27
27
|
}
|
|
28
28
|
}
|
|
29
29
|
export class UserDisabledError extends KoolbaseAuthError {
|
|
30
30
|
constructor() {
|
|
31
|
-
super('This account has been disabled', '
|
|
31
|
+
super('This account has been disabled', 'account_disabled');
|
|
32
32
|
this.name = 'UserDisabledError';
|
|
33
33
|
Object.setPrototypeOf(this, UserDisabledError.prototype);
|
|
34
34
|
}
|
|
@@ -49,7 +49,7 @@ export class WeakPasswordError extends KoolbaseAuthError {
|
|
|
49
49
|
// ─── Session lifecycle ─────────────────────────────────────────────────────
|
|
50
50
|
export class SessionExpiredError extends KoolbaseAuthError {
|
|
51
51
|
constructor() {
|
|
52
|
-
super('Session expired, please log in again', '
|
|
52
|
+
super('Session expired, please log in again', 'invalid_refresh_token');
|
|
53
53
|
this.name = 'SessionExpiredError';
|
|
54
54
|
Object.setPrototypeOf(this, SessionExpiredError.prototype);
|
|
55
55
|
}
|
|
@@ -95,7 +95,7 @@ export class AccountLockedError extends KoolbaseAuthError {
|
|
|
95
95
|
*/
|
|
96
96
|
export class UnlockTokenInvalidError extends KoolbaseAuthError {
|
|
97
97
|
constructor() {
|
|
98
|
-
super('Unlock link is invalid or has expired', '
|
|
98
|
+
super('Unlock link is invalid or has expired', 'invalid_unlock_token');
|
|
99
99
|
this.name = 'UnlockTokenInvalidError';
|
|
100
100
|
Object.setPrototypeOf(this, UnlockTokenInvalidError.prototype);
|
|
101
101
|
}
|
|
@@ -162,6 +162,18 @@ export class InvalidPhoneNumberError extends KoolbaseAuthError {
|
|
|
162
162
|
Object.setPrototypeOf(this, InvalidPhoneNumberError.prototype);
|
|
163
163
|
}
|
|
164
164
|
}
|
|
165
|
+
/**
|
|
166
|
+
* The project has switched off signing in with an emailed code — 403
|
|
167
|
+
* `email_code_disabled`. Applies to every address alike, so it reveals
|
|
168
|
+
* nothing about which accounts exist.
|
|
169
|
+
*/
|
|
170
|
+
export class EmailCodeDisabledError extends KoolbaseAuthError {
|
|
171
|
+
constructor() {
|
|
172
|
+
super('Signing in with an emailed code is switched off for this project', 'email_code_disabled');
|
|
173
|
+
this.name = 'EmailCodeDisabledError';
|
|
174
|
+
Object.setPrototypeOf(this, EmailCodeDisabledError.prototype);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
165
177
|
export class OtpExpiredError extends KoolbaseAuthError {
|
|
166
178
|
constructor() {
|
|
167
179
|
super('OTP has expired, please request a new code', 'otp_expired');
|
|
@@ -185,42 +197,42 @@ export class OtpMaxAttemptsError extends KoolbaseAuthError {
|
|
|
185
197
|
}
|
|
186
198
|
export class OtpRateLimitError extends KoolbaseAuthError {
|
|
187
199
|
constructor() {
|
|
188
|
-
super('Too many OTP requests, please wait before trying again', '
|
|
200
|
+
super('Too many OTP requests, please wait before trying again', 'rate_limit');
|
|
189
201
|
this.name = 'OtpRateLimitError';
|
|
190
202
|
Object.setPrototypeOf(this, OtpRateLimitError.prototype);
|
|
191
203
|
}
|
|
192
204
|
}
|
|
193
205
|
export class PhoneAlreadyLinkedError extends KoolbaseAuthError {
|
|
194
206
|
constructor() {
|
|
195
|
-
super('Phone number is already associated with another account', '
|
|
207
|
+
super('Phone number is already associated with another account', 'phone_in_use');
|
|
196
208
|
this.name = 'PhoneAlreadyLinkedError';
|
|
197
209
|
Object.setPrototypeOf(this, PhoneAlreadyLinkedError.prototype);
|
|
198
210
|
}
|
|
199
211
|
}
|
|
200
212
|
export class SmsConfigMissingError extends KoolbaseAuthError {
|
|
201
213
|
constructor() {
|
|
202
|
-
super('SMS provider not configured for this project', '
|
|
214
|
+
super('SMS provider not configured for this project', 'sms_not_configured');
|
|
203
215
|
this.name = 'SmsConfigMissingError';
|
|
204
216
|
Object.setPrototypeOf(this, SmsConfigMissingError.prototype);
|
|
205
217
|
}
|
|
206
218
|
}
|
|
207
219
|
export class AppleSignInNotConfiguredError extends KoolbaseAuthError {
|
|
208
220
|
constructor() {
|
|
209
|
-
super('Apple Sign-In is not configured for this environment', '
|
|
221
|
+
super('Apple Sign-In is not configured for this environment', 'oauth_not_configured');
|
|
210
222
|
this.name = 'AppleSignInNotConfiguredError';
|
|
211
223
|
Object.setPrototypeOf(this, AppleSignInNotConfiguredError.prototype);
|
|
212
224
|
}
|
|
213
225
|
}
|
|
214
226
|
export class InvalidAppleTokenError extends KoolbaseAuthError {
|
|
215
227
|
constructor() {
|
|
216
|
-
super('Invalid Apple identity token', '
|
|
228
|
+
super('Invalid Apple identity token', 'invalid_oauth_token');
|
|
217
229
|
this.name = 'InvalidAppleTokenError';
|
|
218
230
|
Object.setPrototypeOf(this, InvalidAppleTokenError.prototype);
|
|
219
231
|
}
|
|
220
232
|
}
|
|
221
233
|
export class AppleEmailRequiredError extends KoolbaseAuthError {
|
|
222
234
|
constructor() {
|
|
223
|
-
super('Apple did not return email for this sign-in. Revoke this app in iOS Settings → Apple ID and retry.', '
|
|
235
|
+
super('Apple did not return email for this sign-in. Revoke this app in iOS Settings → Apple ID and retry.', 'oauth_email_required');
|
|
224
236
|
this.name = 'AppleEmailRequiredError';
|
|
225
237
|
Object.setPrototypeOf(this, AppleEmailRequiredError.prototype);
|
|
226
238
|
}
|
|
@@ -234,7 +246,7 @@ export class OAuthEmailConflictError extends KoolbaseAuthError {
|
|
|
234
246
|
}
|
|
235
247
|
export class GoogleSignInNotConfiguredError extends KoolbaseAuthError {
|
|
236
248
|
constructor() {
|
|
237
|
-
super('Google Sign-In is not configured for this environment', '
|
|
249
|
+
super('Google Sign-In is not configured for this environment', 'oauth_not_configured');
|
|
238
250
|
this.name = 'GoogleSignInNotConfiguredError';
|
|
239
251
|
Object.setPrototypeOf(this, GoogleSignInNotConfiguredError.prototype);
|
|
240
252
|
}
|
|
@@ -414,14 +426,14 @@ export class MalformedSessionResponseError extends KoolbaseAuthError {
|
|
|
414
426
|
}
|
|
415
427
|
export class InvalidGoogleTokenError extends KoolbaseAuthError {
|
|
416
428
|
constructor() {
|
|
417
|
-
super('Invalid Google identity token', '
|
|
429
|
+
super('Invalid Google identity token', 'invalid_oauth_token');
|
|
418
430
|
this.name = 'InvalidGoogleTokenError';
|
|
419
431
|
Object.setPrototypeOf(this, InvalidGoogleTokenError.prototype);
|
|
420
432
|
}
|
|
421
433
|
}
|
|
422
434
|
export class GoogleEmailRequiredError extends KoolbaseAuthError {
|
|
423
435
|
constructor() {
|
|
424
|
-
super('Google did not return email for this sign-in. Ensure the email scope is requested in the native flow.', '
|
|
436
|
+
super('Google did not return email for this sign-in. Ensure the email scope is requested in the native flow.', 'oauth_email_required');
|
|
425
437
|
this.name = 'GoogleEmailRequiredError';
|
|
426
438
|
Object.setPrototypeOf(this, GoogleEmailRequiredError.prototype);
|
|
427
439
|
}
|