backd-js 0.1.15 → 0.2.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/LICENSE +21 -201
- package/README.md +32 -26
- package/package.json +47 -40
- package/src/admin.js +597 -0
- package/src/auth.js +374 -0
- package/src/client.js +214 -0
- package/src/data.js +278 -0
- package/src/errors.js +135 -0
- package/src/functions.js +143 -0
- package/src/index.js +51 -0
- package/src/storage.js +43 -0
- package/types/admin.d.ts +709 -0
- package/types/auth.d.ts +281 -0
- package/types/client.d.ts +132 -0
- package/types/data.d.ts +260 -0
- package/types/errors.d.ts +106 -0
- package/types/functions.d.ts +135 -0
- package/types/index.d.ts +59 -0
- package/types/storage.d.ts +26 -0
- package/.babelrc +0 -4
- package/.editorconfig +0 -12
- package/.eslintrc.js +0 -28
- package/.npmignore +0 -9
- package/.nvmrc +0 -1
- package/.travis.yml +0 -33
- package/lib/backd.js +0 -13625
- package/lib/backd.js.map +0 -1
- package/lib/backd.min.js +0 -7
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @typedef {object} ErrorDetail
|
|
3
|
+
* @property {string} path Field or parameter the problem is about.
|
|
4
|
+
* @property {string} reason What's wrong with it.
|
|
5
|
+
*/
|
|
6
|
+
export type ErrorDetail = {
|
|
7
|
+
/**
|
|
8
|
+
* Field or parameter the problem is about.
|
|
9
|
+
*/
|
|
10
|
+
path: string;
|
|
11
|
+
/**
|
|
12
|
+
* What's wrong with it.
|
|
13
|
+
*/
|
|
14
|
+
reason: string;
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* An error answered by backd, or a network failure (status 0).
|
|
18
|
+
* `code` is stable and meant for programs; `message` is for humans.
|
|
19
|
+
*/
|
|
20
|
+
export declare class BackdError extends Error {
|
|
21
|
+
/** @type {number} */
|
|
22
|
+
status: number;
|
|
23
|
+
/** @type {string} */
|
|
24
|
+
code: string;
|
|
25
|
+
/** @type {ErrorDetail[]} */
|
|
26
|
+
details: ErrorDetail[];
|
|
27
|
+
/** @type {string | undefined} */
|
|
28
|
+
requestId: string | undefined;
|
|
29
|
+
/**
|
|
30
|
+
* @param {object} init
|
|
31
|
+
* @param {number} init.status HTTP status; 0 for network failures.
|
|
32
|
+
* @param {string} init.code backd error code, e.g. "validation_error".
|
|
33
|
+
* @param {string} init.message
|
|
34
|
+
* @param {ErrorDetail[]} [init.details]
|
|
35
|
+
* @param {string} [init.requestId] The request ID, to match server logs.
|
|
36
|
+
* @param {unknown} [init.cause]
|
|
37
|
+
*/
|
|
38
|
+
constructor({ status, code, message, details, requestId, cause }: {
|
|
39
|
+
status: number;
|
|
40
|
+
code: string;
|
|
41
|
+
message: string;
|
|
42
|
+
details?: ErrorDetail[];
|
|
43
|
+
requestId?: string;
|
|
44
|
+
cause?: unknown;
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
/** 400: invalid body, query or header (`validation_error`, `invalid_json`, `invalid_query`, `invalid_header`). */
|
|
48
|
+
export declare class ValidationError extends BackdError {
|
|
49
|
+
}
|
|
50
|
+
/** 401: missing, invalid or expired credentials, or a wrong email or password. */
|
|
51
|
+
export declare class AuthenticationError extends BackdError {
|
|
52
|
+
}
|
|
53
|
+
/** 403: the caller isn't allowed to do this. */
|
|
54
|
+
export declare class ForbiddenError extends BackdError {
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Not an HTTP error: a sign-up in a realm that requires verified addresses
|
|
58
|
+
* answers `202`, because the account exists but there is no session. Thrown
|
|
59
|
+
* by `auth.signup()` so the code after it, which expects a session, doesn't run.
|
|
60
|
+
*/
|
|
61
|
+
export declare class VerificationRequiredError extends BackdError {
|
|
62
|
+
}
|
|
63
|
+
/** 404: unknown realm, collection, document, user or session. */
|
|
64
|
+
export declare class NotFoundError extends BackdError {
|
|
65
|
+
}
|
|
66
|
+
/** 409: a unique value already exists (`conflict`, `email_taken`) or concurrent writes (`write_conflict`). */
|
|
67
|
+
export declare class ConflictError extends BackdError {
|
|
68
|
+
}
|
|
69
|
+
/** 412: `If-Match` didn't match the current version. */
|
|
70
|
+
export declare class VersionMismatchError extends BackdError {
|
|
71
|
+
}
|
|
72
|
+
/** 429 and 503: try again later; `retryAfter` is in milliseconds when the server said. */
|
|
73
|
+
export declare class RetryableError extends BackdError {
|
|
74
|
+
/** @type {number | undefined} */
|
|
75
|
+
retryAfter: number | undefined;
|
|
76
|
+
/**
|
|
77
|
+
* @param {ConstructorParameters<typeof BackdError>[0] & { retryAfter?: number }} init
|
|
78
|
+
*/
|
|
79
|
+
constructor(init: ConstructorParameters<typeof BackdError>[0] & {
|
|
80
|
+
retryAfter?: number;
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
/** 0: the request didn't get an answer (network error, abort, timeout). */
|
|
84
|
+
export declare class NetworkError extends BackdError {
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* The `BackdError` subclass for a status, or the base class if there's
|
|
88
|
+
* no specific one for it. Used for real responses (`errorFromResponse`)
|
|
89
|
+
* and to reconstruct the same shape from an async job's stored result,
|
|
90
|
+
* which never went through an HTTP response of its own (roadmap F14).
|
|
91
|
+
* @param {number} status
|
|
92
|
+
* @returns {typeof BackdError}
|
|
93
|
+
*/
|
|
94
|
+
export declare function errorClassFor(status: number): typeof BackdError;
|
|
95
|
+
/**
|
|
96
|
+
* Builds the error for a non-2xx response.
|
|
97
|
+
* @param {Response} res
|
|
98
|
+
* @returns {Promise<BackdError>}
|
|
99
|
+
*/
|
|
100
|
+
export declare function errorFromResponse(res: Response): Promise<BackdError>;
|
|
101
|
+
/**
|
|
102
|
+
* Parses a Retry-After header (seconds or an HTTP date) into milliseconds.
|
|
103
|
+
* @param {string | null} value
|
|
104
|
+
* @returns {number | undefined}
|
|
105
|
+
*/
|
|
106
|
+
export declare function parseRetryAfter(value: string | null): number | undefined;
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
export type Client = import('./client.js').Client;
|
|
2
|
+
export type RequestOptions = import('./client.js').RequestOptions;
|
|
3
|
+
export type ErrorDetail = import('./errors.js').ErrorDetail;
|
|
4
|
+
export type JobResult = {
|
|
5
|
+
/**
|
|
6
|
+
* `ok`, `function_error`, `timeout`, `memory`, `cpu`, `crash`, `output_too_large` or `bundle`.
|
|
7
|
+
*/
|
|
8
|
+
status: string;
|
|
9
|
+
/**
|
|
10
|
+
* The function's output, when `status` is `ok`.
|
|
11
|
+
*/
|
|
12
|
+
output?: unknown;
|
|
13
|
+
/**
|
|
14
|
+
* The status a `sync` call would have answered with; absent only when `status` is `ok`.
|
|
15
|
+
*/
|
|
16
|
+
http_status?: number;
|
|
17
|
+
/**
|
|
18
|
+
* The function's own code (`function_error`) or one of backd's own; absent only when `status` is `ok`.
|
|
19
|
+
*/
|
|
20
|
+
code?: string;
|
|
21
|
+
/**
|
|
22
|
+
* Absent only when `status` is `ok`.
|
|
23
|
+
*/
|
|
24
|
+
message?: string;
|
|
25
|
+
details?: ErrorDetail[];
|
|
26
|
+
duration_ms: number;
|
|
27
|
+
};
|
|
28
|
+
export type JobData = {
|
|
29
|
+
id: string;
|
|
30
|
+
/**
|
|
31
|
+
* `<database>/<name>`.
|
|
32
|
+
*/
|
|
33
|
+
function: string;
|
|
34
|
+
status: 'queued' | 'running' | 'done';
|
|
35
|
+
created_at: string;
|
|
36
|
+
/**
|
|
37
|
+
* How many times a worker has started it.
|
|
38
|
+
*/
|
|
39
|
+
attempts?: number;
|
|
40
|
+
/**
|
|
41
|
+
* When a failed attempt will be retried (the function's `retry` policy); null otherwise.
|
|
42
|
+
*/
|
|
43
|
+
next_attempt_at?: string | null;
|
|
44
|
+
result: JobResult | null;
|
|
45
|
+
};
|
|
46
|
+
/**
|
|
47
|
+
* @typedef {import('./client.js').Client} Client
|
|
48
|
+
* @typedef {import('./client.js').RequestOptions} RequestOptions
|
|
49
|
+
* @typedef {import('./errors.js').ErrorDetail} ErrorDetail
|
|
50
|
+
*/
|
|
51
|
+
/**
|
|
52
|
+
* How an `async` job ended, once `status` is `'done'` — the same shape
|
|
53
|
+
* a `sync` call's own answer would carry for the same outcome.
|
|
54
|
+
* @typedef {object} JobResult
|
|
55
|
+
* @property {string} status `ok`, `function_error`, `timeout`, `memory`, `cpu`, `crash`, `output_too_large` or `bundle`.
|
|
56
|
+
* @property {unknown} [output] The function's output, when `status` is `ok`.
|
|
57
|
+
* @property {number} [http_status] The status a `sync` call would have answered with; absent only when `status` is `ok`.
|
|
58
|
+
* @property {string} [code] The function's own code (`function_error`) or one of backd's own; absent only when `status` is `ok`.
|
|
59
|
+
* @property {string} [message] Absent only when `status` is `ok`.
|
|
60
|
+
* @property {ErrorDetail[]} [details]
|
|
61
|
+
* @property {number} duration_ms
|
|
62
|
+
*/
|
|
63
|
+
/**
|
|
64
|
+
* A job as the API returns it: `POST .../_func/{name}`'s `202` body, or `GET .../_jobs/{id}`.
|
|
65
|
+
* @typedef {object} JobData
|
|
66
|
+
* @property {string} id
|
|
67
|
+
* @property {string} function `<database>/<name>`.
|
|
68
|
+
* @property {'queued' | 'running' | 'done'} status
|
|
69
|
+
* @property {string} created_at
|
|
70
|
+
* @property {number} [attempts] How many times a worker has started it.
|
|
71
|
+
* @property {string | null} [next_attempt_at] When a failed attempt will be retried (the function's `retry` policy); null otherwise.
|
|
72
|
+
* @property {JobResult | null} result
|
|
73
|
+
*/
|
|
74
|
+
/**
|
|
75
|
+
* `job.wait()` gave up before the job finished (roadmap F14) — the job
|
|
76
|
+
* itself is unaffected and still running; call `wait()` again, or poll
|
|
77
|
+
* with `status()`, whenever you like.
|
|
78
|
+
*/
|
|
79
|
+
export declare class JobTimeoutError extends Error {
|
|
80
|
+
/** @readonly */
|
|
81
|
+
jobId: string;
|
|
82
|
+
/** @param {string} jobId */
|
|
83
|
+
constructor(jobId: string);
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* A handle to an `async` function's job (roadmap F11, this client since
|
|
87
|
+
* F14): `db.fn(name, input)` returns one instead of the output directly
|
|
88
|
+
* when the function is `async`. Poll with `status()`, or use `wait()`
|
|
89
|
+
* to get the output the same way a `sync` call's return value works.
|
|
90
|
+
*/
|
|
91
|
+
export declare class Job {
|
|
92
|
+
/** @internal */
|
|
93
|
+
client: import("./client.js").Client;
|
|
94
|
+
/** @internal */
|
|
95
|
+
database: string;
|
|
96
|
+
/** @readonly */
|
|
97
|
+
id: string;
|
|
98
|
+
/** @readonly The function this job runs, as `<database>/<name>`. */
|
|
99
|
+
function: string;
|
|
100
|
+
/** @internal */
|
|
101
|
+
data: JobData;
|
|
102
|
+
/**
|
|
103
|
+
* @param {Client} client
|
|
104
|
+
* @param {string} database
|
|
105
|
+
* @param {JobData} data
|
|
106
|
+
*/
|
|
107
|
+
constructor(client: Client, database: string, data: JobData);
|
|
108
|
+
/** The job's data as of the last `status()` or `wait()` call, or when it was created. */
|
|
109
|
+
get raw(): JobData;
|
|
110
|
+
/**
|
|
111
|
+
* Polls the job's current status.
|
|
112
|
+
* @param {RequestOptions} [opts]
|
|
113
|
+
* @returns {Promise<'queued' | 'running' | 'done'>}
|
|
114
|
+
*/
|
|
115
|
+
status(opts?: RequestOptions): Promise<'queued' | 'running' | 'done'>;
|
|
116
|
+
/**
|
|
117
|
+
* Polls until the job is done, then returns its output — the same
|
|
118
|
+
* value a `sync` call would have returned — or throws a `BackdError`
|
|
119
|
+
* matching what a `sync` call would have thrown for the same outcome
|
|
120
|
+
* (the function's own `code`, when it threw `ctx.error(...)`).
|
|
121
|
+
* @param {RequestOptions & { pollIntervalMs?: number, timeoutMs?: number }} [opts]
|
|
122
|
+
* `pollIntervalMs` default 500. Without `timeoutMs`, waits indefinitely.
|
|
123
|
+
* @returns {Promise<unknown>}
|
|
124
|
+
*/
|
|
125
|
+
wait(opts?: RequestOptions & {
|
|
126
|
+
pollIntervalMs?: number;
|
|
127
|
+
timeoutMs?: number;
|
|
128
|
+
}): Promise<unknown>;
|
|
129
|
+
/**
|
|
130
|
+
* @internal
|
|
131
|
+
* @param {RequestOptions} [opts]
|
|
132
|
+
* @returns {Promise<JobData>}
|
|
133
|
+
*/
|
|
134
|
+
_fetch(opts?: RequestOptions): Promise<JobData>;
|
|
135
|
+
}
|
package/types/index.d.ts
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
export { createClient, Client } from './client.js';
|
|
2
|
+
export { Auth } from './auth.js';
|
|
3
|
+
export { Database, Collection, ifMatchValue } from './data.js';
|
|
4
|
+
export { Admin } from './admin.js';
|
|
5
|
+
export { Job, JobTimeoutError } from './functions.js';
|
|
6
|
+
export { BackdError, ValidationError, AuthenticationError, ForbiddenError, NotFoundError, ConflictError, VersionMismatchError, RetryableError, NetworkError, VerificationRequiredError, } from './errors.js';
|
|
7
|
+
export { memoryStorage, localStorageStorage } from './storage.js';
|
|
8
|
+
export type ClientOptions = import('./client.js').ClientOptions;
|
|
9
|
+
export type RequestOptions = import('./client.js').RequestOptions;
|
|
10
|
+
export type RetryOptions = import('./client.js').RetryOptions;
|
|
11
|
+
export type TokenStorage = import('./storage.js').TokenStorage;
|
|
12
|
+
export type User = import('./auth.js').User;
|
|
13
|
+
export type Session = import('./auth.js').Session;
|
|
14
|
+
export type SessionInfo = import('./auth.js').SessionInfo;
|
|
15
|
+
export type AuthEvent = import('./auth.js').AuthEvent;
|
|
16
|
+
export type ErrorDetail = import('./errors.js').ErrorDetail;
|
|
17
|
+
export type Meta = import('./data.js').Meta;
|
|
18
|
+
export type ListParams = import('./data.js').ListParams;
|
|
19
|
+
export type WriteOptions = import('./data.js').WriteOptions;
|
|
20
|
+
export type AdminUser = import('./admin.js').AdminUser;
|
|
21
|
+
export type UserPage = import('./admin.js').UserPage;
|
|
22
|
+
export type Invitation = import('./admin.js').Invitation;
|
|
23
|
+
export type NewInvitation = import('./admin.js').NewInvitation;
|
|
24
|
+
export type SentInvitation = import('./admin.js').SentInvitation;
|
|
25
|
+
export type OwnedReport = import('./admin.js').OwnedReport;
|
|
26
|
+
export type JobData = import('./functions.js').JobData;
|
|
27
|
+
export type JobResult = import('./functions.js').JobResult;
|
|
28
|
+
export type Doc<T extends object = Record<string, any>> = import('./data.js').Doc<T>;
|
|
29
|
+
export type Page<T extends object = Record<string, any>> = import('./data.js').Page<T>;
|
|
30
|
+
/**
|
|
31
|
+
* @typedef {import('./client.js').ClientOptions} ClientOptions
|
|
32
|
+
* @typedef {import('./client.js').RequestOptions} RequestOptions
|
|
33
|
+
* @typedef {import('./client.js').RetryOptions} RetryOptions
|
|
34
|
+
* @typedef {import('./storage.js').TokenStorage} TokenStorage
|
|
35
|
+
* @typedef {import('./auth.js').User} User
|
|
36
|
+
* @typedef {import('./auth.js').Session} Session
|
|
37
|
+
* @typedef {import('./auth.js').SessionInfo} SessionInfo
|
|
38
|
+
* @typedef {import('./auth.js').AuthEvent} AuthEvent
|
|
39
|
+
* @typedef {import('./errors.js').ErrorDetail} ErrorDetail
|
|
40
|
+
* @typedef {import('./data.js').Meta} Meta
|
|
41
|
+
* @typedef {import('./data.js').ListParams} ListParams
|
|
42
|
+
* @typedef {import('./data.js').WriteOptions} WriteOptions
|
|
43
|
+
* @typedef {import('./admin.js').AdminUser} AdminUser
|
|
44
|
+
* @typedef {import('./admin.js').UserPage} UserPage
|
|
45
|
+
* @typedef {import('./admin.js').Invitation} Invitation
|
|
46
|
+
* @typedef {import('./admin.js').NewInvitation} NewInvitation
|
|
47
|
+
* @typedef {import('./admin.js').SentInvitation} SentInvitation
|
|
48
|
+
* @typedef {import('./admin.js').OwnedReport} OwnedReport
|
|
49
|
+
* @typedef {import('./functions.js').JobData} JobData
|
|
50
|
+
* @typedef {import('./functions.js').JobResult} JobResult
|
|
51
|
+
*/
|
|
52
|
+
/**
|
|
53
|
+
* @template {object} [T=Record<string, any>]
|
|
54
|
+
* @typedef {import('./data.js').Doc<T>} Doc
|
|
55
|
+
*/
|
|
56
|
+
/**
|
|
57
|
+
* @template {object} [T=Record<string, any>]
|
|
58
|
+
* @typedef {import('./data.js').Page<T>} Page
|
|
59
|
+
*/
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where the client keeps the session token. Methods may be async.
|
|
3
|
+
* @typedef {object} TokenStorage
|
|
4
|
+
* @property {() => string | null | undefined | Promise<string | null | undefined>} get
|
|
5
|
+
* @property {(token: string) => void | Promise<void>} set
|
|
6
|
+
* @property {() => void | Promise<void>} remove
|
|
7
|
+
*/
|
|
8
|
+
export type TokenStorage = {
|
|
9
|
+
get: () => string | null | undefined | Promise<string | null | undefined>;
|
|
10
|
+
set: (token: string) => void | Promise<void>;
|
|
11
|
+
remove: () => void | Promise<void>;
|
|
12
|
+
};
|
|
13
|
+
/**
|
|
14
|
+
* Keeps the token in memory: it is lost on reload, and scripts on the page
|
|
15
|
+
* can't read it from storage. The default.
|
|
16
|
+
* @returns {TokenStorage}
|
|
17
|
+
*/
|
|
18
|
+
export declare function memoryStorage(): TokenStorage;
|
|
19
|
+
/**
|
|
20
|
+
* Keeps the token in `localStorage`, so sessions survive reloads. Any
|
|
21
|
+
* script running on the page (including injected ones) can read it:
|
|
22
|
+
* protect the app against cross-site scripting.
|
|
23
|
+
* @param {string} [key] Storage key; defaults to "backd.session".
|
|
24
|
+
* @returns {TokenStorage}
|
|
25
|
+
*/
|
|
26
|
+
export declare function localStorageStorage(key?: string): TokenStorage;
|
package/.babelrc
DELETED
package/.editorconfig
DELETED
package/.eslintrc.js
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
// https://eslint.org/docs/user-guide/configuring
|
|
2
|
-
|
|
3
|
-
module.exports = {
|
|
4
|
-
root: true,
|
|
5
|
-
parser: 'babel-eslint',
|
|
6
|
-
parserOptions: {
|
|
7
|
-
sourceType: 'module'
|
|
8
|
-
},
|
|
9
|
-
env: {
|
|
10
|
-
browser: true,
|
|
11
|
-
es6: true,
|
|
12
|
-
node: true
|
|
13
|
-
},
|
|
14
|
-
// https://github.com/standard/standard/blob/master/docs/RULES-en.md
|
|
15
|
-
extends: 'standard',
|
|
16
|
-
// required to lint *.vue files
|
|
17
|
-
plugins: [
|
|
18
|
-
],
|
|
19
|
-
// add your custom rules here
|
|
20
|
-
rules: {
|
|
21
|
-
// allow async-await
|
|
22
|
-
'generator-star-spacing': 'off',
|
|
23
|
-
// allow debugger during development
|
|
24
|
-
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
|
|
25
|
-
'padded-blocks': 'off',
|
|
26
|
-
'no-unused-expressions': 'off'
|
|
27
|
-
}
|
|
28
|
-
}
|
package/.npmignore
DELETED
package/.nvmrc
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
v6.10
|
package/.travis.yml
DELETED
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
language: node_js
|
|
2
|
-
node_js:
|
|
3
|
-
- "node"
|
|
4
|
-
- "7"
|
|
5
|
-
- "6"
|
|
6
|
-
|
|
7
|
-
sudo: required
|
|
8
|
-
services:
|
|
9
|
-
- docker
|
|
10
|
-
|
|
11
|
-
env:
|
|
12
|
-
COMPOSE_VERSION: 1.16.0
|
|
13
|
-
|
|
14
|
-
before_install:
|
|
15
|
-
- curl -L https://github.com/docker/compose/releases/download/${COMPOSE_VERSION}/docker-compose-`uname -s`-`uname -m` > docker-compose
|
|
16
|
-
- chmod +x docker-compose
|
|
17
|
-
- sudo mv docker-compose /usr/local/bin
|
|
18
|
-
- docker-compose up -d mongodb
|
|
19
|
-
- docker-compose run -e ADMIN_NAME -e ADMIN_EMAIL -e ADMIN_PASSWORD bootstrap
|
|
20
|
-
- docker-compose up -d api
|
|
21
|
-
|
|
22
|
-
script:
|
|
23
|
-
- npm run test
|
|
24
|
-
|
|
25
|
-
after_install:
|
|
26
|
-
- docker-compose down
|
|
27
|
-
|
|
28
|
-
notifications:
|
|
29
|
-
email:
|
|
30
|
-
on_success: always
|
|
31
|
-
on_failure: always
|
|
32
|
-
recipients:
|
|
33
|
-
- antoniofernandezvara+backd@gmail.com
|