@cratestack/refine 0.7.14 → 0.7.17
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 +151 -53
- package/dist/errors.d.ts +21 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +39 -0
- package/dist/errors.js.map +1 -0
- package/dist/filter-operators.d.ts +12 -0
- package/dist/filter-operators.d.ts.map +1 -0
- package/dist/filter-operators.js +60 -0
- package/dist/filter-operators.js.map +1 -0
- package/dist/filters.d.ts +17 -0
- package/dist/filters.d.ts.map +1 -0
- package/dist/filters.js +33 -0
- package/dist/filters.js.map +1 -0
- package/dist/index.d.ts +39 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +210 -0
- package/dist/index.js.map +1 -0
- package/dist/rpc-filters.d.ts +25 -0
- package/dist/rpc-filters.d.ts.map +1 -0
- package/dist/rpc-filters.js +40 -0
- package/dist/rpc-filters.js.map +1 -0
- package/dist/rpc-many.d.ts +16 -0
- package/dist/rpc-many.d.ts.map +1 -0
- package/dist/rpc-many.js +40 -0
- package/dist/rpc-many.js.map +1 -0
- package/dist/rpc-provider.d.ts +30 -0
- package/dist/rpc-provider.d.ts.map +1 -0
- package/dist/rpc-provider.js +159 -0
- package/dist/rpc-provider.js.map +1 -0
- package/dist/rpc-types.d.ts +95 -0
- package/dist/rpc-types.d.ts.map +1 -0
- package/dist/rpc-types.js +2 -0
- package/dist/rpc-types.js.map +1 -0
- package/dist/types.d.ts +86 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/version.d.ts +37 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +50 -0
- package/dist/version.js.map +1 -0
- package/package.json +3 -3
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import type { CratestackPage } from "./types.js";
|
|
2
|
+
/** Mirrors the generated RPC client's own `RpcListPredicate`
|
|
3
|
+
* (`crates/cratestack-client-typescript/templates/src/rpc-queries.ts.j2`)
|
|
4
|
+
* exactly — a single arbitrary `(key, value)` predicate, where `key`
|
|
5
|
+
* already carries the `field[__operator]` suffix (`toRpcQueryFilters` in
|
|
6
|
+
* `rpc-filters.ts` builds these). */
|
|
7
|
+
export interface RpcListPredicate {
|
|
8
|
+
key: string;
|
|
9
|
+
value: string;
|
|
10
|
+
}
|
|
11
|
+
/** Mirrors the generated RPC client's own `CratestackRpcListQuery`
|
|
12
|
+
* structurally — only the subset this package writes to (`filters`,
|
|
13
|
+
* `sort`, `limit`, `offset`). Unlike REST's `CratestackFetchQuery`,
|
|
14
|
+
* `sort` is a single already-joined string and `filters` is an array of
|
|
15
|
+
* {@link RpcListPredicate} rather than a `Record<string,string>` — see
|
|
16
|
+
* `rpc-filters.ts`'s doc comments for why each differs from its REST
|
|
17
|
+
* counterpart. `where`/`or`/`fields`/`include`/`includeFields` exist on
|
|
18
|
+
* the wire but this package never writes to them. */
|
|
19
|
+
export interface CratestackRpcListQuery {
|
|
20
|
+
limit?: number;
|
|
21
|
+
offset?: number;
|
|
22
|
+
fields?: string[];
|
|
23
|
+
include?: string[];
|
|
24
|
+
includeFields?: Record<string, string[]>;
|
|
25
|
+
sort?: string;
|
|
26
|
+
where?: string;
|
|
27
|
+
or?: string;
|
|
28
|
+
filters?: RpcListPredicate[];
|
|
29
|
+
}
|
|
30
|
+
/** Mirrors the generated RPC client's own `CratestackRpcCallOptions`
|
|
31
|
+
* (`rpc-runtime.ts.j2`) structurally — REST's `CratestackRequestConfig`
|
|
32
|
+
* sibling, plus `idempotencyKey` (RPC-only; propagated as the
|
|
33
|
+
* `Idempotency-Key` header). `headers` is where `If-Match` travels for
|
|
34
|
+
* `update`/`deleteOne` on a `@version` model — the RPC dispatch arms
|
|
35
|
+
* (`crates/cratestack-macros/src/transport/rpc.rs`) pass the real HTTP
|
|
36
|
+
* `HeaderMap` straight through to the same `handle_update_*_dispatch`/
|
|
37
|
+
* `handle_delete_*_dispatch` fns REST uses
|
|
38
|
+
* (`crates/cratestack-macros/src/axum/model/handlers_update.rs`), which
|
|
39
|
+
* read it via `parse_if_match_version` — so `If-Match` is enforced
|
|
40
|
+
* identically on both transports. */
|
|
41
|
+
export interface CratestackRpcCallOptions {
|
|
42
|
+
headers?: HeadersInit;
|
|
43
|
+
signal?: AbortSignal;
|
|
44
|
+
idempotencyKey?: string;
|
|
45
|
+
}
|
|
46
|
+
/** The structural surface a generated RPC model class (`client.widgets`,
|
|
47
|
+
* `client.ledgers`, …) needs for this package to drive it — matched
|
|
48
|
+
* against the real shape emitted by `cratestack generate-typescript`
|
|
49
|
+
* onto a `transport rpc` schema
|
|
50
|
+
* (`crates/cratestack-client-typescript/templates/src/rpc-client.ts.j2`).
|
|
51
|
+
* `get`/`create`/`update`/`delete` are positionally identical to REST's
|
|
52
|
+
* `CratestackModelApi` — only `list` differs, taking the query
|
|
53
|
+
* positionally (`list(query, options)`) instead of nested in an options
|
|
54
|
+
* object (REST's `list({ query, headers, signal })`). */
|
|
55
|
+
export interface CratestackRpcModelApi<TModel = unknown, TCreateInput = unknown, TUpdateInput = unknown> {
|
|
56
|
+
list(query?: CratestackRpcListQuery, options?: CratestackRpcCallOptions): Promise<TModel[] | CratestackPage<TModel>>;
|
|
57
|
+
get(id: unknown, options?: CratestackRpcCallOptions): Promise<TModel>;
|
|
58
|
+
/** Absent on the generated class entirely when the model declares no
|
|
59
|
+
* `@@allow("create", ...)` policy at all — same caveat as REST's
|
|
60
|
+
* `CratestackModelApi.create`, see the package README's
|
|
61
|
+
* "Policy-denied operations" note. */
|
|
62
|
+
create?(input: TCreateInput, options?: CratestackRpcCallOptions): Promise<TModel>;
|
|
63
|
+
update(id: unknown, patch: TUpdateInput, options?: CratestackRpcCallOptions): Promise<TModel>;
|
|
64
|
+
delete(id: unknown, options?: CratestackRpcCallOptions): Promise<void>;
|
|
65
|
+
}
|
|
66
|
+
/** One RPC resource's binding to a generated model class — the RPC
|
|
67
|
+
* sibling of REST's `ResourceConfig` in `types.ts`, same four facts
|
|
68
|
+
* (`api`, `primaryKey`, `paged`, optional `versionField`), same
|
|
69
|
+
* reasoning for why they can't be discovered at runtime and have to be
|
|
70
|
+
* supplied. Hand-write it (as every example in the README does), or
|
|
71
|
+
* generate it: `cratestack generate-typescript --refine` has emitted a
|
|
72
|
+
* real `refine.ts` typed to `RpcResourceMap` for `transport rpc` schemas
|
|
73
|
+
* since issue #571's RPC follow-up (#586 —
|
|
74
|
+
* `TypeScriptGeneratorError::RefineRequiresRest` was widened to
|
|
75
|
+
* `RefineRequiresRestOrRpc`). This package's own tests check both paths:
|
|
76
|
+
* `tests/typecheck/rpc-manifest.ts` builds one by hand against a real
|
|
77
|
+
* generated RPC client, and `tests/typecheck/generated-manifest-rpc.ts`
|
|
78
|
+
* checks the generator's own output round-trips into this type. */
|
|
79
|
+
export interface RpcResourceConfig<TModel = unknown, TCreateInput = unknown, TUpdateInput = unknown> {
|
|
80
|
+
api: CratestackRpcModelApi<TModel, TCreateInput, TUpdateInput>;
|
|
81
|
+
/** The schema's `@id` field name. refine assumes `id`; cratestack's
|
|
82
|
+
* `@id` may be on any field. */
|
|
83
|
+
primaryKey: string;
|
|
84
|
+
/** Mirrors whether the model declares `@@paged` — gates whether
|
|
85
|
+
* `.list()` returns `Page<TModel>` (with a real `totalCount`) or a
|
|
86
|
+
* bare `TModel[]`. */
|
|
87
|
+
paged: boolean;
|
|
88
|
+
/** Mirrors whether the model declares `@version`, and which field it's
|
|
89
|
+
* on. Omit for a model with no `@version` field — `update`/
|
|
90
|
+
* `deleteOne` then send no `If-Match` at all, matching the server's
|
|
91
|
+
* own behavior of not requiring one. */
|
|
92
|
+
versionField?: string;
|
|
93
|
+
}
|
|
94
|
+
export type RpcResourceMap = Record<string, RpcResourceConfig<any, any, any>>;
|
|
95
|
+
//# sourceMappingURL=rpc-types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rpc-types.d.ts","sourceRoot":"","sources":["../src/rpc-types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEjD;;;;sCAIsC;AACtC,MAAM,WAAW,gBAAgB;IAC/B,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;;;;;sDAOsD;AACtD,MAAM,WAAW,sBAAsB;IACrC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;IACzC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,OAAO,CAAC,EAAE,gBAAgB,EAAE,CAAC;CAC9B;AAED;;;;;;;;;;sCAUsC;AACtC,MAAM,WAAW,wBAAwB;IACvC,OAAO,CAAC,EAAE,WAAW,CAAC;IACtB,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;;;;;;;0DAQ0D;AAC1D,MAAM,WAAW,qBAAqB,CACpC,MAAM,GAAG,OAAO,EAChB,YAAY,GAAG,OAAO,EACtB,YAAY,GAAG,OAAO;IAEtB,IAAI,CACF,KAAK,CAAC,EAAE,sBAAsB,EAC9B,OAAO,CAAC,EAAE,wBAAwB,GACjC,OAAO,CAAC,MAAM,EAAE,GAAG,cAAc,CAAC,MAAM,CAAC,CAAC,CAAC;IAC9C,GAAG,CAAC,EAAE,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,wBAAwB,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACtE;;;2CAGuC;IACvC,MAAM,CAAC,CAAC,KAAK,EAAE,YAAY,EAAE,OAAO,CAAC,EAAE,wBAAwB,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAClF,MAAM,CAAC,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,YAAY,EAAE,OAAO,CAAC,EAAE,wBAAwB,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC9F,MAAM,CAAC,EAAE,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,wBAAwB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACxE;AAED;;;;;;;;;;;;oEAYoE;AACpE,MAAM,WAAW,iBAAiB,CAChC,MAAM,GAAG,OAAO,EAChB,YAAY,GAAG,OAAO,EACtB,YAAY,GAAG,OAAO;IAEtB,GAAG,EAAE,qBAAqB,CAAC,MAAM,EAAE,YAAY,EAAE,YAAY,CAAC,CAAC;IAC/D;qCACiC;IACjC,UAAU,EAAE,MAAM,CAAC;IACnB;;2BAEuB;IACvB,KAAK,EAAE,OAAO,CAAC;IACf;;;6CAGyC;IACzC,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAKD,MAAM,MAAM,cAAc,GAAG,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rpc-types.js","sourceRoot":"","sources":["../src/rpc-types.ts"],"names":[],"mappings":""}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/** The structural surface a generated model class (`client.widgets`,
|
|
2
|
+
* `client.ledgers`, …) needs for this package to drive it — matched
|
|
3
|
+
* against the real shape emitted by `cratestack generate-typescript`
|
|
4
|
+
* (`crates/cratestack-client-typescript/templates/src/client.ts.j2`),
|
|
5
|
+
* not a hand-invented interface. Each generated model class is its own
|
|
6
|
+
* concrete TypeScript class with no shared base type the generator
|
|
7
|
+
* exports, so this is duck-typed rather than an `implements` target —
|
|
8
|
+
* every real generated class satisfies it structurally. */
|
|
9
|
+
export interface CratestackModelApi<TModel = unknown, TCreateInput = unknown, TUpdateInput = unknown> {
|
|
10
|
+
list(options?: {
|
|
11
|
+
query?: CratestackFetchQuery;
|
|
12
|
+
headers?: HeadersInit;
|
|
13
|
+
signal?: AbortSignal;
|
|
14
|
+
}): Promise<TModel[] | CratestackPage<TModel>>;
|
|
15
|
+
get(id: unknown, options?: {
|
|
16
|
+
query?: CratestackFetchQuery;
|
|
17
|
+
headers?: HeadersInit;
|
|
18
|
+
signal?: AbortSignal;
|
|
19
|
+
}): Promise<TModel>;
|
|
20
|
+
/** Absent on the generated class entirely when the model declares no
|
|
21
|
+
* `@@allow("create", ...)` policy at all — see the package README's
|
|
22
|
+
* "Policy-denied operations" note for why that's a different, weaker
|
|
23
|
+
* guarantee than "the current caller can create one". */
|
|
24
|
+
create?(input: TCreateInput, options?: CratestackRequestConfig): Promise<TModel>;
|
|
25
|
+
update(id: unknown, input: TUpdateInput, options?: CratestackRequestConfig): Promise<TModel>;
|
|
26
|
+
delete(id: unknown, options?: CratestackRequestConfig): Promise<void>;
|
|
27
|
+
}
|
|
28
|
+
/** Mirrors the generated client's own `CratestackFetchQuery`
|
|
29
|
+
* (`queries.ts.j2`) structurally — only the subset this package writes
|
|
30
|
+
* to (`filters`, `sort`, `limit`, `offset`). */
|
|
31
|
+
export interface CratestackFetchQuery {
|
|
32
|
+
filters?: Record<string, string>;
|
|
33
|
+
sort?: string[];
|
|
34
|
+
limit?: number;
|
|
35
|
+
offset?: number;
|
|
36
|
+
}
|
|
37
|
+
export interface CratestackRequestConfig {
|
|
38
|
+
signal?: AbortSignal;
|
|
39
|
+
headers?: HeadersInit;
|
|
40
|
+
}
|
|
41
|
+
/** Mirrors `cratestack_core::page::Page<T>` (`models.ts.j2`) — only the
|
|
42
|
+
* `items`/`totalCount` fields this package reads. */
|
|
43
|
+
export interface CratestackPage<T> {
|
|
44
|
+
items: T[];
|
|
45
|
+
totalCount: number | null;
|
|
46
|
+
}
|
|
47
|
+
/** Structural match for the generated client's `CratestackHttpError`
|
|
48
|
+
* (`rest-runtime.ts.j2`) — checked with {@link isCratestackHttpError}
|
|
49
|
+
* rather than `instanceof`, since that class has no shared import path
|
|
50
|
+
* this package can reference (it's regenerated per consumer, into
|
|
51
|
+
* their own package). */
|
|
52
|
+
export interface CratestackHttpErrorLike {
|
|
53
|
+
status: number;
|
|
54
|
+
response?: unknown;
|
|
55
|
+
payload?: unknown;
|
|
56
|
+
message?: string;
|
|
57
|
+
}
|
|
58
|
+
/** One resource's binding to a generated model class. Every field here
|
|
59
|
+
* is a fact about the `.cstack` model that the generated client's own
|
|
60
|
+
* TypeScript types encode at compile time but expose nowhere at
|
|
61
|
+
* runtime — there is no `client.widgets.$meta` object to introspect,
|
|
62
|
+
* confirmed by reading the generated `client.ts`/`models.ts` output.
|
|
63
|
+
*
|
|
64
|
+
* Hand-write it, or let `cratestack generate-typescript --refine` emit
|
|
65
|
+
* it from the schema those facts came from — see the README's "A
|
|
66
|
+
* runtime package with a generated manifest". */
|
|
67
|
+
export interface ResourceConfig<TModel = unknown, TCreateInput = unknown, TUpdateInput = unknown> {
|
|
68
|
+
api: CratestackModelApi<TModel, TCreateInput, TUpdateInput>;
|
|
69
|
+
/** The schema's `@id` field name. refine assumes `id`; cratestack's
|
|
70
|
+
* `@id` may be on any field (`crates/cratestack-client-typescript/src/types.rs::primary_key_field`). */
|
|
71
|
+
primaryKey: string;
|
|
72
|
+
/** Mirrors whether the model declares `@@paged` — gates whether
|
|
73
|
+
* `.list()` returns `Page<TModel>` (with a real `totalCount`) or a
|
|
74
|
+
* bare `TModel[]`. A non-`@@paged` resource cannot report a true
|
|
75
|
+
* `total` beyond what one page returned — see the package README's
|
|
76
|
+
* "Pagination" section for why, and set `false` here honestly rather
|
|
77
|
+
* than treating a non-paged model as paged. */
|
|
78
|
+
paged: boolean;
|
|
79
|
+
/** Mirrors whether the model declares `@version`, and which field
|
|
80
|
+
* it's on. Omit for a model with no `@version` field — `update`/
|
|
81
|
+
* `deleteOne` then send no `If-Match` at all, matching the server's
|
|
82
|
+
* own behavior of not requiring one. */
|
|
83
|
+
versionField?: string;
|
|
84
|
+
}
|
|
85
|
+
export type ResourceMap = Record<string, ResourceConfig<any, any, any>>;
|
|
86
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;;4DAO4D;AAC5D,MAAM,WAAW,kBAAkB,CACjC,MAAM,GAAG,OAAO,EAChB,YAAY,GAAG,OAAO,EACtB,YAAY,GAAG,OAAO;IAEtB,IAAI,CAAC,OAAO,CAAC,EAAE;QACb,KAAK,CAAC,EAAE,oBAAoB,CAAC;QAC7B,OAAO,CAAC,EAAE,WAAW,CAAC;QACtB,MAAM,CAAC,EAAE,WAAW,CAAC;KACtB,GAAG,OAAO,CAAC,MAAM,EAAE,GAAG,cAAc,CAAC,MAAM,CAAC,CAAC,CAAC;IAC/C,GAAG,CACD,EAAE,EAAE,OAAO,EACX,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,oBAAoB,CAAC;QAAC,OAAO,CAAC,EAAE,WAAW,CAAC;QAAC,MAAM,CAAC,EAAE,WAAW,CAAA;KAAE,GACtF,OAAO,CAAC,MAAM,CAAC,CAAC;IACnB;;;8DAG0D;IAC1D,MAAM,CAAC,CAAC,KAAK,EAAE,YAAY,EAAE,OAAO,CAAC,EAAE,uBAAuB,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACjF,MAAM,CAAC,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,YAAY,EAAE,OAAO,CAAC,EAAE,uBAAuB,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAC7F,MAAM,CAAC,EAAE,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,uBAAuB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACvE;AAED;;iDAEiD;AACjD,MAAM,WAAW,oBAAoB;IACnC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,uBAAuB;IACtC,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,OAAO,CAAC,EAAE,WAAW,CAAC;CACvB;AAED;sDACsD;AACtD,MAAM,WAAW,cAAc,CAAC,CAAC;IAC/B,KAAK,EAAE,CAAC,EAAE,CAAC;IACX,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED;;;;0BAI0B;AAC1B,MAAM,WAAW,uBAAuB;IACtC,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;;;kDAQkD;AAClD,MAAM,WAAW,cAAc,CAAC,MAAM,GAAG,OAAO,EAAE,YAAY,GAAG,OAAO,EAAE,YAAY,GAAG,OAAO;IAC9F,GAAG,EAAE,kBAAkB,CAAC,MAAM,EAAE,YAAY,EAAE,YAAY,CAAC,CAAC;IAC5D;6GACyG;IACzG,UAAU,EAAE,MAAM,CAAC;IACnB;;;;;oDAKgD;IAChD,KAAK,EAAE,OAAO,CAAC;IACf;;;6CAGyC;IACzC,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAQD,MAAM,MAAM,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/** The one fact this module needs out of either transport's resource
|
|
2
|
+
* config (`ResourceConfig` in `types.ts`, `RpcResourceConfig` in
|
|
3
|
+
* `rpc-types.ts`) — deliberately narrower than either so this module
|
|
4
|
+
* stays shared between both `createCratestackDataProvider` and
|
|
5
|
+
* `createCratestackRpcDataProvider` rather than picking one transport's
|
|
6
|
+
* type and asking the other to import it. Both real config types satisfy
|
|
7
|
+
* this structurally already; nothing about them changes. */
|
|
8
|
+
export interface VersionedResourceConfig {
|
|
9
|
+
versionField?: string;
|
|
10
|
+
}
|
|
11
|
+
/** `If-Match` is required on both `PATCH` (update) and `DELETE` for a
|
|
12
|
+
* `@version` model (cratestack#493/#519, closed by #538 — the
|
|
13
|
+
* delete-side enforcement deliberately mirrors the update path
|
|
14
|
+
* token-for-token, `crates/cratestack-macros/src/axum/model/prep/etag.rs`).
|
|
15
|
+
* refine's `update`/`deleteOne` hooks fetch the record before editing it
|
|
16
|
+
* (`useOne`/`useShow` populate the edit form; a list/detail view is
|
|
17
|
+
* usually on screen before a delete button is clicked), so the version
|
|
18
|
+
* is available by the time a mutation fires — it just isn't part of
|
|
19
|
+
* refine's `UpdateParams`/`DeleteOneParams` by default. This cache,
|
|
20
|
+
* populated by every read and write that returns a fresh record, is how
|
|
21
|
+
* the provider threads it through. One cache per `createCratestackDataProvider`
|
|
22
|
+
* call, not module-global, so two providers (e.g. two tenants in the
|
|
23
|
+
* same process) never cross-pollinate versions. */
|
|
24
|
+
export declare function createVersionCache(): Map<string, number>;
|
|
25
|
+
export declare function rememberVersion(cache: Map<string, number>, resource: string, id: unknown, record: Record<string, unknown>, config: VersionedResourceConfig): void;
|
|
26
|
+
export declare function forgetVersion(cache: Map<string, number>, resource: string, id: unknown): void;
|
|
27
|
+
/** Builds the `If-Match` header for a `@version` model's update/delete,
|
|
28
|
+
* or `{}` for a model with no `versionField` configured (matching the
|
|
29
|
+
* server's own behavior of not requiring one). Throws rather than
|
|
30
|
+
* silently omitting `If-Match` when a version is required but unknown
|
|
31
|
+
* — sending an update/delete with no `If-Match` against a `@version`
|
|
32
|
+
* model doesn't fail loudly on the wire (cratestack#519/#538 gate on
|
|
33
|
+
* the header's *presence*, so a caller can accidentally opt out of the
|
|
34
|
+
* safety net exactly when it matters most), so this package refuses to
|
|
35
|
+
* make that mistake on the caller's behalf. */
|
|
36
|
+
export declare function ifMatchHeaders(cache: Map<string, number>, resource: string, id: unknown, config: VersionedResourceConfig, override?: number): HeadersInit;
|
|
37
|
+
//# sourceMappingURL=version.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAAA;;;;;;6DAM6D;AAC7D,MAAM,WAAW,uBAAuB;IACtC,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;;;;;;;;;;;oDAYoD;AACpD,wBAAgB,kBAAkB,IAAI,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAExD;AAMD,wBAAgB,eAAe,CAC7B,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EAC1B,QAAQ,EAAE,MAAM,EAChB,EAAE,EAAE,OAAO,EACX,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,MAAM,EAAE,uBAAuB,GAC9B,IAAI,CAMN;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,OAAO,GAAG,IAAI,CAE7F;AAED;;;;;;;;gDAQgD;AAChD,wBAAgB,cAAc,CAC5B,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EAC1B,QAAQ,EAAE,MAAM,EAChB,EAAE,EAAE,OAAO,EACX,MAAM,EAAE,uBAAuB,EAC/B,QAAQ,CAAC,EAAE,MAAM,GAChB,WAAW,CAUb"}
|
package/dist/version.js
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/** `If-Match` is required on both `PATCH` (update) and `DELETE` for a
|
|
2
|
+
* `@version` model (cratestack#493/#519, closed by #538 — the
|
|
3
|
+
* delete-side enforcement deliberately mirrors the update path
|
|
4
|
+
* token-for-token, `crates/cratestack-macros/src/axum/model/prep/etag.rs`).
|
|
5
|
+
* refine's `update`/`deleteOne` hooks fetch the record before editing it
|
|
6
|
+
* (`useOne`/`useShow` populate the edit form; a list/detail view is
|
|
7
|
+
* usually on screen before a delete button is clicked), so the version
|
|
8
|
+
* is available by the time a mutation fires — it just isn't part of
|
|
9
|
+
* refine's `UpdateParams`/`DeleteOneParams` by default. This cache,
|
|
10
|
+
* populated by every read and write that returns a fresh record, is how
|
|
11
|
+
* the provider threads it through. One cache per `createCratestackDataProvider`
|
|
12
|
+
* call, not module-global, so two providers (e.g. two tenants in the
|
|
13
|
+
* same process) never cross-pollinate versions. */
|
|
14
|
+
export function createVersionCache() {
|
|
15
|
+
return new Map();
|
|
16
|
+
}
|
|
17
|
+
function versionKey(resource, id) {
|
|
18
|
+
return `${resource}:${String(id)}`;
|
|
19
|
+
}
|
|
20
|
+
export function rememberVersion(cache, resource, id, record, config) {
|
|
21
|
+
if (!config.versionField)
|
|
22
|
+
return;
|
|
23
|
+
const version = record[config.versionField];
|
|
24
|
+
if (typeof version === "number") {
|
|
25
|
+
cache.set(versionKey(resource, id), version);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
export function forgetVersion(cache, resource, id) {
|
|
29
|
+
cache.delete(versionKey(resource, id));
|
|
30
|
+
}
|
|
31
|
+
/** Builds the `If-Match` header for a `@version` model's update/delete,
|
|
32
|
+
* or `{}` for a model with no `versionField` configured (matching the
|
|
33
|
+
* server's own behavior of not requiring one). Throws rather than
|
|
34
|
+
* silently omitting `If-Match` when a version is required but unknown
|
|
35
|
+
* — sending an update/delete with no `If-Match` against a `@version`
|
|
36
|
+
* model doesn't fail loudly on the wire (cratestack#519/#538 gate on
|
|
37
|
+
* the header's *presence*, so a caller can accidentally opt out of the
|
|
38
|
+
* safety net exactly when it matters most), so this package refuses to
|
|
39
|
+
* make that mistake on the caller's behalf. */
|
|
40
|
+
export function ifMatchHeaders(cache, resource, id, config, override) {
|
|
41
|
+
if (!config.versionField)
|
|
42
|
+
return {};
|
|
43
|
+
const version = override ?? cache.get(versionKey(resource, id));
|
|
44
|
+
if (version === undefined) {
|
|
45
|
+
throw new Error(`no known version for ${resource}/${String(id)} — call getOne (or getList) before ` +
|
|
46
|
+
"update/delete on a @version model, or pass meta: { ifMatch: <version> } explicitly");
|
|
47
|
+
}
|
|
48
|
+
return { "If-Match": `"${version}"` };
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=version.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"version.js","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAWA;;;;;;;;;;;;oDAYoD;AACpD,MAAM,UAAU,kBAAkB;IAChC,OAAO,IAAI,GAAG,EAAE,CAAC;AACnB,CAAC;AAED,SAAS,UAAU,CAAC,QAAgB,EAAE,EAAW;IAC/C,OAAO,GAAG,QAAQ,IAAI,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC;AACrC,CAAC;AAED,MAAM,UAAU,eAAe,CAC7B,KAA0B,EAC1B,QAAgB,EAChB,EAAW,EACX,MAA+B,EAC/B,MAA+B;IAE/B,IAAI,CAAC,MAAM,CAAC,YAAY;QAAE,OAAO;IACjC,MAAM,OAAO,GAAG,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC;IAC5C,IAAI,OAAO,OAAO,KAAK,QAAQ,EAAE,CAAC;QAChC,KAAK,CAAC,GAAG,CAAC,UAAU,CAAC,QAAQ,EAAE,EAAE,CAAC,EAAE,OAAO,CAAC,CAAC;IAC/C,CAAC;AACH,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,KAA0B,EAAE,QAAgB,EAAE,EAAW;IACrF,KAAK,CAAC,MAAM,CAAC,UAAU,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,CAAC;AACzC,CAAC;AAED;;;;;;;;gDAQgD;AAChD,MAAM,UAAU,cAAc,CAC5B,KAA0B,EAC1B,QAAgB,EAChB,EAAW,EACX,MAA+B,EAC/B,QAAiB;IAEjB,IAAI,CAAC,MAAM,CAAC,YAAY;QAAE,OAAO,EAAE,CAAC;IACpC,MAAM,OAAO,GAAG,QAAQ,IAAI,KAAK,CAAC,GAAG,CAAC,UAAU,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,CAAC;IAChE,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;QAC1B,MAAM,IAAI,KAAK,CACb,wBAAwB,QAAQ,IAAI,MAAM,CAAC,EAAE,CAAC,qCAAqC;YACjF,oFAAoF,CACvF,CAAC;IACJ,CAAC;IACD,OAAO,EAAE,UAAU,EAAE,IAAI,OAAO,GAAG,EAAE,CAAC;AACxC,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cratestack/refine",
|
|
3
|
-
"version": "0.7.
|
|
4
|
-
"description": "A refine.dev DataProvider over CrateStack's generated TypeScript REST
|
|
3
|
+
"version": "0.7.17",
|
|
4
|
+
"description": "A refine.dev DataProvider over CrateStack's generated TypeScript client (REST or RPC) — the safe end-user admin-UI surface that goes through the generated API (policy, validation, @version concurrency, audit), unlike cratestack-studio's direct-to-database sysadmin access.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
],
|
|
36
36
|
"scripts": {
|
|
37
37
|
"build": "tsc -p tsconfig.json",
|
|
38
|
-
"test": "vitest run",
|
|
38
|
+
"test": "tsc --noEmit -p tsconfig.typecheck.json && vitest run",
|
|
39
39
|
"lint": "biome check ."
|
|
40
40
|
},
|
|
41
41
|
"peerDependencies": {
|