@zenstackhq/client-helpers 3.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/.turbo/turbo-build.log +24 -0
- package/LICENSE +21 -0
- package/dist/fetch.d.ts +35 -0
- package/dist/fetch.js +101 -0
- package/dist/fetch.js.map +1 -0
- package/dist/index.d.ts +278 -0
- package/dist/index.js +822 -0
- package/dist/index.js.map +1 -0
- package/eslint.config.js +4 -0
- package/package.json +40 -0
- package/src/constants.ts +4 -0
- package/src/fetch.ts +107 -0
- package/src/index.ts +9 -0
- package/src/invalidation.ts +89 -0
- package/src/logging.ts +15 -0
- package/src/mutator.ts +449 -0
- package/src/nested-read-visitor.ts +68 -0
- package/src/nested-write-visitor.ts +359 -0
- package/src/optimistic.ts +139 -0
- package/src/query-analysis.ts +111 -0
- package/src/types.ts +82 -0
- package/test/fetch.test.ts +423 -0
- package/test/invalidation.test.ts +602 -0
- package/test/mutator.test.ts +1533 -0
- package/test/nested-read-visitor.test.ts +949 -0
- package/test/nested-write-visitor.test.ts +1244 -0
- package/test/optimistic.test.ts +743 -0
- package/test/query-analysis.test.ts +1399 -0
- package/test/test-helpers.ts +37 -0
- package/tsconfig.json +4 -0
- package/tsconfig.test.json +7 -0
- package/tsup.config.ts +14 -0
- package/vitest.config.ts +4 -0
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
|
|
2
|
+
> @zenstackhq/client-helpers@3.1.0 build /home/runner/work/zenstack-v3/zenstack-v3/packages/clients/client-helpers
|
|
3
|
+
> tsc --noEmit && tsup-node && pnpm test:typecheck
|
|
4
|
+
|
|
5
|
+
[34mCLI[39m Building entry: {"index":"src/index.ts","fetch":"src/fetch.ts"}
|
|
6
|
+
[34mCLI[39m Using tsconfig: tsconfig.json
|
|
7
|
+
[34mCLI[39m tsup v8.5.0
|
|
8
|
+
[34mCLI[39m Using tsup config: /home/runner/work/zenstack-v3/zenstack-v3/packages/clients/client-helpers/tsup.config.ts
|
|
9
|
+
[34mCLI[39m Target: esnext
|
|
10
|
+
[34mCLI[39m Cleaning output folder
|
|
11
|
+
[34mESM[39m Build start
|
|
12
|
+
[32mESM[39m [1mdist/index.js [22m[32m25.68 KB[39m
|
|
13
|
+
[32mESM[39m [1mdist/fetch.js [22m[32m2.73 KB[39m
|
|
14
|
+
[32mESM[39m [1mdist/index.js.map [22m[32m63.32 KB[39m
|
|
15
|
+
[32mESM[39m [1mdist/fetch.js.map [22m[32m5.94 KB[39m
|
|
16
|
+
[32mESM[39m ⚡️ Build success in 136ms
|
|
17
|
+
[34mDTS[39m Build start
|
|
18
|
+
[32mDTS[39m ⚡️ Build success in 5075ms
|
|
19
|
+
[32mDTS[39m [1mdist/index.d.ts [22m[32m10.28 KB[39m
|
|
20
|
+
[32mDTS[39m [1mdist/fetch.d.ts [22m[32m1.24 KB[39m
|
|
21
|
+
|
|
22
|
+
> @zenstackhq/client-helpers@3.1.0 test:typecheck /home/runner/work/zenstack-v3/zenstack-v3/packages/clients/client-helpers
|
|
23
|
+
> tsc --noEmit --project tsconfig.test.json
|
|
24
|
+
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 ZenStack
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/dist/fetch.d.ts
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Function signature for `fetch`.
|
|
3
|
+
*/
|
|
4
|
+
type FetchFn = (url: string, options?: RequestInit) => Promise<Response>;
|
|
5
|
+
/**
|
|
6
|
+
* A fetcher function that uses fetch API to make HTTP requests and automatically unmarshals
|
|
7
|
+
* the response using superjson.
|
|
8
|
+
*/
|
|
9
|
+
declare function fetcher<R>(url: string, options?: RequestInit, customFetch?: FetchFn): Promise<R>;
|
|
10
|
+
/**
|
|
11
|
+
* Makes a URL for the given endpoint, model, operation, and args that matches the RPC-style server API.
|
|
12
|
+
*/
|
|
13
|
+
declare function makeUrl(endpoint: string, model: string, operation: string, args?: unknown): string;
|
|
14
|
+
/**
|
|
15
|
+
* Serialize the given value with superjson
|
|
16
|
+
*/
|
|
17
|
+
declare function serialize(value: unknown): {
|
|
18
|
+
data: unknown;
|
|
19
|
+
meta: unknown;
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* Deserialize the given value with superjson using the given metadata
|
|
23
|
+
*/
|
|
24
|
+
declare function deserialize(value: unknown, meta: any): unknown;
|
|
25
|
+
/**
|
|
26
|
+
* Marshal the given value to a string using superjson
|
|
27
|
+
*/
|
|
28
|
+
declare function marshal(value: unknown): string;
|
|
29
|
+
/**
|
|
30
|
+
* Unmarshal the given string value using superjson, assuming the value is a JSON stringified
|
|
31
|
+
* object containing the serialized data and serialization metadata.
|
|
32
|
+
*/
|
|
33
|
+
declare function unmarshal(value: string): any;
|
|
34
|
+
|
|
35
|
+
export { type FetchFn, deserialize, fetcher, makeUrl, marshal, serialize, unmarshal };
|
package/dist/fetch.js
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
|
|
3
|
+
|
|
4
|
+
// src/fetch.ts
|
|
5
|
+
import { lowerCaseFirst } from "@zenstackhq/common-helpers";
|
|
6
|
+
import Decimal from "decimal.js";
|
|
7
|
+
import SuperJSON from "superjson";
|
|
8
|
+
async function fetcher(url, options, customFetch) {
|
|
9
|
+
const _fetch = customFetch ?? fetch;
|
|
10
|
+
const res = await _fetch(url, options);
|
|
11
|
+
if (!res.ok) {
|
|
12
|
+
const errData = unmarshal(await res.text());
|
|
13
|
+
if (errData.error?.rejectedByPolicy && errData.error?.rejectReason === "cannot-read-back") {
|
|
14
|
+
return void 0;
|
|
15
|
+
}
|
|
16
|
+
const error = new Error("An error occurred while fetching the data.");
|
|
17
|
+
error.info = errData.error;
|
|
18
|
+
error.status = res.status;
|
|
19
|
+
throw error;
|
|
20
|
+
}
|
|
21
|
+
const textResult = await res.text();
|
|
22
|
+
try {
|
|
23
|
+
return unmarshal(textResult).data;
|
|
24
|
+
} catch (err) {
|
|
25
|
+
console.error(`Unable to deserialize data:`, textResult);
|
|
26
|
+
throw err;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
__name(fetcher, "fetcher");
|
|
30
|
+
function makeUrl(endpoint, model, operation, args) {
|
|
31
|
+
const baseUrl = `${endpoint}/${lowerCaseFirst(model)}/${operation}`;
|
|
32
|
+
if (!args) {
|
|
33
|
+
return baseUrl;
|
|
34
|
+
}
|
|
35
|
+
const { data, meta } = serialize(args);
|
|
36
|
+
let result = `${baseUrl}?q=${encodeURIComponent(JSON.stringify(data))}`;
|
|
37
|
+
if (meta) {
|
|
38
|
+
result += `&meta=${encodeURIComponent(JSON.stringify({
|
|
39
|
+
serialization: meta
|
|
40
|
+
}))}`;
|
|
41
|
+
}
|
|
42
|
+
return result;
|
|
43
|
+
}
|
|
44
|
+
__name(makeUrl, "makeUrl");
|
|
45
|
+
SuperJSON.registerCustom({
|
|
46
|
+
isApplicable: /* @__PURE__ */ __name((v) => v instanceof Decimal || // interop with decimal.js
|
|
47
|
+
v?.toStringTag === "[object Decimal]", "isApplicable"),
|
|
48
|
+
serialize: /* @__PURE__ */ __name((v) => v.toJSON(), "serialize"),
|
|
49
|
+
deserialize: /* @__PURE__ */ __name((v) => new Decimal(v), "deserialize")
|
|
50
|
+
}, "Decimal");
|
|
51
|
+
function serialize(value) {
|
|
52
|
+
const { json, meta } = SuperJSON.serialize(value);
|
|
53
|
+
return {
|
|
54
|
+
data: json,
|
|
55
|
+
meta
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
__name(serialize, "serialize");
|
|
59
|
+
function deserialize(value, meta) {
|
|
60
|
+
return SuperJSON.deserialize({
|
|
61
|
+
json: value,
|
|
62
|
+
meta
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
__name(deserialize, "deserialize");
|
|
66
|
+
function marshal(value) {
|
|
67
|
+
const { data, meta } = serialize(value);
|
|
68
|
+
if (meta) {
|
|
69
|
+
return JSON.stringify({
|
|
70
|
+
...data,
|
|
71
|
+
meta: {
|
|
72
|
+
serialization: meta
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
} else {
|
|
76
|
+
return JSON.stringify(data);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
__name(marshal, "marshal");
|
|
80
|
+
function unmarshal(value) {
|
|
81
|
+
const parsed = JSON.parse(value);
|
|
82
|
+
if (typeof parsed === "object" && parsed?.data && parsed?.meta?.serialization) {
|
|
83
|
+
const deserializedData = deserialize(parsed.data, parsed.meta.serialization);
|
|
84
|
+
return {
|
|
85
|
+
...parsed,
|
|
86
|
+
data: deserializedData
|
|
87
|
+
};
|
|
88
|
+
} else {
|
|
89
|
+
return parsed;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
__name(unmarshal, "unmarshal");
|
|
93
|
+
export {
|
|
94
|
+
deserialize,
|
|
95
|
+
fetcher,
|
|
96
|
+
makeUrl,
|
|
97
|
+
marshal,
|
|
98
|
+
serialize,
|
|
99
|
+
unmarshal
|
|
100
|
+
};
|
|
101
|
+
//# sourceMappingURL=fetch.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/fetch.ts"],"sourcesContent":["import { lowerCaseFirst } from '@zenstackhq/common-helpers';\nimport Decimal from 'decimal.js';\nimport SuperJSON from 'superjson';\nimport type { QueryError } from './types';\n\n/**\n * Function signature for `fetch`.\n */\nexport type FetchFn = (url: string, options?: RequestInit) => Promise<Response>;\n\n/**\n * A fetcher function that uses fetch API to make HTTP requests and automatically unmarshals\n * the response using superjson.\n */\nexport async function fetcher<R>(url: string, options?: RequestInit, customFetch?: FetchFn): Promise<R> {\n const _fetch = customFetch ?? fetch;\n const res = await _fetch(url, options);\n if (!res.ok) {\n const errData = unmarshal(await res.text());\n if (errData.error?.rejectedByPolicy && errData.error?.rejectReason === 'cannot-read-back') {\n // policy doesn't allow mutation result to be read back, just return undefined\n return undefined as any;\n }\n const error: QueryError = new Error('An error occurred while fetching the data.');\n error.info = errData.error;\n error.status = res.status;\n throw error;\n }\n\n const textResult = await res.text();\n try {\n return unmarshal(textResult).data as R;\n } catch (err) {\n console.error(`Unable to deserialize data:`, textResult);\n throw err;\n }\n}\n\n/**\n * Makes a URL for the given endpoint, model, operation, and args that matches the RPC-style server API.\n */\nexport function makeUrl(endpoint: string, model: string, operation: string, args?: unknown) {\n const baseUrl = `${endpoint}/${lowerCaseFirst(model)}/${operation}`;\n if (!args) {\n return baseUrl;\n }\n\n const { data, meta } = serialize(args);\n let result = `${baseUrl}?q=${encodeURIComponent(JSON.stringify(data))}`;\n if (meta) {\n result += `&meta=${encodeURIComponent(JSON.stringify({ serialization: meta }))}`;\n }\n return result;\n}\n\nSuperJSON.registerCustom<Decimal, string>(\n {\n isApplicable: (v): v is Decimal =>\n v instanceof Decimal ||\n // interop with decimal.js\n v?.toStringTag === '[object Decimal]',\n serialize: (v) => v.toJSON(),\n deserialize: (v) => new Decimal(v),\n },\n 'Decimal',\n);\n\n/**\n * Serialize the given value with superjson\n */\nexport function serialize(value: unknown): { data: unknown; meta: unknown } {\n const { json, meta } = SuperJSON.serialize(value);\n return { data: json, meta };\n}\n\n/**\n * Deserialize the given value with superjson using the given metadata\n */\nexport function deserialize(value: unknown, meta: any): unknown {\n return SuperJSON.deserialize({ json: value as any, meta });\n}\n\n/**\n * Marshal the given value to a string using superjson\n */\nexport function marshal(value: unknown) {\n const { data, meta } = serialize(value);\n if (meta) {\n return JSON.stringify({ ...(data as any), meta: { serialization: meta } });\n } else {\n return JSON.stringify(data);\n }\n}\n\n/**\n * Unmarshal the given string value using superjson, assuming the value is a JSON stringified\n * object containing the serialized data and serialization metadata.\n */\nexport function unmarshal(value: string) {\n const parsed = JSON.parse(value);\n if (typeof parsed === 'object' && parsed?.data && parsed?.meta?.serialization) {\n const deserializedData = deserialize(parsed.data, parsed.meta.serialization);\n return { ...parsed, data: deserializedData };\n } else {\n return parsed;\n }\n}\n"],"mappings":";;;;AAAA,SAASA,sBAAsB;AAC/B,OAAOC,aAAa;AACpB,OAAOC,eAAe;AAYtB,eAAsBC,QAAWC,KAAaC,SAAuBC,aAAqB;AACtF,QAAMC,SAASD,eAAeE;AAC9B,QAAMC,MAAM,MAAMF,OAAOH,KAAKC,OAAAA;AAC9B,MAAI,CAACI,IAAIC,IAAI;AACT,UAAMC,UAAUC,UAAU,MAAMH,IAAII,KAAI,CAAA;AACxC,QAAIF,QAAQG,OAAOC,oBAAoBJ,QAAQG,OAAOE,iBAAiB,oBAAoB;AAEvF,aAAOC;IACX;AACA,UAAMH,QAAoB,IAAII,MAAM,4CAAA;AACpCJ,UAAMK,OAAOR,QAAQG;AACrBA,UAAMM,SAASX,IAAIW;AACnB,UAAMN;EACV;AAEA,QAAMO,aAAa,MAAMZ,IAAII,KAAI;AACjC,MAAI;AACA,WAAOD,UAAUS,UAAAA,EAAYC;EACjC,SAASC,KAAK;AACVC,YAAQV,MAAM,+BAA+BO,UAAAA;AAC7C,UAAME;EACV;AACJ;AAtBsBpB;AA2Bf,SAASsB,QAAQC,UAAkBC,OAAeC,WAAmBC,MAAc;AACtF,QAAMC,UAAU,GAAGJ,QAAAA,IAAYK,eAAeJ,KAAAA,CAAAA,IAAUC,SAAAA;AACxD,MAAI,CAACC,MAAM;AACP,WAAOC;EACX;AAEA,QAAM,EAAER,MAAMU,KAAI,IAAKC,UAAUJ,IAAAA;AACjC,MAAIK,SAAS,GAAGJ,OAAAA,MAAaK,mBAAmBC,KAAKC,UAAUf,IAAAA,CAAAA,CAAAA;AAC/D,MAAIU,MAAM;AACNE,cAAU,SAASC,mBAAmBC,KAAKC,UAAU;MAAEC,eAAeN;IAAK,CAAA,CAAA,CAAA;EAC/E;AACA,SAAOE;AACX;AAZgBT;AAchBc,UAAUC,eACN;EACIC,cAAc,wBAACC,MACXA,aAAaC;EAEbD,GAAGE,gBAAgB,oBAHT;EAIdX,WAAW,wBAACS,MAAMA,EAAEG,OAAM,GAAf;EACXC,aAAa,wBAACJ,MAAM,IAAIC,QAAQD,CAAAA,GAAnB;AACjB,GACA,SAAA;AAMG,SAAST,UAAUc,OAAc;AACpC,QAAM,EAAEC,MAAMhB,KAAI,IAAKO,UAAUN,UAAUc,KAAAA;AAC3C,SAAO;IAAEzB,MAAM0B;IAAMhB;EAAK;AAC9B;AAHgBC;AAQT,SAASa,YAAYC,OAAgBf,MAAS;AACjD,SAAOO,UAAUO,YAAY;IAAEE,MAAMD;IAAcf;EAAK,CAAA;AAC5D;AAFgBc;AAOT,SAASG,QAAQF,OAAc;AAClC,QAAM,EAAEzB,MAAMU,KAAI,IAAKC,UAAUc,KAAAA;AACjC,MAAIf,MAAM;AACN,WAAOI,KAAKC,UAAU;MAAE,GAAIf;MAAcU,MAAM;QAAEM,eAAeN;MAAK;IAAE,CAAA;EAC5E,OAAO;AACH,WAAOI,KAAKC,UAAUf,IAAAA;EAC1B;AACJ;AAPgB2B;AAaT,SAASrC,UAAUmC,OAAa;AACnC,QAAMG,SAASd,KAAKe,MAAMJ,KAAAA;AAC1B,MAAI,OAAOG,WAAW,YAAYA,QAAQ5B,QAAQ4B,QAAQlB,MAAMM,eAAe;AAC3E,UAAMc,mBAAmBN,YAAYI,OAAO5B,MAAM4B,OAAOlB,KAAKM,aAAa;AAC3E,WAAO;MAAE,GAAGY;MAAQ5B,MAAM8B;IAAiB;EAC/C,OAAO;AACH,WAAOF;EACX;AACJ;AARgBtC;","names":["lowerCaseFirst","Decimal","SuperJSON","fetcher","url","options","customFetch","_fetch","fetch","res","ok","errData","unmarshal","text","error","rejectedByPolicy","rejectReason","undefined","Error","info","status","textResult","data","err","console","makeUrl","endpoint","model","operation","args","baseUrl","lowerCaseFirst","meta","serialize","result","encodeURIComponent","JSON","stringify","serialization","SuperJSON","registerCustom","isApplicable","v","Decimal","toStringTag","toJSON","deserialize","value","json","marshal","parsed","parse","deserializedData"]}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
import { SchemaDef, FieldDef } from '@zenstackhq/schema';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The default query endpoint.
|
|
5
|
+
*/
|
|
6
|
+
declare const DEFAULT_QUERY_ENDPOINT = "/api/model";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Logger configuration. `true` enables console logging. A function can be provided for custom logging.
|
|
10
|
+
*/
|
|
11
|
+
type Logger = boolean | ((message: string) => void);
|
|
12
|
+
/**
|
|
13
|
+
* Logs a message using the provided logger.
|
|
14
|
+
*/
|
|
15
|
+
declare function log(logger: Logger, message: string): void;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* A type that represents either a value of type T or a Promise that resolves to type T.
|
|
19
|
+
*/
|
|
20
|
+
type MaybePromise<T> = T | Promise<T> | PromiseLike<T>;
|
|
21
|
+
/**
|
|
22
|
+
* List of ORM write actions.
|
|
23
|
+
*/
|
|
24
|
+
declare const ORMWriteActions: readonly ["create", "createMany", "createManyAndReturn", "connectOrCreate", "update", "updateMany", "updateManyAndReturn", "upsert", "connect", "disconnect", "set", "delete", "deleteMany"];
|
|
25
|
+
/**
|
|
26
|
+
* Type representing ORM write action types.
|
|
27
|
+
*/
|
|
28
|
+
type ORMWriteActionType = (typeof ORMWriteActions)[number];
|
|
29
|
+
/**
|
|
30
|
+
* Type for query and mutation errors.
|
|
31
|
+
*/
|
|
32
|
+
type QueryError = Error & {
|
|
33
|
+
/**
|
|
34
|
+
* Additional error information.
|
|
35
|
+
*/
|
|
36
|
+
info?: unknown;
|
|
37
|
+
/**
|
|
38
|
+
* HTTP status code.
|
|
39
|
+
*/
|
|
40
|
+
status?: number;
|
|
41
|
+
};
|
|
42
|
+
/**
|
|
43
|
+
* Information about a cached query.
|
|
44
|
+
*/
|
|
45
|
+
type QueryInfo = {
|
|
46
|
+
/**
|
|
47
|
+
* Model of the query.
|
|
48
|
+
*/
|
|
49
|
+
model: string;
|
|
50
|
+
/**
|
|
51
|
+
* Query operation, e.g., `findUnique`
|
|
52
|
+
*/
|
|
53
|
+
operation: string;
|
|
54
|
+
/**
|
|
55
|
+
* Query arguments.
|
|
56
|
+
*/
|
|
57
|
+
args: unknown;
|
|
58
|
+
/**
|
|
59
|
+
* Current data cached for this query.
|
|
60
|
+
*/
|
|
61
|
+
data: unknown;
|
|
62
|
+
/**
|
|
63
|
+
* Whether optimistic update is enabled for this query.
|
|
64
|
+
*/
|
|
65
|
+
optimisticUpdate: boolean;
|
|
66
|
+
/**
|
|
67
|
+
* Function to update the cached data.
|
|
68
|
+
*
|
|
69
|
+
* @param data New data to set.
|
|
70
|
+
* @param cancelOnTheFlyQueries Whether to cancel on-the-fly queries to avoid accidentally
|
|
71
|
+
* overwriting the optimistic update.
|
|
72
|
+
*/
|
|
73
|
+
updateData: (data: unknown, cancelOnTheFlyQueries: boolean) => void;
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Type for a predicate that determines whether a query should be invalidated.
|
|
78
|
+
*/
|
|
79
|
+
type InvalidationPredicate = ({ model, args }: {
|
|
80
|
+
model: string;
|
|
81
|
+
args: unknown;
|
|
82
|
+
}) => boolean;
|
|
83
|
+
/**
|
|
84
|
+
* Type for a function that invalidates queries matching the given predicate.
|
|
85
|
+
*/
|
|
86
|
+
type InvalidateFunc = (predicate: InvalidationPredicate) => MaybePromise<void>;
|
|
87
|
+
/**
|
|
88
|
+
* Create a function that invalidates queries affected by the given mutation operation.
|
|
89
|
+
*
|
|
90
|
+
* @param model Model under mutation.
|
|
91
|
+
* @param operation Mutation operation (e.g, `update`).
|
|
92
|
+
* @param schema The schema.
|
|
93
|
+
* @param invalidator Function to invalidate queries matching a predicate. It should internally
|
|
94
|
+
* enumerate all query cache entries and invalidate those for which the predicate returns true.
|
|
95
|
+
* @param logging Logging option.
|
|
96
|
+
*/
|
|
97
|
+
declare function createInvalidator(model: string, operation: string, schema: SchemaDef, invalidator: InvalidateFunc, logging: Logger | undefined): (...args: unknown[]) => Promise<void>;
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Tries to apply a mutation to a query result.
|
|
101
|
+
*
|
|
102
|
+
* @param queryModel the model of the query
|
|
103
|
+
* @param queryOp the operation of the query
|
|
104
|
+
* @param queryData the result data of the query
|
|
105
|
+
* @param mutationModel the model of the mutation
|
|
106
|
+
* @param mutationOp the operation of the mutation
|
|
107
|
+
* @param mutationArgs the arguments of the mutation
|
|
108
|
+
* @param schema the schema
|
|
109
|
+
* @param logging logging configuration
|
|
110
|
+
* @returns the updated query data if the mutation is applicable, otherwise undefined
|
|
111
|
+
*/
|
|
112
|
+
declare function applyMutation(queryModel: string, queryOp: string, queryData: any, mutationModel: string, mutationOp: ORMWriteActionType, mutationArgs: any, schema: SchemaDef, logging: Logger | undefined): Promise<any>;
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Callback functions for nested read visitor.
|
|
116
|
+
*/
|
|
117
|
+
type NestedReadVisitorCallback = {
|
|
118
|
+
/**
|
|
119
|
+
* Callback for each field visited.
|
|
120
|
+
* @returns If returns false, traversal will not continue into this field.
|
|
121
|
+
*/
|
|
122
|
+
field?: (model: string, field: FieldDef | undefined, kind: 'include' | 'select' | undefined, args: unknown) => void | boolean;
|
|
123
|
+
};
|
|
124
|
+
/**
|
|
125
|
+
* Visitor for nested read payload.
|
|
126
|
+
*/
|
|
127
|
+
declare class NestedReadVisitor {
|
|
128
|
+
private readonly schema;
|
|
129
|
+
private readonly callback;
|
|
130
|
+
constructor(schema: SchemaDef, callback: NestedReadVisitorCallback);
|
|
131
|
+
private doVisit;
|
|
132
|
+
visit(model: string, args: unknown): void;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
type NestingPathItem = {
|
|
136
|
+
field?: FieldDef;
|
|
137
|
+
model: string;
|
|
138
|
+
where: any;
|
|
139
|
+
unique: boolean;
|
|
140
|
+
};
|
|
141
|
+
/**
|
|
142
|
+
* Context for visiting
|
|
143
|
+
*/
|
|
144
|
+
type NestedWriteVisitorContext = {
|
|
145
|
+
/**
|
|
146
|
+
* Parent data, can be used to replace fields
|
|
147
|
+
*/
|
|
148
|
+
parent: any;
|
|
149
|
+
/**
|
|
150
|
+
* Current field, undefined if toplevel
|
|
151
|
+
*/
|
|
152
|
+
field?: FieldDef;
|
|
153
|
+
/**
|
|
154
|
+
* A top-down path of all nested update conditions and corresponding field till now
|
|
155
|
+
*/
|
|
156
|
+
nestingPath: NestingPathItem[];
|
|
157
|
+
};
|
|
158
|
+
/**
|
|
159
|
+
* NestedWriteVisitor's callback actions. A call back function should return true or void to indicate
|
|
160
|
+
* that the visitor should continue traversing its children, or false to stop. It can also return an object
|
|
161
|
+
* to let the visitor traverse it instead of its original children.
|
|
162
|
+
*/
|
|
163
|
+
type NestedWriteVisitorCallback = {
|
|
164
|
+
create?: (model: string, data: any, context: NestedWriteVisitorContext) => MaybePromise<boolean | object | void>;
|
|
165
|
+
createMany?: (model: string, args: {
|
|
166
|
+
data: any;
|
|
167
|
+
skipDuplicates?: boolean;
|
|
168
|
+
}, context: NestedWriteVisitorContext) => MaybePromise<boolean | object | void>;
|
|
169
|
+
connectOrCreate?: (model: string, args: {
|
|
170
|
+
where: object;
|
|
171
|
+
create: any;
|
|
172
|
+
}, context: NestedWriteVisitorContext) => MaybePromise<boolean | object | void>;
|
|
173
|
+
connect?: (model: string, args: object, context: NestedWriteVisitorContext) => MaybePromise<boolean | object | void>;
|
|
174
|
+
disconnect?: (model: string, args: object, context: NestedWriteVisitorContext) => MaybePromise<boolean | object | void>;
|
|
175
|
+
set?: (model: string, args: object, context: NestedWriteVisitorContext) => MaybePromise<boolean | object | void>;
|
|
176
|
+
update?: (model: string, args: object, context: NestedWriteVisitorContext) => MaybePromise<boolean | object | void>;
|
|
177
|
+
updateMany?: (model: string, args: {
|
|
178
|
+
where?: object;
|
|
179
|
+
data: any;
|
|
180
|
+
}, context: NestedWriteVisitorContext) => MaybePromise<boolean | object | void>;
|
|
181
|
+
upsert?: (model: string, args: {
|
|
182
|
+
where: object;
|
|
183
|
+
create: any;
|
|
184
|
+
update: any;
|
|
185
|
+
}, context: NestedWriteVisitorContext) => MaybePromise<boolean | object | void>;
|
|
186
|
+
delete?: (model: string, args: object | boolean, context: NestedWriteVisitorContext) => MaybePromise<boolean | object | void>;
|
|
187
|
+
deleteMany?: (model: string, args: any | object, context: NestedWriteVisitorContext) => MaybePromise<boolean | object | void>;
|
|
188
|
+
field?: (field: FieldDef, action: ORMWriteActionType, data: any, context: NestedWriteVisitorContext) => MaybePromise<void>;
|
|
189
|
+
};
|
|
190
|
+
/**
|
|
191
|
+
* Recursive visitor for nested write (create/update) payload.
|
|
192
|
+
*/
|
|
193
|
+
declare class NestedWriteVisitor {
|
|
194
|
+
private readonly schema;
|
|
195
|
+
private readonly callback;
|
|
196
|
+
constructor(schema: SchemaDef, callback: NestedWriteVisitorCallback);
|
|
197
|
+
private isWriteAction;
|
|
198
|
+
/**
|
|
199
|
+
* Start visiting
|
|
200
|
+
*
|
|
201
|
+
* @see NestedWriteVisitorCallback
|
|
202
|
+
*/
|
|
203
|
+
visit(model: string, action: ORMWriteActionType, args: any): Promise<void>;
|
|
204
|
+
private doVisit;
|
|
205
|
+
private visitSubPayload;
|
|
206
|
+
private enumerateReverse;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Custom optimistic data provider. It takes query information (usually fetched from query cache)
|
|
211
|
+
* and returns a verdict on how to optimistically update the query data.
|
|
212
|
+
*
|
|
213
|
+
* @param args Arguments.
|
|
214
|
+
* @param args.queryModel The model of the query.
|
|
215
|
+
* @param args.queryOperation The operation of the query, `findMany`, `count`, etc.
|
|
216
|
+
* @param args.queryArgs The arguments of the query.
|
|
217
|
+
* @param args.currentData The current cache data for the query.
|
|
218
|
+
* @param args.mutationArgs The arguments of the mutation.
|
|
219
|
+
*/
|
|
220
|
+
type OptimisticDataProvider = (args: {
|
|
221
|
+
queryModel: string;
|
|
222
|
+
queryOperation: string;
|
|
223
|
+
queryArgs: any;
|
|
224
|
+
currentData: any;
|
|
225
|
+
mutationArgs: any;
|
|
226
|
+
}) => OptimisticDataProviderResult | Promise<OptimisticDataProviderResult>;
|
|
227
|
+
/**
|
|
228
|
+
* Result of optimistic data provider.
|
|
229
|
+
*/
|
|
230
|
+
type OptimisticDataProviderResult = {
|
|
231
|
+
/**
|
|
232
|
+
* Kind of the result.
|
|
233
|
+
* - Update: use the `data` field to update the query cache.
|
|
234
|
+
* - Skip: skip the optimistic update for this query.
|
|
235
|
+
* - ProceedDefault: proceed with the default optimistic update.
|
|
236
|
+
*/
|
|
237
|
+
kind: 'Update' | 'Skip' | 'ProceedDefault';
|
|
238
|
+
/**
|
|
239
|
+
* Data to update the query cache. Only applicable if `kind` is 'Update'.
|
|
240
|
+
*
|
|
241
|
+
* If the data is an object with fields updated, it should have a `$optimistic`
|
|
242
|
+
* field set to `true`. If it's an array and an element object is created or updated,
|
|
243
|
+
* the element should have a `$optimistic` field set to `true`.
|
|
244
|
+
*/
|
|
245
|
+
data?: any;
|
|
246
|
+
};
|
|
247
|
+
/**
|
|
248
|
+
* Options for optimistic update.
|
|
249
|
+
*/
|
|
250
|
+
type OptimisticUpdateOptions = {
|
|
251
|
+
/**
|
|
252
|
+
* A custom optimistic data provider.
|
|
253
|
+
*/
|
|
254
|
+
optimisticDataProvider?: OptimisticDataProvider;
|
|
255
|
+
};
|
|
256
|
+
/**
|
|
257
|
+
* Creates a function that performs optimistic updates for queries potentially
|
|
258
|
+
* affected by the given mutation operation.
|
|
259
|
+
*
|
|
260
|
+
* @param model Model under mutation.
|
|
261
|
+
* @param operation Mutation operation (e.g, `update`).
|
|
262
|
+
* @param schema The schema.
|
|
263
|
+
* @param options Optimistic update options.
|
|
264
|
+
* @param getAllQueries Callback to get all cached queries.
|
|
265
|
+
* @param logging Logging option.
|
|
266
|
+
*/
|
|
267
|
+
declare function createOptimisticUpdater(model: string, operation: string, schema: SchemaDef, options: OptimisticUpdateOptions, getAllQueries: () => readonly QueryInfo[], logging: Logger | undefined): (...args: unknown[]) => Promise<void>;
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Gets models read (including nested ones) given a query args.
|
|
271
|
+
*/
|
|
272
|
+
declare function getReadModels(model: string, schema: SchemaDef, args: any): string[];
|
|
273
|
+
/**
|
|
274
|
+
* Gets mutated models (including nested ones) given a mutation args.
|
|
275
|
+
*/
|
|
276
|
+
declare function getMutatedModels(model: string, operation: ORMWriteActionType, mutationArgs: any, schema: SchemaDef): Promise<string[]>;
|
|
277
|
+
|
|
278
|
+
export { DEFAULT_QUERY_ENDPOINT, type InvalidateFunc, type InvalidationPredicate, type Logger, type MaybePromise, NestedReadVisitor, type NestedReadVisitorCallback, NestedWriteVisitor, type NestedWriteVisitorCallback, type NestedWriteVisitorContext, type ORMWriteActionType, ORMWriteActions, type OptimisticDataProvider, type OptimisticDataProviderResult, type OptimisticUpdateOptions, type QueryError, type QueryInfo, applyMutation, createInvalidator, createOptimisticUpdater, getMutatedModels, getReadModels, log };
|