@vertz/fetch 0.2.11 → 0.2.13

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/dist/index.d.ts CHANGED
@@ -66,6 +66,32 @@ interface ListResponse<T> {
66
66
  nextCursor: string | null;
67
67
  hasNextPage: boolean;
68
68
  }
69
+ /** Metadata for entity-backed query descriptors. */
70
+ interface EntityQueryMeta {
71
+ readonly entityType: string;
72
+ readonly kind: "get" | "list";
73
+ readonly id?: string;
74
+ }
75
+ /** Metadata for mutation descriptors. */
76
+ interface MutationMeta {
77
+ readonly entityType: string;
78
+ readonly kind: "update" | "delete" | "create";
79
+ readonly id?: string;
80
+ readonly body?: unknown;
81
+ /** Skip MutationEventBus emission on commit. Defaults to false. */
82
+ readonly skipInvalidation?: boolean;
83
+ }
84
+ /**
85
+ * Callback interface for optimistic updates.
86
+ * Decouples @vertz/fetch from @vertz/ui — fetch defines the contract,
87
+ * ui provides the implementation via EntityStore.
88
+ */
89
+ interface OptimisticHandler {
90
+ /** Apply optimistic patch. Returns a rollback function. */
91
+ apply(meta: MutationMeta, mutationId: string): (() => void) | undefined;
92
+ /** Commit server-confirmed data after successful mutation. */
93
+ commit(meta: MutationMeta, mutationId: string, data: unknown): void;
94
+ }
69
95
  declare class FetchClient {
70
96
  private readonly config;
71
97
  /**
@@ -106,6 +132,19 @@ interface QueryDescriptor<
106
132
  readonly _tag: "QueryDescriptor";
107
133
  readonly _key: string;
108
134
  readonly _fetch: () => Promise<Result2<T, E>>;
135
+ /** Entity metadata for entity-backed queries. */
136
+ readonly _entity?: EntityQueryMeta;
137
+ /** Phantom field to carry the error type through generics. Never set at runtime. */
138
+ readonly _error?: E;
139
+ }
140
+ interface MutationDescriptor<
141
+ T,
142
+ E = FetchError
143
+ > extends PromiseLike<Result2<T, E>> {
144
+ readonly _tag: "MutationDescriptor";
145
+ readonly _key: string;
146
+ readonly _fetch: () => Promise<Result2<T, E>>;
147
+ readonly _mutation: MutationMeta;
109
148
  /** Phantom field to carry the error type through generics. Never set at runtime. */
110
149
  readonly _error?: E;
111
150
  }
@@ -113,7 +152,12 @@ declare function isQueryDescriptor<
113
152
  T,
114
153
  E = FetchError
115
154
  >(value: unknown): value is QueryDescriptor<T, E>;
