@zenstackhq/client-helpers 3.1.1 → 3.2.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/package.json CHANGED
@@ -1,10 +1,13 @@
1
1
  {
2
2
  "name": "@zenstackhq/client-helpers",
3
- "version": "3.1.1",
3
+ "version": "3.2.1",
4
4
  "description": "Helpers for implementing clients that consume ZenStack's CRUD service",
5
5
  "type": "module",
6
6
  "author": "ZenStack Team",
7
7
  "license": "MIT",
8
+ "files": [
9
+ "dist"
10
+ ],
8
11
  "exports": {
9
12
  ".": {
10
13
  "types": "./dist/index.d.ts",
@@ -18,16 +21,16 @@
18
21
  "dependencies": {
19
22
  "decimal.js": "^10.4.3",
20
23
  "superjson": "^2.2.3",
21
- "@zenstackhq/common-helpers": "3.1.1",
22
- "@zenstackhq/schema": "3.1.1"
24
+ "@zenstackhq/common-helpers": "3.2.1",
25
+ "@zenstackhq/schema": "3.2.1"
23
26
  },
24
27
  "devDependencies": {
25
- "@zenstackhq/sdk": "3.1.1",
26
- "@zenstackhq/typescript-config": "3.1.1",
27
- "@zenstackhq/vitest-config": "3.1.1",
28
- "@zenstackhq/language": "3.1.1",
29
- "@zenstackhq/eslint-config": "3.1.1",
30
- "@zenstackhq/orm": "3.1.1"
28
+ "@zenstackhq/eslint-config": "3.2.1",
29
+ "@zenstackhq/language": "3.2.1",
30
+ "@zenstackhq/orm": "3.2.1",
31
+ "@zenstackhq/sdk": "3.2.1",
32
+ "@zenstackhq/typescript-config": "3.2.1",
33
+ "@zenstackhq/vitest-config": "3.2.1"
31
34
  },
32
35
  "scripts": {
33
36
  "build": "tsc --noEmit && tsup-node && pnpm test:typecheck",
@@ -1,24 +0,0 @@
1
-
2
- > @zenstackhq/client-helpers@3.1.1 build /home/runner/work/zenstack-v3/zenstack-v3/packages/clients/client-helpers
3
- > tsc --noEmit && tsup-node && pnpm test:typecheck
4
-
5
- CLI Building entry: {"index":"src/index.ts","fetch":"src/fetch.ts"}
6
- CLI Using tsconfig: tsconfig.json
7
- CLI tsup v8.5.0
8
- CLI Using tsup config: /home/runner/work/zenstack-v3/zenstack-v3/packages/clients/client-helpers/tsup.config.ts
9
- CLI Target: esnext
10
- CLI Cleaning output folder
11
- ESM Build start
12
- ESM dist/index.js 25.68 KB
13
- ESM dist/fetch.js 2.73 KB
14
- ESM dist/index.js.map 63.32 KB
15
- ESM dist/fetch.js.map 5.94 KB
16
- ESM ⚡️ Build success in 137ms
17
- DTS Build start
18
- DTS ⚡️ Build success in 5106ms
19
- DTS dist/index.d.ts 10.28 KB
20
- DTS dist/fetch.d.ts 1.24 KB
21
-
22
- > @zenstackhq/client-helpers@3.1.1 test:typecheck /home/runner/work/zenstack-v3/zenstack-v3/packages/clients/client-helpers
23
- > tsc --noEmit --project tsconfig.test.json
24
-
package/eslint.config.js DELETED
@@ -1,4 +0,0 @@
1
- import config from '@zenstackhq/eslint-config/base.js';
2
-
3
- /** @type {import("eslint").Linter.Config} */
4
- export default config;
package/src/constants.ts DELETED
@@ -1,4 +0,0 @@
1
- /**
2
- * The default query endpoint.
3
- */
4
- export const DEFAULT_QUERY_ENDPOINT = '/api/model';
package/src/fetch.ts DELETED
@@ -1,107 +0,0 @@
1
- import { lowerCaseFirst } from '@zenstackhq/common-helpers';
2
- import Decimal from 'decimal.js';
3
- import SuperJSON from 'superjson';
4
- import type { QueryError } from './types';
5
-
6
- /**
7
- * Function signature for `fetch`.
8
- */
9
- export type FetchFn = (url: string, options?: RequestInit) => Promise<Response>;
10
-
11
- /**
12
- * A fetcher function that uses fetch API to make HTTP requests and automatically unmarshals
13
- * the response using superjson.
14
- */
15
- export async function fetcher<R>(url: string, options?: RequestInit, customFetch?: FetchFn): Promise<R> {
16
- const _fetch = customFetch ?? fetch;
17
- const res = await _fetch(url, options);
18
- if (!res.ok) {
19
- const errData = unmarshal(await res.text());
20
- if (errData.error?.rejectedByPolicy && errData.error?.rejectReason === 'cannot-read-back') {
21
- // policy doesn't allow mutation result to be read back, just return undefined
22
- return undefined as any;
23
- }
24
- const error: QueryError = new Error('An error occurred while fetching the data.');
25
- error.info = errData.error;
26
- error.status = res.status;
27
- throw error;
28
- }
29
-
30
- const textResult = await res.text();
31
- try {
32
- return unmarshal(textResult).data as R;
33
- } catch (err) {
34
- console.error(`Unable to deserialize data:`, textResult);
35
- throw err;
36
- }
37
- }
38
-
39
- /**
40
- * Makes a URL for the given endpoint, model, operation, and args that matches the RPC-style server API.
41
- */
42
- export function makeUrl(endpoint: string, model: string, operation: string, args?: unknown) {
43
- const baseUrl = `${endpoint}/${lowerCaseFirst(model)}/${operation}`;
44
- if (!args) {
45
- return baseUrl;
46
- }
47
-
48
- const { data, meta } = serialize(args);
49
- let result = `${baseUrl}?q=${encodeURIComponent(JSON.stringify(data))}`;
50
- if (meta) {
51
- result += `&meta=${encodeURIComponent(JSON.stringify({ serialization: meta }))}`;
52
- }
53
- return result;
54
- }
55
-
56
- SuperJSON.registerCustom<Decimal, string>(
57
- {
58
- isApplicable: (v): v is Decimal =>
59
- v instanceof Decimal ||
60
- // interop with decimal.js
61
- v?.toStringTag === '[object Decimal]',
62
- serialize: (v) => v.toJSON(),
63
- deserialize: (v) => new Decimal(v),
64
- },
65
- 'Decimal',
66
- );
67
-
68
- /**
69
- * Serialize the given value with superjson
70
- */
71
- export function serialize(value: unknown): { data: unknown; meta: unknown } {
72
- const { json, meta } = SuperJSON.serialize(value);
73
- return { data: json, meta };
74
- }
75
-
76
- /**
77
- * Deserialize the given value with superjson using the given metadata
78
- */
79
- export function deserialize(value: unknown, meta: any): unknown {
80
- return SuperJSON.deserialize({ json: value as any, meta });
81
- }
82
-
83
- /**
84
- * Marshal the given value to a string using superjson
85
- */
86
- export function marshal(value: unknown) {
87
- const { data, meta } = serialize(value);
88
- if (meta) {
89
- return JSON.stringify({ ...(data as any), meta: { serialization: meta } });
90
- } else {
91
- return JSON.stringify(data);
92
- }
93
- }
94
-
95
- /**
96
- * Unmarshal the given string value using superjson, assuming the value is a JSON stringified
97
- * object containing the serialized data and serialization metadata.
98
- */
99
- export function unmarshal(value: string) {
100
- const parsed = JSON.parse(value);
101
- if (typeof parsed === 'object' && parsed?.data && parsed?.meta?.serialization) {
102
- const deserializedData = deserialize(parsed.data, parsed.meta.serialization);
103
- return { ...parsed, data: deserializedData };
104
- } else {
105
- return parsed;
106
- }
107
- }
package/src/index.ts DELETED
@@ -1,9 +0,0 @@
1
- export * from './constants';
2
- export * from './invalidation';
3
- export * from './logging';
4
- export * from './mutator';
5
- export * from './nested-read-visitor';
6
- export * from './nested-write-visitor';
7
- export * from './optimistic';
8
- export * from './query-analysis';
9
- export * from './types';
@@ -1,89 +0,0 @@
1
- import type { SchemaDef } from '@zenstackhq/schema';
2
- import { log, type Logger } from './logging';
3
- import { getMutatedModels, getReadModels } from './query-analysis';
4
- import type { MaybePromise, ORMWriteActionType } from './types';
5
-
6
- /**
7
- * Type for a predicate that determines whether a query should be invalidated.
8
- */
9
- export type InvalidationPredicate = ({ model, args }: { model: string; args: unknown }) => boolean;
10
-
11
- /**
12
- * Type for a function that invalidates queries matching the given predicate.
13
- */
14
- export type InvalidateFunc = (predicate: InvalidationPredicate) => MaybePromise<void>;
15
-
16
- /**
17
- * Create a function that invalidates queries affected by the given mutation operation.
18
- *
19
- * @param model Model under mutation.
20
- * @param operation Mutation operation (e.g, `update`).
21
- * @param schema The schema.
22
- * @param invalidator Function to invalidate queries matching a predicate. It should internally
23
- * enumerate all query cache entries and invalidate those for which the predicate returns true.
24
- * @param logging Logging option.
25
- */
26
- export function createInvalidator(
27
- model: string,
28
- operation: string,
29
- schema: SchemaDef,
30
- invalidator: InvalidateFunc,
31
- logging: Logger | undefined,
32
- ) {
33
- return async (...args: unknown[]) => {
34
- const [_, variables] = args;
35
- const predicate = await getInvalidationPredicate(
36
- model,
37
- operation as ORMWriteActionType,
38
- variables,
39
- schema,
40
- logging,
41
- );
42
- await invalidator(predicate);
43
- };
44
- }
45
-
46
- // gets a predicate for evaluating whether a query should be invalidated
47
- async function getInvalidationPredicate(
48
- model: string,
49
- operation: ORMWriteActionType,
50
- mutationArgs: any,
51
- schema: SchemaDef,
52
- logging: Logger | undefined,
53
- ): Promise<InvalidationPredicate> {
54
- const mutatedModels = await getMutatedModels(model, operation, mutationArgs, schema);
55
-
56
- return ({ model, args }) => {
57
- if (mutatedModels.includes(model)) {
58
- // direct match
59
- if (logging) {
60
- log(
61
- logging,
62
- `Marking "${model}" query for invalidation due to mutation "${operation}", query args: ${JSON.stringify(args)}`,
63
- );
64
- }
65
- return true;
66
- }
67
-
68
- if (args) {
69
- // traverse query args to find nested reads that match the model under mutation
70
- if (findNestedRead(model, mutatedModels, schema, args)) {
71
- if (logging) {
72
- log(
73
- logging,
74
- `Marking "${model}" query for invalidation due to mutation "${operation}", query args: ${JSON.stringify(args)}`,
75
- );
76
- }
77
- return true;
78
- }
79
- }
80
-
81
- return false;
82
- };
83
- }
84
-
85
- // find nested reads that match the given models
86
- function findNestedRead(visitingModel: string, targetModels: string[], schema: SchemaDef, args: any) {
87
- const modelsRead = getReadModels(visitingModel, schema, args);
88
- return targetModels.some((m) => modelsRead.includes(m));
89
- }
package/src/logging.ts DELETED
@@ -1,15 +0,0 @@
1
- /**
2
- * Logger configuration. `true` enables console logging. A function can be provided for custom logging.
3
- */
4
- export type Logger = boolean | ((message: string) => void);
5
-
6
- /**
7
- * Logs a message using the provided logger.
8
- */
9
- export function log(logger: Logger, message: string) {
10
- if (typeof logger === 'function') {
11
- logger(message);
12
- } else if (logger) {
13
- console.log(message);
14
- }
15
- }