@qlever-llc/trellis 0.5.1
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/README.md +9 -0
- package/esm/_dnt.polyfills.d.ts +7 -0
- package/esm/_dnt.polyfills.d.ts.map +1 -0
- package/esm/_dnt.polyfills.js +1 -0
- package/esm/package.json +3 -0
- package/esm/trellis/browser.d.ts +11 -0
- package/esm/trellis/browser.d.ts.map +1 -0
- package/esm/trellis/browser.js +10 -0
- package/esm/trellis/client.d.ts +31 -0
- package/esm/trellis/client.d.ts.map +1 -0
- package/esm/trellis/client.js +13 -0
- package/esm/trellis/codec.d.ts +12 -0
- package/esm/trellis/codec.d.ts.map +1 -0
- package/esm/trellis/codec.js +60 -0
- package/esm/trellis/env.d.ts +2 -0
- package/esm/trellis/env.d.ts.map +1 -0
- package/esm/trellis/env.js +1 -0
- package/esm/trellis/errors/AuthError.d.ts +30 -0
- package/esm/trellis/errors/AuthError.d.ts.map +1 -0
- package/esm/trellis/errors/AuthError.js +65 -0
- package/esm/trellis/errors/KVError.d.ts +31 -0
- package/esm/trellis/errors/KVError.d.ts.map +1 -0
- package/esm/trellis/errors/KVError.js +46 -0
- package/esm/trellis/errors/RemoteError.d.ts +47 -0
- package/esm/trellis/errors/RemoteError.d.ts.map +1 -0
- package/esm/trellis/errors/RemoteError.js +80 -0
- package/esm/trellis/errors/TrellisError.d.ts +16 -0
- package/esm/trellis/errors/TrellisError.d.ts.map +1 -0
- package/esm/trellis/errors/TrellisError.js +15 -0
- package/esm/trellis/errors/ValidationError.d.ts +51 -0
- package/esm/trellis/errors/ValidationError.d.ts.map +1 -0
- package/esm/trellis/errors/ValidationError.js +77 -0
- package/esm/trellis/errors/index.d.ts +38 -0
- package/esm/trellis/errors/index.d.ts.map +1 -0
- package/esm/trellis/errors/index.js +26 -0
- package/esm/trellis/globals.d.ts +2 -0
- package/esm/trellis/globals.d.ts.map +1 -0
- package/esm/trellis/globals.js +8 -0
- package/esm/trellis/helpers.d.ts +12 -0
- package/esm/trellis/helpers.d.ts.map +1 -0
- package/esm/trellis/helpers.js +47 -0
- package/esm/trellis/index.d.ts +11 -0
- package/esm/trellis/index.d.ts.map +1 -0
- package/esm/trellis/index.js +6 -0
- package/esm/trellis/kv.d.ts +67 -0
- package/esm/trellis/kv.d.ts.map +1 -0
- package/esm/trellis/kv.js +326 -0
- package/esm/trellis/models/trellis/TrellisError.d.ts +43 -0
- package/esm/trellis/models/trellis/TrellisError.d.ts.map +1 -0
- package/esm/trellis/models/trellis/TrellisError.js +16 -0
- package/esm/trellis/tasks.d.ts +11 -0
- package/esm/trellis/tasks.d.ts.map +1 -0
- package/esm/trellis/tasks.js +41 -0
- package/esm/trellis/tracing.d.ts +5 -0
- package/esm/trellis/tracing.d.ts.map +1 -0
- package/esm/trellis/tracing.js +7 -0
- package/esm/trellis/trellis.d.ts +117 -0
- package/esm/trellis/trellis.d.ts.map +1 -0
- package/esm/trellis/trellis.js +710 -0
- package/package.json +49 -0
- package/script/_dnt.polyfills.d.ts +7 -0
- package/script/_dnt.polyfills.d.ts.map +1 -0
- package/script/_dnt.polyfills.js +2 -0
- package/script/package.json +3 -0
- package/script/trellis/browser.d.ts +11 -0
- package/script/trellis/browser.d.ts.map +1 -0
- package/script/trellis/browser.js +21 -0
- package/script/trellis/client.d.ts +31 -0
- package/script/trellis/client.d.ts.map +1 -0
- package/script/trellis/client.js +16 -0
- package/script/trellis/codec.d.ts +12 -0
- package/script/trellis/codec.d.ts.map +1 -0
- package/script/trellis/codec.js +66 -0
- package/script/trellis/env.d.ts +2 -0
- package/script/trellis/env.d.ts.map +1 -0
- package/script/trellis/env.js +5 -0
- package/script/trellis/errors/AuthError.d.ts +30 -0
- package/script/trellis/errors/AuthError.d.ts.map +1 -0
- package/script/trellis/errors/AuthError.js +72 -0
- package/script/trellis/errors/KVError.d.ts +31 -0
- package/script/trellis/errors/KVError.d.ts.map +1 -0
- package/script/trellis/errors/KVError.js +53 -0
- package/script/trellis/errors/RemoteError.d.ts +47 -0
- package/script/trellis/errors/RemoteError.d.ts.map +1 -0
- package/script/trellis/errors/RemoteError.js +87 -0
- package/script/trellis/errors/TrellisError.d.ts +16 -0
- package/script/trellis/errors/TrellisError.d.ts.map +1 -0
- package/script/trellis/errors/TrellisError.js +19 -0
- package/script/trellis/errors/ValidationError.d.ts +51 -0
- package/script/trellis/errors/ValidationError.d.ts.map +1 -0
- package/script/trellis/errors/ValidationError.js +84 -0
- package/script/trellis/errors/index.d.ts +38 -0
- package/script/trellis/errors/index.d.ts.map +1 -0
- package/script/trellis/errors/index.js +40 -0
- package/script/trellis/globals.d.ts +2 -0
- package/script/trellis/globals.d.ts.map +1 -0
- package/script/trellis/globals.js +11 -0
- package/script/trellis/helpers.d.ts +12 -0
- package/script/trellis/helpers.d.ts.map +1 -0
- package/script/trellis/helpers.js +54 -0
- package/script/trellis/index.d.ts +11 -0
- package/script/trellis/index.d.ts.map +1 -0
- package/script/trellis/index.js +24 -0
- package/script/trellis/kv.d.ts +67 -0
- package/script/trellis/kv.d.ts.map +1 -0
- package/script/trellis/kv.js +354 -0
- package/script/trellis/models/trellis/TrellisError.d.ts +43 -0
- package/script/trellis/models/trellis/TrellisError.d.ts.map +1 -0
- package/script/trellis/models/trellis/TrellisError.js +22 -0
- package/script/trellis/tasks.d.ts +11 -0
- package/script/trellis/tasks.d.ts.map +1 -0
- package/script/trellis/tasks.js +45 -0
- package/script/trellis/tracing.d.ts +5 -0
- package/script/trellis/tracing.d.ts.map +1 -0
- package/script/trellis/tracing.js +49 -0
- package/script/trellis/trellis.d.ts +117 -0
- package/script/trellis/trellis.d.ts.map +1 -0
- package/script/trellis/trellis.js +715 -0
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
|
|
2
|
+
if (kind === "m") throw new TypeError("Private method is not writable");
|
|
3
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
|
4
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
|
5
|
+
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
6
|
+
};
|
|
7
|
+
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
8
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
9
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
10
|
+
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
11
|
+
};
|
|
12
|
+
var _ValidationError_normalizedErrors;
|
|
13
|
+
import Type from "typebox";
|
|
14
|
+
import { TrellisError } from "./TrellisError.js";
|
|
15
|
+
function normalizeError(e) {
|
|
16
|
+
if ("schemaPath" in e) {
|
|
17
|
+
return { schemaPath: e.schemaPath, message: e.message ?? "Invalid value" };
|
|
18
|
+
}
|
|
19
|
+
return { schemaPath: e.path, message: e.message };
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Schema for validation issue in ValidationError.
|
|
23
|
+
*/
|
|
24
|
+
export const ValidationIssueSchema = Type.Object({
|
|
25
|
+
path: Type.String(),
|
|
26
|
+
message: Type.String(),
|
|
27
|
+
});
|
|
28
|
+
/**
|
|
29
|
+
* Schema for ValidationError serialization.
|
|
30
|
+
*/
|
|
31
|
+
export const ValidationErrorDataSchema = Type.Object({
|
|
32
|
+
id: Type.String(),
|
|
33
|
+
type: Type.Literal("ValidationError"),
|
|
34
|
+
message: Type.String(),
|
|
35
|
+
issues: Type.Array(ValidationIssueSchema),
|
|
36
|
+
context: Type.Optional(Type.Record(Type.String(), Type.Unknown())),
|
|
37
|
+
traceId: Type.Optional(Type.String()),
|
|
38
|
+
});
|
|
39
|
+
/**
|
|
40
|
+
* Error for data validation failures.
|
|
41
|
+
* Includes schema validation and missing required data.
|
|
42
|
+
*/
|
|
43
|
+
export class ValidationError extends TrellisError {
|
|
44
|
+
constructor(options) {
|
|
45
|
+
const { errors: rawErrors, ...baseOptions } = options;
|
|
46
|
+
const errors = [...rawErrors].map(normalizeError);
|
|
47
|
+
const msg = errors
|
|
48
|
+
.map((e) => `Validation failed. ${e.schemaPath}: ${e.message}.`)
|
|
49
|
+
.join("\n");
|
|
50
|
+
super(msg.length ? msg : "Data validation failed.", baseOptions);
|
|
51
|
+
Object.defineProperty(this, "name", {
|
|
52
|
+
enumerable: true,
|
|
53
|
+
configurable: true,
|
|
54
|
+
writable: true,
|
|
55
|
+
value: "ValidationError"
|
|
56
|
+
});
|
|
57
|
+
_ValidationError_normalizedErrors.set(this, void 0);
|
|
58
|
+
__classPrivateFieldSet(this, _ValidationError_normalizedErrors, errors, "f");
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Serializes error to a plain object.
|
|
62
|
+
* Transforms internal errors array to issues array for serialization.
|
|
63
|
+
*
|
|
64
|
+
* @returns Plain object representation of the error
|
|
65
|
+
*/
|
|
66
|
+
toSerializable() {
|
|
67
|
+
return {
|
|
68
|
+
...this.baseSerializable(),
|
|
69
|
+
type: this.name,
|
|
70
|
+
issues: __classPrivateFieldGet(this, _ValidationError_normalizedErrors, "f").map((e) => ({
|
|
71
|
+
path: e.schemaPath,
|
|
72
|
+
message: e.message,
|
|
73
|
+
})),
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
_ValidationError_normalizedErrors = new WeakMap();
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { AuthError } from "./AuthError.js";
|
|
2
|
+
import { ValidationError } from "./ValidationError.js";
|
|
3
|
+
import { RemoteError } from "./RemoteError.js";
|
|
4
|
+
import { KVError } from "./KVError.js";
|
|
5
|
+
export { UnexpectedError } from "@qlever-llc/trellis-result";
|
|
6
|
+
export { TrellisError } from "./TrellisError.js";
|
|
7
|
+
export { AuthError } from "./AuthError.js";
|
|
8
|
+
export { ValidationError } from "./ValidationError.js";
|
|
9
|
+
export { RemoteError } from "./RemoteError.js";
|
|
10
|
+
export { KVError } from "./KVError.js";
|
|
11
|
+
export { type AuthErrorData, AuthErrorDataSchema } from "./AuthError.js";
|
|
12
|
+
export { type ValidationErrorData, ValidationErrorDataSchema, type ValidationIssue, ValidationIssueSchema, } from "./ValidationError.js";
|
|
13
|
+
export { type RemoteErrorData, RemoteErrorDataSchema } from "./RemoteError.js";
|
|
14
|
+
export { type KVErrorData, KVErrorDataSchema } from "./KVError.js";
|
|
15
|
+
/**
|
|
16
|
+
* Single source of truth for all Trellis errors.
|
|
17
|
+
* This object is used for compile-time type inference.
|
|
18
|
+
*/
|
|
19
|
+
declare const TRELLIS_ERRORS: {
|
|
20
|
+
readonly UnexpectedError: any;
|
|
21
|
+
readonly AuthError: typeof AuthError;
|
|
22
|
+
readonly ValidationError: typeof ValidationError;
|
|
23
|
+
readonly RemoteError: typeof RemoteError;
|
|
24
|
+
readonly KVError: typeof KVError;
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* Compile-time mapping from error names to their instance types.
|
|
28
|
+
* Derived from TRELLIS_ERRORS to ensure types stay in sync with runtime.
|
|
29
|
+
*/
|
|
30
|
+
export type TrellisErrorMap = {
|
|
31
|
+
[K in keyof typeof TRELLIS_ERRORS]: InstanceType<(typeof TRELLIS_ERRORS)[K]>;
|
|
32
|
+
};
|
|
33
|
+
export type TrellisErrorName = keyof TrellisErrorMap;
|
|
34
|
+
export type TrellisErrorInstance = TrellisErrorMap[TrellisErrorName];
|
|
35
|
+
export type MapErrorNamesToTypes<T extends readonly TrellisErrorName[]> = {
|
|
36
|
+
[K in keyof T]: T[K] extends TrellisErrorName ? TrellisErrorMap[T[K]] : never;
|
|
37
|
+
}[number];
|
|
38
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/trellis/errors/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAEvC,OAAO,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAC7D,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACjD,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAEvC,OAAO,EAAE,KAAK,aAAa,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AACzE,OAAO,EACL,KAAK,mBAAmB,EACxB,yBAAyB,EACzB,KAAK,eAAe,EACpB,qBAAqB,GACtB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,KAAK,eAAe,EAAE,qBAAqB,EAAE,MAAM,kBAAkB,CAAC;AAC/E,OAAO,EAAE,KAAK,WAAW,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAEnE;;;GAGG;AACH,QAAA,MAAM,cAAc;;;;;;CAMV,CAAC;AAEX;;;GAGG;AACH,MAAM,MAAM,eAAe,GAAG;KAC3B,CAAC,IAAI,MAAM,OAAO,cAAc,GAAG,YAAY,CAAC,CAAC,OAAO,cAAc,CAAC,CAAC,CAAC,CAAC,CAAC;CAC7E,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG,MAAM,eAAe,CAAC;AACrD,MAAM,MAAM,oBAAoB,GAAG,eAAe,CAAC,gBAAgB,CAAC,CAAC;AACrE,MAAM,MAAM,oBAAoB,CAAC,CAAC,SAAS,SAAS,gBAAgB,EAAE,IAAI;KACvE,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,gBAAgB,GAAG,eAAe,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,KAAK;CAC9E,CAAC,MAAM,CAAC,CAAC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { UnexpectedError } from "@qlever-llc/trellis-result";
|
|
2
|
+
import { AuthError } from "./AuthError.js";
|
|
3
|
+
import { ValidationError } from "./ValidationError.js";
|
|
4
|
+
import { RemoteError } from "./RemoteError.js";
|
|
5
|
+
import { KVError } from "./KVError.js";
|
|
6
|
+
export { UnexpectedError } from "@qlever-llc/trellis-result";
|
|
7
|
+
export { TrellisError } from "./TrellisError.js";
|
|
8
|
+
export { AuthError } from "./AuthError.js";
|
|
9
|
+
export { ValidationError } from "./ValidationError.js";
|
|
10
|
+
export { RemoteError } from "./RemoteError.js";
|
|
11
|
+
export { KVError } from "./KVError.js";
|
|
12
|
+
export { AuthErrorDataSchema } from "./AuthError.js";
|
|
13
|
+
export { ValidationErrorDataSchema, ValidationIssueSchema, } from "./ValidationError.js";
|
|
14
|
+
export { RemoteErrorDataSchema } from "./RemoteError.js";
|
|
15
|
+
export { KVErrorDataSchema } from "./KVError.js";
|
|
16
|
+
/**
|
|
17
|
+
* Single source of truth for all Trellis errors.
|
|
18
|
+
* This object is used for compile-time type inference.
|
|
19
|
+
*/
|
|
20
|
+
const TRELLIS_ERRORS = {
|
|
21
|
+
UnexpectedError,
|
|
22
|
+
AuthError,
|
|
23
|
+
ValidationError,
|
|
24
|
+
RemoteError,
|
|
25
|
+
KVError,
|
|
26
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"globals.d.ts","sourceRoot":"","sources":["../../src/trellis/globals.ts"],"names":[],"mappings":"AAMA,eAAO,MAAM,MAAM,uCAGjB,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Paginated } from "@qlever-llc/trellis-contracts";
|
|
2
|
+
/**
|
|
3
|
+
* Compute the subject template from the message itself
|
|
4
|
+
*/
|
|
5
|
+
export declare function template(template: string, data: Record<string, string | number | boolean>, opts?: {
|
|
6
|
+
allowWildcards: boolean;
|
|
7
|
+
}): string;
|
|
8
|
+
export declare function escapeNats(token: string): string;
|
|
9
|
+
export declare function escapeKvKey(key: string): string;
|
|
10
|
+
export declare function decodeSubject(token: string): string;
|
|
11
|
+
export declare function makePaginated(offset: number, limit: number, count: number): Paginated;
|
|
12
|
+
//# sourceMappingURL=helpers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../src/trellis/helpers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,+BAA+B,CAAC;AAG/D;;GAEG;AACH,wBAAgB,QAAQ,CACtB,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,EAC/C,IAAI,GAAE;IAAE,cAAc,EAAE,OAAO,CAAA;CAA8B,GAC5D,MAAM,CAWR;AAGD,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAYhD;AAGD,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAW/C;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAUnD;AAED,wBAAgB,aAAa,CAC3B,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,GACZ,SAAS,CAQX"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { Pointer } from "typebox/value";
|
|
2
|
+
/**
|
|
3
|
+
* Compute the subject template from the message itself
|
|
4
|
+
*/
|
|
5
|
+
export function template(template, data, opts = { allowWildcards: false }) {
|
|
6
|
+
return template.replace(/\{([^}]+)\}/g, (_, key) => {
|
|
7
|
+
const token = Pointer.Get(data, key);
|
|
8
|
+
const v = token ? escapeNats(`${token}`) : "*";
|
|
9
|
+
if (!opts.allowWildcards && v === "*") {
|
|
10
|
+
throw new Error("All option templates must have values at runtime.");
|
|
11
|
+
}
|
|
12
|
+
return v;
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
const NATS_SUBJECT_TOKEN_FORBIDDEN = /[\u0000\s.*>~]/gu;
|
|
16
|
+
export function escapeNats(token) {
|
|
17
|
+
const out = token.replace(NATS_SUBJECT_TOKEN_FORBIDDEN, (ch) => `~${ch.codePointAt(0).toString(16).toUpperCase()}~`);
|
|
18
|
+
// Protect start with $ due to NATS internal use of it
|
|
19
|
+
if (out.length === 0 || out.startsWith("$")) {
|
|
20
|
+
return `_${out}`;
|
|
21
|
+
}
|
|
22
|
+
return out;
|
|
23
|
+
}
|
|
24
|
+
const NATS_KV_KEY_FORBIDDEN = /[\u0000\s*>~]/gu;
|
|
25
|
+
export function escapeKvKey(key) {
|
|
26
|
+
const out = key.replace(NATS_KV_KEY_FORBIDDEN, (ch) => `~${ch.codePointAt(0).toString(16).toUpperCase()}~`);
|
|
27
|
+
if (out.length === 0 || out.startsWith("$")) {
|
|
28
|
+
return `_${out}`;
|
|
29
|
+
}
|
|
30
|
+
return out;
|
|
31
|
+
}
|
|
32
|
+
export function decodeSubject(token) {
|
|
33
|
+
const out = token.replace(/~([0-9A-F]{1,6})~/g, (_, hex) => String.fromCodePoint(Number.parseInt(hex, 16)));
|
|
34
|
+
if (out.startsWith("_$")) {
|
|
35
|
+
return out.slice(1);
|
|
36
|
+
}
|
|
37
|
+
return out;
|
|
38
|
+
}
|
|
39
|
+
export function makePaginated(offset, limit, count) {
|
|
40
|
+
return {
|
|
41
|
+
count,
|
|
42
|
+
offset,
|
|
43
|
+
limit,
|
|
44
|
+
next: offset + limit >= count ? undefined : offset + limit,
|
|
45
|
+
prev: offset - limit <= 0 ? undefined : offset - limit,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import "../_dnt.polyfills.js";
|
|
2
|
+
export type { TrellisAPI } from "@qlever-llc/trellis-contracts";
|
|
3
|
+
export { err, isErr, isOk, ok, Result } from "@qlever-llc/trellis-result";
|
|
4
|
+
export type { ClientOpts } from "./client.js";
|
|
5
|
+
export { createClient } from "./client.js";
|
|
6
|
+
export type { TrellisErrorInstance } from "./errors/index.js";
|
|
7
|
+
export { AuthError, KVError, RemoteError, TrellisError, UnexpectedError, ValidationError, } from "./errors/index.js";
|
|
8
|
+
export { TypedKV } from "./kv.js";
|
|
9
|
+
export type { TrellisAuth, TrellisSigner } from "./trellis.js";
|
|
10
|
+
export { Trellis, TrellisServer } from "./trellis.js";
|
|
11
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/trellis/index.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAC9B,YAAY,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAChE,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,4BAA4B,CAAC;AAC1E,YAAY,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,YAAY,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAC9D,OAAO,EACL,SAAS,EACT,OAAO,EACP,WAAW,EACX,YAAY,EACZ,eAAe,EACf,eAAe,GAChB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,YAAY,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC/D,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import "../_dnt.polyfills.js";
|
|
2
|
+
export { err, isErr, isOk, ok, Result } from "@qlever-llc/trellis-result";
|
|
3
|
+
export { createClient } from "./client.js";
|
|
4
|
+
export { AuthError, KVError, RemoteError, TrellisError, UnexpectedError, ValidationError, } from "./errors/index.js";
|
|
5
|
+
export { TypedKV } from "./kv.js";
|
|
6
|
+
export { Trellis, TrellisServer } from "./trellis.js";
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { type KV, type KvEntry } from "@nats-io/kv";
|
|
2
|
+
import type { NatsConnection } from "@nats-io/nats-core/internal";
|
|
3
|
+
import { Result } from "@qlever-llc/trellis-result";
|
|
4
|
+
import type { StaticDecode, TSchema } from "typebox";
|
|
5
|
+
import { KVError, ValidationError } from "./errors/index.js";
|
|
6
|
+
/**
|
|
7
|
+
* Represents a watch event emitted when a KV entry changes.
|
|
8
|
+
*/
|
|
9
|
+
export type WatchEvent<S extends TSchema> = {
|
|
10
|
+
/** The type of change: "update" for new/modified values, "delete" for deletions */
|
|
11
|
+
type: "update" | "delete";
|
|
12
|
+
/** The key that changed */
|
|
13
|
+
key: string;
|
|
14
|
+
/** The new value (only present for update events) */
|
|
15
|
+
value?: StaticDecode<S>;
|
|
16
|
+
/** The revision number of this change */
|
|
17
|
+
revision: number;
|
|
18
|
+
/** The timestamp when this change occurred */
|
|
19
|
+
timestamp: Date;
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* Options for the watch() method.
|
|
23
|
+
*/
|
|
24
|
+
export type WatchOptions = {
|
|
25
|
+
/** If true, include delete events in the watch stream. Defaults to false. */
|
|
26
|
+
includeDeletes?: boolean;
|
|
27
|
+
};
|
|
28
|
+
export declare class TypedKV<S extends TSchema> {
|
|
29
|
+
readonly schema: S;
|
|
30
|
+
readonly kv: KV;
|
|
31
|
+
private constructor();
|
|
32
|
+
static open<S extends TSchema>(nats: NatsConnection, name: string, schema: S, options: {
|
|
33
|
+
history?: number;
|
|
34
|
+
ttl?: number;
|
|
35
|
+
bindOnly?: boolean;
|
|
36
|
+
maxValueBytes?: number;
|
|
37
|
+
}): Promise<Result<TypedKV<S>, KVError>>;
|
|
38
|
+
get(key: string): Promise<Result<TypedKVEntry<S>, KVError | ValidationError>>;
|
|
39
|
+
create(key: string, value: StaticDecode<S>): Promise<Result<void, KVError>>;
|
|
40
|
+
put(key: string, value: StaticDecode<S>): Promise<Result<void, KVError>>;
|
|
41
|
+
delete(key: string): Promise<Result<void, KVError>>;
|
|
42
|
+
keys(filter?: string | string[]): Promise<Result<AsyncIterable<string>, KVError>>;
|
|
43
|
+
status(): Promise<Result<{
|
|
44
|
+
values: number;
|
|
45
|
+
}, KVError>>;
|
|
46
|
+
}
|
|
47
|
+
export declare class TypedKVEntry<S extends TSchema> {
|
|
48
|
+
private schema;
|
|
49
|
+
private kv;
|
|
50
|
+
private entry;
|
|
51
|
+
readonly value: StaticDecode<S>;
|
|
52
|
+
constructor(schema: S, kv: KV, entry: KvEntry, value: StaticDecode<S>);
|
|
53
|
+
static create<S extends TSchema>(schema: S, kv: KV, entry: KvEntry): Promise<Result<TypedKVEntry<S>, ValidationError>>;
|
|
54
|
+
get key(): string;
|
|
55
|
+
/**
|
|
56
|
+
* Watch this KV entry for changes.
|
|
57
|
+
*
|
|
58
|
+
* @param callback - Function called when the entry changes
|
|
59
|
+
* @param opts - Watch options (e.g., includeDeletes)
|
|
60
|
+
* @returns A function to stop watching
|
|
61
|
+
*/
|
|
62
|
+
watch(callback: (event: WatchEvent<S>) => void, opts?: WatchOptions): Promise<() => void>;
|
|
63
|
+
merge(value: Partial<StaticDecode<S>>, vcc?: boolean): Promise<Result<void, KVError | ValidationError>>;
|
|
64
|
+
put(value: StaticDecode<S>, vcc?: boolean): Promise<Result<void, KVError>>;
|
|
65
|
+
delete(vcc?: boolean): Promise<Result<void, KVError>>;
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=kv.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"kv.d.ts","sourceRoot":"","sources":["../../src/trellis/kv.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,326 @@
|
|
|
1
|
+
// @ts-nocheck -- svelte-check hits pathological generic instantiation in this file
|
|
2
|
+
import { Kvm } from "@nats-io/kv";
|
|
3
|
+
import { Result } from "@qlever-llc/trellis-result";
|
|
4
|
+
import { merge } from "ts-deepmerge";
|
|
5
|
+
import Value, { ParseError } from "typebox/value";
|
|
6
|
+
import { KVError, ValidationError } from "./errors/index.js";
|
|
7
|
+
import { decodeSubject, escapeKvKey } from "./helpers.js";
|
|
8
|
+
function externalizeValue(value) {
|
|
9
|
+
if (value instanceof Date) {
|
|
10
|
+
return value.toISOString();
|
|
11
|
+
}
|
|
12
|
+
if (Array.isArray(value)) {
|
|
13
|
+
return value.map(externalizeValue);
|
|
14
|
+
}
|
|
15
|
+
if (value !== null && typeof value === "object") {
|
|
16
|
+
const out = {};
|
|
17
|
+
for (const [key, entry] of Object.entries(value)) {
|
|
18
|
+
if (entry !== undefined) {
|
|
19
|
+
out[key] = externalizeValue(entry);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return out;
|
|
23
|
+
}
|
|
24
|
+
return value;
|
|
25
|
+
}
|
|
26
|
+
function parseExternalValue(schema, value) {
|
|
27
|
+
if (Value.HasCodec(schema)) {
|
|
28
|
+
return Value.Decode(schema, value);
|
|
29
|
+
}
|
|
30
|
+
return Value.Parse(schema, value);
|
|
31
|
+
}
|
|
32
|
+
function serializeValue(schema, value) {
|
|
33
|
+
return JSON.stringify(Value.Parse(schema, externalizeValue(value)));
|
|
34
|
+
}
|
|
35
|
+
function serializeExternalValue(schema, value) {
|
|
36
|
+
return serializeValue(schema, value);
|
|
37
|
+
}
|
|
38
|
+
export class TypedKV {
|
|
39
|
+
constructor(schema, kv) {
|
|
40
|
+
Object.defineProperty(this, "schema", {
|
|
41
|
+
enumerable: true,
|
|
42
|
+
configurable: true,
|
|
43
|
+
writable: true,
|
|
44
|
+
value: schema
|
|
45
|
+
});
|
|
46
|
+
Object.defineProperty(this, "kv", {
|
|
47
|
+
enumerable: true,
|
|
48
|
+
configurable: true,
|
|
49
|
+
writable: true,
|
|
50
|
+
value: kv
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
static async open(nats, name, schema, options) {
|
|
54
|
+
try {
|
|
55
|
+
const kvm = new Kvm(nats);
|
|
56
|
+
const kv = options.bindOnly
|
|
57
|
+
? await kvm.open(name)
|
|
58
|
+
: await kvm.create(name, {
|
|
59
|
+
history: options.history ?? 1,
|
|
60
|
+
ttl: options.ttl ?? 0,
|
|
61
|
+
...(options.maxValueBytes ? { maxValueSize: options.maxValueBytes } : {}),
|
|
62
|
+
});
|
|
63
|
+
const typedKv = new TypedKV(schema, kv);
|
|
64
|
+
return Result.ok(typedKv);
|
|
65
|
+
}
|
|
66
|
+
catch (cause) {
|
|
67
|
+
return Result.err(new KVError({ operation: "open", cause }));
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
async get(key) {
|
|
71
|
+
let s;
|
|
72
|
+
try {
|
|
73
|
+
s = await this.kv.get(escapeKvKey(key));
|
|
74
|
+
}
|
|
75
|
+
catch (cause) {
|
|
76
|
+
return Result.err(new KVError({ operation: "get", cause, context: { key } }));
|
|
77
|
+
}
|
|
78
|
+
if (!s) {
|
|
79
|
+
return Result.err(new KVError({
|
|
80
|
+
operation: "get",
|
|
81
|
+
context: { key, reason: "not found" },
|
|
82
|
+
}));
|
|
83
|
+
}
|
|
84
|
+
return await createTypedKvEntry(this.schema, this.kv, s);
|
|
85
|
+
}
|
|
86
|
+
async create(key, value) {
|
|
87
|
+
const schema = this.schema;
|
|
88
|
+
const rawValue = value;
|
|
89
|
+
// @ts-expect-error svelte-check hits excessive type instantiation here
|
|
90
|
+
const serialized = serializeExternalValue(schema, rawValue);
|
|
91
|
+
try {
|
|
92
|
+
await this.kv.create(escapeKvKey(key), serialized);
|
|
93
|
+
return Result.ok(undefined);
|
|
94
|
+
}
|
|
95
|
+
catch (cause) {
|
|
96
|
+
return Result.err(new KVError({ operation: "create", cause, context: { key } }));
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
async put(key, value) {
|
|
100
|
+
const schema = this.schema;
|
|
101
|
+
const rawValue = value;
|
|
102
|
+
// @ts-expect-error svelte-check hits excessive type instantiation here
|
|
103
|
+
const serialized = serializeExternalValue(schema, rawValue);
|
|
104
|
+
try {
|
|
105
|
+
await this.kv.put(escapeKvKey(key), serialized);
|
|
106
|
+
return Result.ok(undefined);
|
|
107
|
+
}
|
|
108
|
+
catch (cause) {
|
|
109
|
+
return Result.err(new KVError({ operation: "put", cause, context: { key } }));
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
async delete(key) {
|
|
113
|
+
try {
|
|
114
|
+
await this.kv.delete(escapeKvKey(key));
|
|
115
|
+
return Result.ok(undefined);
|
|
116
|
+
}
|
|
117
|
+
catch (cause) {
|
|
118
|
+
return Result.err(new KVError({ operation: "delete", cause, context: { key } }));
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
async keys(filter = ">") {
|
|
122
|
+
try {
|
|
123
|
+
return Result.ok(await this.kv.keys(filter));
|
|
124
|
+
}
|
|
125
|
+
catch (cause) {
|
|
126
|
+
return Result.err(new KVError({ operation: "keys", cause, context: { filter } }));
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
async status() {
|
|
130
|
+
try {
|
|
131
|
+
const status = await this.kv.status();
|
|
132
|
+
return Result.ok({ values: status.values });
|
|
133
|
+
}
|
|
134
|
+
catch (cause) {
|
|
135
|
+
return Result.err(new KVError({ operation: "status", cause }));
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
export class TypedKVEntry {
|
|
140
|
+
constructor(schema, kv, entry, value) {
|
|
141
|
+
Object.defineProperty(this, "schema", {
|
|
142
|
+
enumerable: true,
|
|
143
|
+
configurable: true,
|
|
144
|
+
writable: true,
|
|
145
|
+
value: schema
|
|
146
|
+
});
|
|
147
|
+
Object.defineProperty(this, "kv", {
|
|
148
|
+
enumerable: true,
|
|
149
|
+
configurable: true,
|
|
150
|
+
writable: true,
|
|
151
|
+
value: kv
|
|
152
|
+
});
|
|
153
|
+
Object.defineProperty(this, "entry", {
|
|
154
|
+
enumerable: true,
|
|
155
|
+
configurable: true,
|
|
156
|
+
writable: true,
|
|
157
|
+
value: entry
|
|
158
|
+
});
|
|
159
|
+
Object.defineProperty(this, "value", {
|
|
160
|
+
enumerable: true,
|
|
161
|
+
configurable: true,
|
|
162
|
+
writable: true,
|
|
163
|
+
value: void 0
|
|
164
|
+
});
|
|
165
|
+
// @ts-expect-error svelte-check hits excessive type instantiation on assignment
|
|
166
|
+
this.value = value;
|
|
167
|
+
}
|
|
168
|
+
static async create(schema, kv, entry) {
|
|
169
|
+
return await createTypedKvEntry(schema, kv, entry);
|
|
170
|
+
}
|
|
171
|
+
get key() {
|
|
172
|
+
return decodeSubject(this.entry.key);
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Watch this KV entry for changes.
|
|
176
|
+
*
|
|
177
|
+
* @param callback - Function called when the entry changes
|
|
178
|
+
* @param opts - Watch options (e.g., includeDeletes)
|
|
179
|
+
* @returns A function to stop watching
|
|
180
|
+
*/
|
|
181
|
+
async watch(callback, opts) {
|
|
182
|
+
const watcher = await this.kv.watch({
|
|
183
|
+
key: this.entry.key,
|
|
184
|
+
include: opts?.includeDeletes ? "history" : "updates",
|
|
185
|
+
});
|
|
186
|
+
const abortController = new AbortController();
|
|
187
|
+
// Start the async iteration in the background
|
|
188
|
+
(async () => {
|
|
189
|
+
for await (const entry of watcher) {
|
|
190
|
+
if (abortController.signal.aborted)
|
|
191
|
+
break;
|
|
192
|
+
if (entry.operation === "DEL" || entry.operation === "PURGE") {
|
|
193
|
+
if (opts?.includeDeletes) {
|
|
194
|
+
callback({
|
|
195
|
+
type: "delete",
|
|
196
|
+
key: decodeSubject(entry.key),
|
|
197
|
+
revision: entry.revision,
|
|
198
|
+
timestamp: entry.created,
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
else {
|
|
203
|
+
let validated;
|
|
204
|
+
try {
|
|
205
|
+
const json = entry.json();
|
|
206
|
+
validated = parseExternalValue(this.schema, json);
|
|
207
|
+
}
|
|
208
|
+
catch {
|
|
209
|
+
try {
|
|
210
|
+
await this.kv.delete(entry.key, {
|
|
211
|
+
previousSeq: entry.revision,
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
catch {
|
|
215
|
+
// Best-effort cleanup of invalid entries.
|
|
216
|
+
}
|
|
217
|
+
continue;
|
|
218
|
+
}
|
|
219
|
+
callback({
|
|
220
|
+
type: "update",
|
|
221
|
+
key: decodeSubject(entry.key),
|
|
222
|
+
value: validated,
|
|
223
|
+
revision: entry.revision,
|
|
224
|
+
timestamp: entry.created,
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
})();
|
|
229
|
+
return () => {
|
|
230
|
+
abortController.abort();
|
|
231
|
+
watcher.stop();
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
async merge(value, vcc) {
|
|
235
|
+
// @ts-expect-error svelte-check hits excessive type instantiation here
|
|
236
|
+
const mergedData = merge(this.value, value);
|
|
237
|
+
const schema = this.schema;
|
|
238
|
+
const mergedRawValue = mergedData;
|
|
239
|
+
// @ts-expect-error svelte-check hits excessive type instantiation here
|
|
240
|
+
const mergeResult = Result.try(() => serializeExternalValue(schema, mergedRawValue));
|
|
241
|
+
if (mergeResult.isErr()) {
|
|
242
|
+
const cause = mergeResult.error.cause;
|
|
243
|
+
if (cause instanceof ParseError) {
|
|
244
|
+
const errors = Value.Errors(schema, externalizeValue(mergedData));
|
|
245
|
+
return Result.err(new ValidationError({ errors, cause }));
|
|
246
|
+
}
|
|
247
|
+
return Result.err(new KVError({ operation: "merge", cause: mergeResult.error, context: { key: this.key } }));
|
|
248
|
+
}
|
|
249
|
+
return this.put(mergedData, vcc);
|
|
250
|
+
}
|
|
251
|
+
async put(value, vcc) {
|
|
252
|
+
const schema = this.schema;
|
|
253
|
+
const serialized = serializeValue(schema, value);
|
|
254
|
+
try {
|
|
255
|
+
await this.kv.put(this.entry.key, serialized, {
|
|
256
|
+
previousSeq: vcc ? this.entry.revision : undefined,
|
|
257
|
+
});
|
|
258
|
+
return Result.ok(undefined);
|
|
259
|
+
}
|
|
260
|
+
catch (cause) {
|
|
261
|
+
return Result.err(new KVError({ operation: "put", cause, context: { key: this.key } }));
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
async delete(vcc) {
|
|
265
|
+
try {
|
|
266
|
+
await this.kv.delete(this.entry.key, {
|
|
267
|
+
previousSeq: vcc ? this.entry.revision : undefined,
|
|
268
|
+
});
|
|
269
|
+
return Result.ok(undefined);
|
|
270
|
+
}
|
|
271
|
+
catch (cause) {
|
|
272
|
+
return Result.err(new KVError({ operation: "delete", cause, context: { key: this.key } }));
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
async function createTypedKvEntry(schema, kv, entry) {
|
|
277
|
+
async function deleteInvalidEntry(reason) {
|
|
278
|
+
try {
|
|
279
|
+
await kv.delete(entry.key, {
|
|
280
|
+
previousSeq: entry.revision,
|
|
281
|
+
});
|
|
282
|
+
return {
|
|
283
|
+
key: decodeSubject(entry.key),
|
|
284
|
+
revision: entry.revision,
|
|
285
|
+
invalidEntryDeleted: true,
|
|
286
|
+
invalidEntryReason: reason,
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
catch (cause) {
|
|
290
|
+
return {
|
|
291
|
+
key: decodeSubject(entry.key),
|
|
292
|
+
revision: entry.revision,
|
|
293
|
+
invalidEntryDeleted: false,
|
|
294
|
+
invalidEntryDeleteError: cause instanceof Error ? cause.message : String(cause),
|
|
295
|
+
invalidEntryReason: reason,
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
const jsonResult = Result.try(() => entry.json());
|
|
300
|
+
if (jsonResult.isErr()) {
|
|
301
|
+
const context = await deleteInvalidEntry(`decode failed: ${jsonResult.error.message}`);
|
|
302
|
+
return Result.err(new ValidationError({
|
|
303
|
+
errors: [{ path: "", message: `Failed to decode KV value: ${jsonResult.error.message}` }],
|
|
304
|
+
cause: jsonResult.error,
|
|
305
|
+
context,
|
|
306
|
+
}));
|
|
307
|
+
}
|
|
308
|
+
const json = jsonResult.take();
|
|
309
|
+
const parseResult = Result.try(() => parseExternalValue(schema, json));
|
|
310
|
+
if (parseResult.isErr()) {
|
|
311
|
+
const cause = parseResult.error.cause;
|
|
312
|
+
if (cause instanceof ParseError) {
|
|
313
|
+
const errors = Value.Errors(schema, json);
|
|
314
|
+
const context = await deleteInvalidEntry("schema parse failed");
|
|
315
|
+
return Result.err(new ValidationError({ errors, cause, context }));
|
|
316
|
+
}
|
|
317
|
+
const context = await deleteInvalidEntry(parseResult.error.message);
|
|
318
|
+
return Result.err(new ValidationError({
|
|
319
|
+
errors: [{ path: "", message: parseResult.error.message }],
|
|
320
|
+
cause: parseResult.error,
|
|
321
|
+
context,
|
|
322
|
+
}));
|
|
323
|
+
}
|
|
324
|
+
const typedEntry = new TypedKVEntry(schema, kv, entry, parseResult.take());
|
|
325
|
+
return Result.ok(typedEntry);
|
|
326
|
+
}
|