116
- declare function createDescriptor<T>(method: string, path: string, fetchFn: () => Promise<FetchResponse<T>>, query?: Record<string, unknown>): QueryDescriptor<T>;
155
+ declare function createDescriptor<T>(method: string, path: string, fetchFn: () => Promise<FetchResponse<T>>, query?: Record<string, unknown>, entity?: EntityQueryMeta): QueryDescriptor<T>;
156
+ declare function isMutationDescriptor<
157
+ T,
158
+ E = FetchError
159
+ >(value: unknown): value is MutationDescriptor<T, E>;
160
+ declare function createMutationDescriptor<T>(method: string, path: string, fetchFn: () => Promise<FetchResponse<T>>, mutation: MutationMeta, handler?: OptimisticHandler): MutationDescriptor<T>;
117
161
  declare class FetchError2 extends Error {
118
162
  readonly status: number;
119
163
  readonly body?: unknown;
@@ -150,4 +194,4 @@ declare class ServiceUnavailableError extends FetchError2 {
150
194
  constructor(message: string, body?: unknown);
151
195
  }
152
196
  declare function createErrorFromStatus(status: number, message: string, body?: unknown): FetchError2;
153
- export { unwrapOr, unwrap, ok, matchError, isQueryDescriptor, isOk, isErr, err, createErrorFromStatus, createDescriptor, UnprocessableEntityError, UnauthorizedError, StreamingRequestOptions, StreamingFormat, ServiceUnavailableError, RetryConfig, Result3 as Result, RequestOptions, RateLimitError, QueryDescriptor, NotFoundError, ListResponse, InternalServerError, HooksConfig, GoneError, ForbiddenError, FetchResponse, FetchErrorType, FetchError2 as FetchError, FetchClientConfig, FetchClient, EntityErrorType, ConflictError, BadRequestError, AuthStrategy };
197
+ export { unwrapOr, unwrap, ok, matchError, isQueryDescriptor, isOk, isMutationDescriptor, isErr, err, createMutationDescriptor, createErrorFromStatus, createDescriptor, UnprocessableEntityError, UnauthorizedError, StreamingRequestOptions, StreamingFormat, ServiceUnavailableError, RetryConfig, Result3 as Result, RequestOptions, RateLimitError, QueryDescriptor, OptimisticHandler, NotFoundError, MutationMeta, MutationDescriptor, ListResponse, InternalServerError, HooksConfig, GoneError, ForbiddenError, FetchResponse, FetchErrorType, FetchError2 as FetchError, FetchClientConfig, FetchClient, EntityQueryMeta, EntityErrorType, ConflictError, BadRequestError, AuthStrategy };
package/dist/index.js CHANGED
@@ -349,7 +349,7 @@ import { ok as ok2 } from "@vertz/errors";
349
349
  function isQueryDescriptor(value) {
350
350
  return value !== null && typeof value === "object" && "_tag" in value && value._tag === "QueryDescriptor";
351
351
  }
352
- function createDescriptor(method, path, fetchFn, query) {
352
+ function createDescriptor(method, path, fetchFn, query, entity) {
353
353
  const key = `${method}:${path}${serializeQuery(query)}`;
354
354
  const fetchResult = async () => {
355
355
  const response = await fetchFn();
@@ -361,11 +361,48 @@ function createDescriptor(method, path, fetchFn, query) {
361
361
  _tag: "QueryDescriptor",
362
362
  _key: key,
363
363
  _fetch: fetchResult,
364
+ ...entity ? { _entity: entity } : {},
364
365
  then(onFulfilled, onRejected) {
365
366
  return fetchResult().then(onFulfilled, onRejected);
366
367
  }
367
368
  };
368
369
  }
370
+ function isMutationDescriptor(value) {
371
+ return value !== null && typeof value === "object" && "_tag" in value && value._tag === "MutationDescriptor";
372
+ }
373
+ function createMutationDescriptor(method, path, fetchFn, mutation, handler) {
374
+ const key = `${method}:${path}`;
375
+ let mutationCounter = 0;
376
+ const fetchResult = async () => {
377
+ const response = await fetchFn();
378
+ if (!response.ok)
379
+ return response;
380
+ return ok2(response.data.data);
381
+ };
382
+ return {
383
+ _tag: "MutationDescriptor",
384
+ _key: key,
385
+ _mutation: mutation,
386
+ _fetch: fetchResult,
387
+ then(onFulfilled, onRejected) {
388
+ const id = `m_${++mutationCounter}_${Date.now().toString(36)}`;
389
+ const rollback = handler?.apply(mutation, id);
390
+ return fetchResult().then((result) => {
391
+ if (result.ok) {
392
+ handler?.commit(mutation, id, result.data);
393
+ } else {
394
+ rollback?.();
395
+ }
396
+ return onFulfilled?.(result) ?? result;
397
+ }, (err2) => {
398
+ rollback?.();
399
+ if (onRejected)
400
+ return onRejected(err2);
401
+ throw err2;
402
+ });
403
+ }
404
+ };
405
+ }
369
406
  function serializeQuery(query) {
370
407
  if (!query)
371
408
  return "";
@@ -486,8 +523,10 @@ export {
486
523
  matchError,
487
524
  isQueryDescriptor,
488
525
  isOk,
526
+ isMutationDescriptor,
489
527
  isErr,
490
528
  err2 as err,
529
+ createMutationDescriptor,
491
530
  createErrorFromStatus,
492
531
  createDescriptor,
493
532
  UnprocessableEntityError,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vertz/fetch",
3
- "version": "0.2.11",
3
+ "version": "0.2.13",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "description": "Type-safe HTTP client for Vertz",
@@ -25,7 +25,7 @@
25
25
  "dist"
26
26
  ],
27
27
  "dependencies": {
28
- "@vertz/errors": "^0.2.11"
28
+ "@vertz/errors": "^0.2.12"
29
29
  },
30
30
  "scripts": {
31
31
  "build": "bunup",