@jayalfredprufrock/mobx-toolbox 0.6.0 → 0.6.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/dist/model.d.mts +40 -12
- package/dist/model.d.mts.map +1 -1
- package/dist/model.mjs +43 -6
- package/dist/model.mjs.map +1 -1
- package/package.json +1 -1
package/dist/model.d.mts
CHANGED
|
@@ -7,17 +7,23 @@ import * as T from "typebox";
|
|
|
7
7
|
interface ModelStore<M> {
|
|
8
8
|
remove?(model: M): void;
|
|
9
9
|
}
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
10
|
+
/**
|
|
11
|
+
* Root schema for a model: a single object (`makeModel`) or a discriminated
|
|
12
|
+
* union of objects (`makeUnionModel`). Shared by both factories and by
|
|
13
|
+
* `makeStore`, which accepts either.
|
|
14
|
+
*/
|
|
15
|
+
type ModelSchema = T.TObject | T.TUnion<T.TObject[]>;
|
|
16
|
+
type Resource<S extends ModelSchema> = T.Static<S>;
|
|
17
|
+
type KeyShape<S extends ModelSchema, K extends readonly (keyof Resource<S>)[]> = K extends readonly [] ? undefined : Pick<Resource<S>, K[number]>;
|
|
18
|
+
type KeyedFn<S extends ModelSchema, K extends readonly (keyof Resource<S>)[], R> = K extends readonly [] ? (...args: any[]) => Promise<R> : (params: KeyShape<S, K>, ...rest: any[]) => Promise<R>;
|
|
19
|
+
type KeyedBodyFn<S extends ModelSchema, K extends readonly (keyof Resource<S>)[], R> = K extends readonly [] ? (body: any, ...rest: any[]) => Promise<R> : (params: KeyShape<S, K>, body: any, ...rest: any[]) => Promise<R>;
|
|
14
20
|
type StripParams<K extends readonly any[], F> = K extends readonly [] ? F : F extends ((params: any, ...rest: infer R) => infer Ret) ? (...args: R) => Ret : never;
|
|
15
21
|
type ReplaceReturn$1<F, R> = F extends ((...args: infer A) => Promise<any>) ? (...args: A) => Promise<R> : never;
|
|
16
22
|
type ReservedActionKey = "reload" | "update" | "delete" | "setData" | "toJSON" | "store";
|
|
17
|
-
type ActionsConfig<S extends
|
|
23
|
+
type ActionsConfig<S extends ModelSchema, K extends readonly (keyof Resource<S>)[]> = {
|
|
18
24
|
[name: string]: KeyedFn<S, K, Resource<S>>;
|
|
19
25
|
} & { [Key in ReservedActionKey]?: never };
|
|
20
|
-
interface ModelConfig<S extends
|
|
26
|
+
interface ModelConfig<S extends ModelSchema, K extends readonly (keyof Resource<S>)[]> {
|
|
21
27
|
keys: K;
|
|
22
28
|
reload?: KeyedFn<S, K, Resource<S>>;
|
|
23
29
|
update?: KeyedBodyFn<S, K, Resource<S>>;
|
|
@@ -39,19 +45,41 @@ type ModelMethods<K extends readonly any[], Cfg> = (Cfg extends {
|
|
|
39
45
|
} : {}) & (Cfg extends {
|
|
40
46
|
actions: infer A;
|
|
41
47
|
} ? { [N in keyof A]: A[N] extends ((...args: any[]) => any) ? ReplaceReturn$1<StripParams<K, A[N]>, any> : never } : {});
|
|
42
|
-
type ModelInstance<S extends
|
|
48
|
+
type ModelInstance<S extends ModelSchema, K extends readonly any[], Cfg> = Resource<S> & {
|
|
43
49
|
readonly store?: ModelStore<any>;
|
|
44
|
-
setData(data:
|
|
50
|
+
setData(data: Resource<S>): void;
|
|
45
51
|
toJSON(): Resource<S>;
|
|
46
52
|
buildParams(): KeyShape<S, K extends readonly (keyof Resource<S>)[] ? K : readonly []>;
|
|
47
53
|
getMobxAnnotations?(): AnnotationsMap<any, never>;
|
|
48
54
|
} & ModelMethods<K, Cfg>;
|
|
49
|
-
type ModelConstructor<S extends
|
|
55
|
+
type ModelConstructor<S extends ModelSchema, K extends readonly any[], Cfg> = {
|
|
50
56
|
new (data: Resource<S>, store?: ModelStore<any>): ModelInstance<S, K, Cfg>;
|
|
51
57
|
readonly schema: S;
|
|
52
58
|
};
|
|
53
59
|
declare function makeModel<S extends T.TObject>(schema: S): ModelConstructor<S, readonly [], {}>;
|
|
54
60
|
declare function makeModel<S extends T.TObject, K extends readonly (keyof Resource<S>)[], Cfg extends ModelConfig<S, K>>(schema: S, config: Cfg): ModelConstructor<S, K, Cfg>;
|
|
61
|
+
type UnionSchema = T.TUnion<T.TObject[]>;
|
|
62
|
+
type SharedFields<S extends UnionSchema> = { [K in keyof Resource<S>]: Resource<S>[K] };
|
|
63
|
+
type VariantFields<S extends UnionSchema, D extends keyof Resource<S>, V> = Extract<Resource<S>, Record<D, V>>;
|
|
64
|
+
interface UnionModelMembers<S extends UnionSchema, D extends keyof Resource<S>, K extends readonly any[]> {
|
|
65
|
+
readonly store?: ModelStore<any>;
|
|
66
|
+
setData(data: Resource<S>): void;
|
|
67
|
+
toJSON(): Resource<S>;
|
|
68
|
+
buildParams(): KeyShape<S, K extends readonly (keyof Resource<S>)[] ? K : readonly []>;
|
|
69
|
+
getMobxAnnotations?(): AnnotationsMap<any, never>;
|
|
70
|
+
/** Type guard: true when the discriminator equals `value`, revealing that variant's fields on this same instance. */
|
|
71
|
+
is<V extends Resource<S>[D]>(value: V): this is this & VariantFields<S, D, V>;
|
|
72
|
+
/** This instance narrowed to the `value` variant (fields exposed directly), or `undefined` if it doesn't match. */
|
|
73
|
+
as<V extends Resource<S>[D]>(value: V): (this & VariantFields<S, D, V>) | undefined;
|
|
74
|
+
}
|
|
75
|
+
type UnionModelInstance<S extends UnionSchema, D extends keyof Resource<S>, K extends readonly any[], Cfg> = SharedFields<S> & UnionModelMembers<S, D, K> & ModelMethods<K, Cfg>;
|
|
76
|
+
type UnionModelConstructor<S extends UnionSchema, D extends keyof Resource<S>, K extends readonly any[], Cfg> = {
|
|
77
|
+
new (data: Resource<S>, store?: ModelStore<any>): UnionModelInstance<S, D, K, Cfg>;
|
|
78
|
+
readonly schema: S;
|
|
79
|
+
readonly discriminator: D;
|
|
80
|
+
};
|
|
81
|
+
declare function makeUnionModel<S extends UnionSchema, D extends keyof Resource<S> & string>(schema: S, discriminator: D): UnionModelConstructor<S, D, readonly [], {}>;
|
|
82
|
+
declare function makeUnionModel<S extends UnionSchema, D extends keyof Resource<S> & string, K extends readonly (keyof Resource<S>)[], Cfg extends ModelConfig<S, K>>(schema: S, discriminator: D, config: Cfg): UnionModelConstructor<S, D, K, Cfg>;
|
|
55
83
|
//#endregion
|
|
56
84
|
//#region src/model/make-store.d.ts
|
|
57
85
|
type ReplaceReturn<F, R> = F extends ((...args: infer A) => Promise<any>) ? (...args: A) => Promise<R> : never;
|
|
@@ -88,8 +116,8 @@ type StoreInstance<M, Cfg> = {
|
|
|
88
116
|
type StoreConstructor<M, Cfg> = {
|
|
89
117
|
new (): StoreInstance<M, Cfg>;
|
|
90
118
|
};
|
|
91
|
-
declare function makeStore<S extends
|
|
92
|
-
declare function makeStore<S extends
|
|
119
|
+
declare function makeStore<S extends ModelSchema>(schema: S): StoreConstructor<T.Static<S>, {}>;
|
|
120
|
+
declare function makeStore<S extends ModelSchema, Cfg extends StoreConfig<T.Static<S>>>(schema: S, config: Cfg & ThisType<StoreThis<InferModel<T.Static<S>, Cfg>>>): StoreConstructor<InferModel<T.Static<S>, Cfg>, Cfg>;
|
|
93
121
|
//#endregion
|
|
94
|
-
export { type AnnotationsMap, type LazyObservableArray, ModelConfig, ModelConstructor, ModelStore, StoreConfig, StoreConstructor, makeModel, makeStore };
|
|
122
|
+
export { type AnnotationsMap, type LazyObservableArray, ModelConfig, ModelConstructor, ModelSchema, ModelStore, StoreConfig, StoreConstructor, UnionModelConstructor, makeModel, makeStore, makeUnionModel };
|
|
95
123
|
//# sourceMappingURL=model.d.mts.map
|
package/dist/model.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"model.d.mts","names":[],"sources":["../src/model/make-model.ts","../src/model/make-store.ts"],"mappings":";;;;;;
|
|
1
|
+
{"version":3,"file":"model.d.mts","names":[],"sources":["../src/model/make-model.ts","../src/model/make-store.ts"],"mappings":"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"}
|
package/dist/model.mjs
CHANGED
|
@@ -1,17 +1,32 @@
|
|
|
1
1
|
import { n as lazyObservableArray } from "./lazy-observable-CdHXpUkk.mjs";
|
|
2
2
|
import { action, makeObservable, observable, runInAction, toJS } from "mobx";
|
|
3
|
-
import "typebox";
|
|
3
|
+
import * as Value from "typebox/value";
|
|
4
|
+
import * as T from "typebox";
|
|
4
5
|
|
|
5
6
|
//#region src/model/make-model.ts
|
|
6
|
-
|
|
7
|
+
/**
|
|
8
|
+
* Every property name across the schema. For a union this is the merged set of
|
|
9
|
+
* all variants' keys, so all of them are made observable up front — that keeps
|
|
10
|
+
* `setData` reactive even when it switches the active variant. `toJSON` runs
|
|
11
|
+
* `Value.Clean` to emit only the keys of the variant the data currently matches.
|
|
12
|
+
*/
|
|
13
|
+
function getPropertyNames(schema) {
|
|
14
|
+
if (!T.IsUnion(schema)) return Object.keys(schema.properties);
|
|
15
|
+
const names = /* @__PURE__ */ new Set();
|
|
16
|
+
for (const variant of schema.anyOf) for (const key of Object.keys(variant.properties)) names.add(key);
|
|
17
|
+
return [...names];
|
|
18
|
+
}
|
|
19
|
+
function createModelClass(schema, config) {
|
|
7
20
|
const keys = config?.keys ?? [];
|
|
21
|
+
const isUnion = T.IsUnion(schema);
|
|
22
|
+
const propertyNames = getPropertyNames(schema);
|
|
8
23
|
class BaseModel {
|
|
9
24
|
static schema = schema;
|
|
10
25
|
store;
|
|
11
26
|
constructor(data, store) {
|
|
12
27
|
this.store = store;
|
|
13
28
|
const annotations = {};
|
|
14
|
-
for (const key of
|
|
29
|
+
for (const key of propertyNames) {
|
|
15
30
|
Object.defineProperty(this, key, {
|
|
16
31
|
value: data[key],
|
|
17
32
|
enumerable: true,
|
|
@@ -26,8 +41,14 @@ function makeModel(schema, config) {
|
|
|
26
41
|
...this.getMobxAnnotations?.()
|
|
27
42
|
});
|
|
28
43
|
}
|
|
44
|
+
/**
|
|
45
|
+
* Replace the model's data with a complete resource. Every property is
|
|
46
|
+
* reassigned (fields absent from `data` — e.g. another variant's — become
|
|
47
|
+
* `undefined`), so the model always holds a coherent, whole variant rather
|
|
48
|
+
* than a partial merge that could mix fields across the union.
|
|
49
|
+
*/
|
|
29
50
|
setData(data) {
|
|
30
|
-
|
|
51
|
+
for (const key of propertyNames) this[key] = data[key];
|
|
31
52
|
}
|
|
32
53
|
/**
|
|
33
54
|
* Build the params object passed as the first arg to keyed API methods.
|
|
@@ -42,10 +63,11 @@ function makeModel(schema, config) {
|
|
|
42
63
|
}
|
|
43
64
|
toJSON() {
|
|
44
65
|
const data = this;
|
|
45
|
-
|
|
66
|
+
const snapshot = propertyNames.reduce((obj, key) => {
|
|
46
67
|
if (data[key] !== void 0) obj[key] = toJS(data[key]);
|
|
47
68
|
return obj;
|
|
48
69
|
}, {});
|
|
70
|
+
return isUnion ? Value.Clean(schema, snapshot) : snapshot;
|
|
49
71
|
}
|
|
50
72
|
}
|
|
51
73
|
const proto = BaseModel.prototype;
|
|
@@ -89,6 +111,21 @@ function makeModel(schema, config) {
|
|
|
89
111
|
}
|
|
90
112
|
return BaseModel;
|
|
91
113
|
}
|
|
114
|
+
function makeModel(schema, config) {
|
|
115
|
+
return createModelClass(schema, config);
|
|
116
|
+
}
|
|
117
|
+
function makeUnionModel(schema, discriminator, config) {
|
|
118
|
+
const ModelClass = createModelClass(schema, config);
|
|
119
|
+
ModelClass.discriminator = discriminator;
|
|
120
|
+
const proto = ModelClass.prototype;
|
|
121
|
+
proto.is = function(value) {
|
|
122
|
+
return this[discriminator] === value;
|
|
123
|
+
};
|
|
124
|
+
proto.as = function(value) {
|
|
125
|
+
return this[discriminator] === value ? this : void 0;
|
|
126
|
+
};
|
|
127
|
+
return ModelClass;
|
|
128
|
+
}
|
|
92
129
|
|
|
93
130
|
//#endregion
|
|
94
131
|
//#region src/model/make-store.ts
|
|
@@ -136,5 +173,5 @@ function makeStore(schema, config) {
|
|
|
136
173
|
}
|
|
137
174
|
|
|
138
175
|
//#endregion
|
|
139
|
-
export { makeModel, makeStore };
|
|
176
|
+
export { makeModel, makeStore, makeUnionModel };
|
|
140
177
|
//# sourceMappingURL=model.mjs.map
|
package/dist/model.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"model.mjs","names":[],"sources":["../src/model/make-model.ts","../src/model/make-store.ts"],"sourcesContent":["import { action, makeObservable, observable, runInAction, toJS, type AnnotationsMap } from \"mobx\";\nimport * as T from \"typebox\";\n\n// -----------------------------------------------------------------------------\n// Public structural contract\n// -----------------------------------------------------------------------------\n\n/** Minimal interface a model's host store must satisfy. */\nexport interface ModelStore<M> {\n remove?(model: M): void;\n}\n\n// -----------------------------------------------------------------------------\n// Type plumbing\n// -----------------------------------------------------------------------------\n\ntype Resource<S extends T.TObject> = T.Static<S>;\n\ntype KeyShape<S extends T.TObject, K extends readonly (keyof Resource<S>)[]> = K extends readonly []\n ? undefined\n : Pick<Resource<S>, K[number]>;\n\ntype KeyedFn<\n S extends T.TObject,\n K extends readonly (keyof Resource<S>)[],\n R,\n> = K extends readonly []\n ? (...args: any[]) => Promise<R>\n : (params: KeyShape<S, K>, ...rest: any[]) => Promise<R>;\n\ntype KeyedBodyFn<\n S extends T.TObject,\n K extends readonly (keyof Resource<S>)[],\n R,\n> = K extends readonly []\n ? (body: any, ...rest: any[]) => Promise<R>\n : (params: KeyShape<S, K>, body: any, ...rest: any[]) => Promise<R>;\n\n// Strip the first arg when keys is non-empty — model methods don't take the params.\ntype StripParams<K extends readonly any[], F> = K extends readonly []\n ? F\n : F extends (params: any, ...rest: infer R) => infer Ret\n ? (...args: R) => Ret\n : never;\n\n// Replace a function's Promise return with Promise<R>.\ntype ReplaceReturn<F, R> = F extends (...args: infer A) => Promise<any>\n ? (...args: A) => Promise<R>\n : never;\n\ntype ReservedActionKey = \"reload\" | \"update\" | \"delete\" | \"setData\" | \"toJSON\" | \"store\";\n\ntype ActionsConfig<S extends T.TObject, K extends readonly (keyof Resource<S>)[]> = {\n [name: string]: KeyedFn<S, K, Resource<S>>;\n} & { [Key in ReservedActionKey]?: never };\n\nexport interface ModelConfig<S extends T.TObject, K extends readonly (keyof Resource<S>)[]> {\n keys: K;\n reload?: KeyedFn<S, K, Resource<S>>;\n update?: KeyedBodyFn<S, K, Resource<S>>;\n delete?: KeyedFn<S, K, any>;\n actions?: ActionsConfig<S, K>;\n}\n\n// Instance method shape from config. Self-mutating methods return Promise<any>;\n// the instance is mutated in place via setData, so callers typically read fields\n// off the same reference instead of chaining the return.\ntype ModelMethods<K extends readonly any[], Cfg> = (Cfg extends { reload: infer F }\n ? { reload: ReplaceReturn<StripParams<K, F>, any> }\n : {}) &\n (Cfg extends { update: infer F } ? { update: ReplaceReturn<StripParams<K, F>, any> } : {}) &\n (Cfg extends { delete: infer F } ? { delete: StripParams<K, F> } : {}) &\n (Cfg extends { actions: infer A }\n ? {\n [N in keyof A]: A[N] extends (...args: any[]) => any\n ? ReplaceReturn<StripParams<K, A[N]>, any>\n : never;\n }\n : {});\n\n// -----------------------------------------------------------------------------\n// Constructor type\n// -----------------------------------------------------------------------------\n\ntype ModelInstance<S extends T.TObject, K extends readonly any[], Cfg> = Resource<S> & {\n readonly store?: ModelStore<any>;\n setData(data: Partial<Resource<S>>): void;\n toJSON(): Resource<S>;\n buildParams(): KeyShape<S, K extends readonly (keyof Resource<S>)[] ? K : readonly []>;\n getMobxAnnotations?(): AnnotationsMap<any, never>;\n} & ModelMethods<K, Cfg>;\n\nexport type ModelConstructor<S extends T.TObject, K extends readonly any[], Cfg> = {\n new (data: Resource<S>, store?: ModelStore<any>): ModelInstance<S, K, Cfg>;\n readonly schema: S;\n};\n\n// -----------------------------------------------------------------------------\n// makeModel\n// -----------------------------------------------------------------------------\n\nexport function makeModel<S extends T.TObject>(schema: S): ModelConstructor<S, readonly [], {}>;\nexport function makeModel<\n S extends T.TObject,\n K extends readonly (keyof Resource<S>)[],\n Cfg extends ModelConfig<S, K>,\n>(schema: S, config: Cfg): ModelConstructor<S, K, Cfg>;\nexport function makeModel<S extends T.TObject>(\n schema: S,\n config?: ModelConfig<S, readonly (keyof Resource<S>)[]>,\n): any {\n const keys = (config?.keys ?? []) as readonly PropertyKey[];\n\n abstract class BaseModel {\n static readonly schema = schema;\n\n readonly store?: ModelStore<any>;\n\n constructor(data: Resource<S>, store?: ModelStore<any>) {\n this.store = store;\n\n const annotations: Record<string, any> = {};\n for (const key of Object.keys(schema.properties)) {\n Object.defineProperty(this, key, {\n value: (data as any)[key],\n enumerable: true,\n configurable: true,\n writable: true,\n });\n annotations[key] = observable.ref;\n }\n\n makeObservable(this, {\n ...annotations,\n setData: action,\n ...(this as any).getMobxAnnotations?.(),\n });\n }\n\n setData(data: Partial<Resource<S>>): void {\n Object.assign(this, data);\n }\n\n /**\n * Build the params object passed as the first arg to keyed API methods.\n * Default extracts each property in `keys` from the model. Override on a\n * subclass when the model field name differs from the API param name, or\n * to construct composite params from derived values.\n */\n buildParams(): any {\n if (keys.length === 0) return undefined;\n const data = this as any;\n return Object.fromEntries(keys.map((k) => [k, data[k]]));\n }\n\n toJSON(): Resource<S> {\n const data = this as any;\n return Object.keys(schema.properties).reduce(\n (obj, key) => {\n if (data[key] !== undefined) obj[key] = toJS(data[key]);\n return obj;\n },\n {} as Record<string, any>,\n ) as Resource<S>;\n }\n }\n\n const proto = BaseModel.prototype as any;\n\n if (config?.reload) {\n const reload = config.reload as (...args: any[]) => Promise<any>;\n proto.reload = async function (...rest: any[]) {\n const params = this.buildParams();\n const data = params === undefined ? await reload(...rest) : await reload(params, ...rest);\n runInAction(() => this.setData(data));\n return this;\n };\n }\n\n if (config?.update) {\n const update = config.update as (...args: any[]) => Promise<any>;\n proto.update = async function (body: any, ...rest: any[]) {\n const params = this.buildParams();\n const data =\n params === undefined ? await update(body, ...rest) : await update(params, body, ...rest);\n runInAction(() => this.setData(data));\n return this;\n };\n }\n\n if (config?.delete) {\n const del = config.delete as (...args: any[]) => Promise<any>;\n proto.delete = async function (...rest: any[]) {\n const params = this.buildParams();\n const result = params === undefined ? await del(...rest) : await del(params, ...rest);\n this.store?.remove?.(this);\n return result;\n };\n }\n\n if (config?.actions) {\n for (const [name, fn] of Object.entries(config.actions)) {\n const call = fn as (...args: any[]) => Promise<any>;\n proto[name] = async function (body?: any, ...rest: any[]) {\n const params = this.buildParams();\n let data: any;\n if (params === undefined) {\n data = body === undefined ? await call() : await call(body, ...rest);\n } else {\n data = body === undefined ? await call(params) : await call(params, body, ...rest);\n }\n runInAction(() => this.setData(data));\n return this;\n };\n }\n }\n\n return BaseModel;\n}\n\nexport type { AnnotationsMap };\n","import * as T from \"typebox\";\nimport { lazyObservableArray, type LazyObservableArray } from \"../lazy-observable/lazy-observable\";\nimport { action, makeObservable, runInAction } from \"mobx\";\n\n// -----------------------------------------------------------------------------\n// Type plumbing\n// -----------------------------------------------------------------------------\n\n// Replace a function's Promise return with Promise<R> — used so get/create\n// return models rather than the raw API response.\ntype ReplaceReturn<F, R> = F extends (...args: infer A) => Promise<any>\n ? (...args: A) => Promise<R>\n : never;\n\n// The structural shape passed as `this` inside `transform` — the only member\n// transform ever needs is `remove`, since it's about to construct a model and\n// hand the store reference along to its constructor.\ntype StoreThis<M> = {\n remove(model: M): void;\n};\n\n// Infer M from config: when transform is present use its return type, else use R.\ntype InferModel<R, Cfg> = Cfg extends { transform: (...args: any[]) => infer M } ? M : R;\n\nexport interface StoreConfig<R> {\n transform?: (data: R, store: { remove(model: any): void }) => any;\n get?: (...args: any[]) => Promise<R>;\n getAll?: (...args: any[]) => Promise<R[]>;\n create?: (...args: any[]) => Promise<R>;\n}\n\n// Final store-instance shape, derived from config slots.\ntype StoreInstance<M, Cfg> = {\n remove(model: M): void;\n} & (Cfg extends { get: infer G } ? { get: ReplaceReturn<G, M> } : {}) &\n (Cfg extends { getAll: any } ? { getAll: () => Promise<M[]>; all: LazyObservableArray<M> } : {}) &\n (Cfg extends { create: infer C } ? { create: ReplaceReturn<C, M> } : {});\n\nexport type StoreConstructor<M, Cfg> = {\n new (): StoreInstance<M, Cfg>;\n};\n\n// -----------------------------------------------------------------------------\n// makeStore\n// -----------------------------------------------------------------------------\n\nexport function makeStore<S extends T.TObject>(schema: S): StoreConstructor<T.Static<S>, {}>;\nexport function makeStore<S extends T.TObject, Cfg extends StoreConfig<T.Static<S>>>(\n schema: S,\n config: Cfg & ThisType<StoreThis<InferModel<T.Static<S>, Cfg>>>,\n): StoreConstructor<InferModel<T.Static<S>, Cfg>, Cfg>;\nexport function makeStore<S extends T.TObject>(\n schema: S,\n config?: StoreConfig<T.Static<S>>,\n): StoreConstructor<any, any> {\n type R = T.Static<S>;\n\n class Store {\n private readonly _transform: (data: R) => any;\n\n all?: LazyObservableArray<any>;\n\n constructor() {\n const rawTransform = config?.transform;\n this._transform = rawTransform\n ? (data: R) => rawTransform.call(this, data, this)\n : (data: R) => data as any;\n\n makeObservable<this, \"_transform\">(this, {\n _transform: false,\n remove: action,\n });\n\n if (config?.getAll) {\n this.all = lazyObservableArray(async () => {\n const items = await (config.getAll as (...args: any[]) => Promise<R[]>)();\n return items.map((item) => this._transform(item));\n });\n }\n }\n\n remove(model: any): void {\n this.all?.value?.remove?.(model);\n }\n }\n\n const proto = Store.prototype as any;\n\n if (config?.get) {\n const get = config.get;\n proto.get = function (...args: any[]) {\n return get(...args).then((data: R) => this._transform(data));\n };\n }\n\n if (config?.getAll) {\n proto.getAll = function () {\n return this.all.getOrLoad();\n };\n }\n\n if (config?.create) {\n const create = config.create;\n proto.create = async function (...args: any[]) {\n const data = await create(...args);\n return runInAction(() => {\n const model = this._transform(data);\n if (this.all) this.all.value.unshift(model);\n return model;\n });\n };\n }\n\n return Store as unknown as StoreConstructor<any, any>;\n}\n\nexport type { LazyObservableArray };\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"model.mjs","names":[],"sources":["../src/model/make-model.ts","../src/model/make-store.ts"],"sourcesContent":["import { action, makeObservable, observable, runInAction, toJS, type AnnotationsMap } from \"mobx\";\nimport * as T from \"typebox\";\nimport * as Value from \"typebox/value\";\n\n// -----------------------------------------------------------------------------\n// Public structural contract\n// -----------------------------------------------------------------------------\n\n/** Minimal interface a model's host store must satisfy. */\nexport interface ModelStore<M> {\n remove?(model: M): void;\n}\n\n/**\n * Root schema for a model: a single object (`makeModel`) or a discriminated\n * union of objects (`makeUnionModel`). Shared by both factories and by\n * `makeStore`, which accepts either.\n */\nexport type ModelSchema = T.TObject | T.TUnion<T.TObject[]>;\n\n/**\n * Every property name across the schema. For a union this is the merged set of\n * all variants' keys, so all of them are made observable up front — that keeps\n * `setData` reactive even when it switches the active variant. `toJSON` runs\n * `Value.Clean` to emit only the keys of the variant the data currently matches.\n */\nfunction getPropertyNames(schema: ModelSchema): string[] {\n if (!T.IsUnion(schema)) return Object.keys(schema.properties);\n const names = new Set<string>();\n for (const variant of schema.anyOf) {\n for (const key of Object.keys((variant as T.TObject).properties)) names.add(key);\n }\n return [...names];\n}\n\n// -----------------------------------------------------------------------------\n// Type plumbing\n// -----------------------------------------------------------------------------\n\ntype Resource<S extends ModelSchema> = T.Static<S>;\n\ntype KeyShape<\n S extends ModelSchema,\n K extends readonly (keyof Resource<S>)[],\n> = K extends readonly [] ? undefined : Pick<Resource<S>, K[number]>;\n\ntype KeyedFn<\n S extends ModelSchema,\n K extends readonly (keyof Resource<S>)[],\n R,\n> = K extends readonly []\n ? (...args: any[]) => Promise<R>\n : (params: KeyShape<S, K>, ...rest: any[]) => Promise<R>;\n\ntype KeyedBodyFn<\n S extends ModelSchema,\n K extends readonly (keyof Resource<S>)[],\n R,\n> = K extends readonly []\n ? (body: any, ...rest: any[]) => Promise<R>\n : (params: KeyShape<S, K>, body: any, ...rest: any[]) => Promise<R>;\n\n// Strip the first arg when keys is non-empty — model methods don't take the params.\ntype StripParams<K extends readonly any[], F> = K extends readonly []\n ? F\n : F extends (params: any, ...rest: infer R) => infer Ret\n ? (...args: R) => Ret\n : never;\n\n// Replace a function's Promise return with Promise<R>.\ntype ReplaceReturn<F, R> = F extends (...args: infer A) => Promise<any>\n ? (...args: A) => Promise<R>\n : never;\n\ntype ReservedActionKey = \"reload\" | \"update\" | \"delete\" | \"setData\" | \"toJSON\" | \"store\";\n\ntype ActionsConfig<S extends ModelSchema, K extends readonly (keyof Resource<S>)[]> = {\n [name: string]: KeyedFn<S, K, Resource<S>>;\n} & { [Key in ReservedActionKey]?: never };\n\nexport interface ModelConfig<S extends ModelSchema, K extends readonly (keyof Resource<S>)[]> {\n keys: K;\n reload?: KeyedFn<S, K, Resource<S>>;\n update?: KeyedBodyFn<S, K, Resource<S>>;\n delete?: KeyedFn<S, K, any>;\n actions?: ActionsConfig<S, K>;\n}\n\n// Instance method shape from config. Self-mutating methods return Promise<any>;\n// the instance is mutated in place via setData, so callers typically read fields\n// off the same reference instead of chaining the return.\ntype ModelMethods<K extends readonly any[], Cfg> = (Cfg extends { reload: infer F }\n ? { reload: ReplaceReturn<StripParams<K, F>, any> }\n : {}) &\n (Cfg extends { update: infer F } ? { update: ReplaceReturn<StripParams<K, F>, any> } : {}) &\n (Cfg extends { delete: infer F } ? { delete: StripParams<K, F> } : {}) &\n (Cfg extends { actions: infer A }\n ? {\n [N in keyof A]: A[N] extends (...args: any[]) => any\n ? ReplaceReturn<StripParams<K, A[N]>, any>\n : never;\n }\n : {});\n\n// -----------------------------------------------------------------------------\n// Constructor type\n// -----------------------------------------------------------------------------\n\ntype ModelInstance<S extends ModelSchema, K extends readonly any[], Cfg> = Resource<S> & {\n readonly store?: ModelStore<any>;\n setData(data: Resource<S>): void;\n toJSON(): Resource<S>;\n buildParams(): KeyShape<S, K extends readonly (keyof Resource<S>)[] ? K : readonly []>;\n getMobxAnnotations?(): AnnotationsMap<any, never>;\n} & ModelMethods<K, Cfg>;\n\nexport type ModelConstructor<S extends ModelSchema, K extends readonly any[], Cfg> = {\n new (data: Resource<S>, store?: ModelStore<any>): ModelInstance<S, K, Cfg>;\n readonly schema: S;\n};\n\n// -----------------------------------------------------------------------------\n// Shared class builder\n// -----------------------------------------------------------------------------\n\n// Builds the observable model class used by both makeModel and makeUnionModel.\n// Handles object and union schemas at runtime; the public factories layer the\n// appropriate types (and, for unions, the `is`/`as` guards) on top.\nfunction createModelClass(schema: ModelSchema, config?: ModelConfig<any, any>): any {\n const keys = (config?.keys ?? []) as readonly PropertyKey[];\n const isUnion = T.IsUnion(schema);\n const propertyNames = getPropertyNames(schema);\n\n abstract class BaseModel {\n static readonly schema = schema;\n\n readonly store?: ModelStore<any>;\n\n constructor(data: any, store?: ModelStore<any>) {\n this.store = store;\n\n // Make every property of every variant observable up front, so `setData`\n // stays reactive even when it switches the active variant. Foreign-variant\n // fields sit as `undefined`; TypeScript hides them, and `toJSON` cleans them out.\n const annotations: Record<string, any> = {};\n for (const key of propertyNames) {\n Object.defineProperty(this, key, {\n value: (data as any)[key],\n enumerable: true,\n configurable: true,\n writable: true,\n });\n annotations[key] = observable.ref;\n }\n\n makeObservable(this, {\n ...annotations,\n setData: action,\n ...(this as any).getMobxAnnotations?.(),\n });\n }\n\n /**\n * Replace the model's data with a complete resource. Every property is\n * reassigned (fields absent from `data` — e.g. another variant's — become\n * `undefined`), so the model always holds a coherent, whole variant rather\n * than a partial merge that could mix fields across the union.\n */\n setData(data: any): void {\n for (const key of propertyNames) {\n (this as any)[key] = (data as any)[key];\n }\n }\n\n /**\n * Build the params object passed as the first arg to keyed API methods.\n * Default extracts each property in `keys` from the model. Override on a\n * subclass when the model field name differs from the API param name, or\n * to construct composite params from derived values.\n */\n buildParams(): any {\n if (keys.length === 0) return undefined;\n const data = this as any;\n return Object.fromEntries(keys.map((k) => [k, data[k]]));\n }\n\n toJSON(): any {\n const data = this as any;\n const snapshot = propertyNames.reduce(\n (obj, key) => {\n if (data[key] !== undefined) obj[key] = toJS(data[key]);\n return obj;\n },\n {} as Record<string, any>,\n );\n // For a union, strip any fields not belonging to the variant the current\n // data matches (e.g. a stale field left over from a previous variant).\n return isUnion ? Value.Clean(schema, snapshot) : snapshot;\n }\n }\n\n const proto = BaseModel.prototype as any;\n\n if (config?.reload) {\n const reload = config.reload as (...args: any[]) => Promise<any>;\n proto.reload = async function (...rest: any[]) {\n const params = this.buildParams();\n const data = params === undefined ? await reload(...rest) : await reload(params, ...rest);\n runInAction(() => this.setData(data));\n return this;\n };\n }\n\n if (config?.update) {\n const update = config.update as (...args: any[]) => Promise<any>;\n proto.update = async function (body: any, ...rest: any[]) {\n const params = this.buildParams();\n const data =\n params === undefined ? await update(body, ...rest) : await update(params, body, ...rest);\n runInAction(() => this.setData(data));\n return this;\n };\n }\n\n if (config?.delete) {\n const del = config.delete as (...args: any[]) => Promise<any>;\n proto.delete = async function (...rest: any[]) {\n const params = this.buildParams();\n const result = params === undefined ? await del(...rest) : await del(params, ...rest);\n this.store?.remove?.(this);\n return result;\n };\n }\n\n if (config?.actions) {\n for (const [name, fn] of Object.entries(config.actions)) {\n const call = fn as (...args: any[]) => Promise<any>;\n proto[name] = async function (body?: any, ...rest: any[]) {\n const params = this.buildParams();\n let data: any;\n if (params === undefined) {\n data = body === undefined ? await call() : await call(body, ...rest);\n } else {\n data = body === undefined ? await call(params) : await call(params, body, ...rest);\n }\n runInAction(() => this.setData(data));\n return this;\n };\n }\n }\n\n return BaseModel;\n}\n\n// -----------------------------------------------------------------------------\n// makeModel (single object schemas)\n// -----------------------------------------------------------------------------\n\nexport function makeModel<S extends T.TObject>(schema: S): ModelConstructor<S, readonly [], {}>;\nexport function makeModel<\n S extends T.TObject,\n K extends readonly (keyof Resource<S>)[],\n Cfg extends ModelConfig<S, K>,\n>(schema: S, config: Cfg): ModelConstructor<S, K, Cfg>;\nexport function makeModel<S extends T.TObject>(\n schema: S,\n config?: ModelConfig<S, readonly (keyof Resource<S>)[]>,\n): any {\n return createModelClass(schema, config);\n}\n\n// -----------------------------------------------------------------------------\n// makeUnionModel (discriminated union schemas)\n// -----------------------------------------------------------------------------\n\ntype UnionSchema = T.TUnion<T.TObject[]>;\n\n// Properties common to every variant (`keyof` a union resolves to shared keys).\ntype SharedFields<S extends UnionSchema> = { [K in keyof Resource<S>]: Resource<S>[K] };\n\n// The full static shape of the variant whose discriminator `D` equals `V`.\ntype VariantFields<S extends UnionSchema, D extends keyof Resource<S>, V> = Extract<\n Resource<S>,\n Record<D, V>\n>;\n\n// The members makeUnionModel adds. An interface (not a type-alias literal) so the\n// polymorphic `this` in `is`/`as` is allowed; at a call site `this` resolves to\n// the full instance, so the guard reveals the variant's fields on it.\ninterface UnionModelMembers<\n S extends UnionSchema,\n D extends keyof Resource<S>,\n K extends readonly any[],\n> {\n readonly store?: ModelStore<any>;\n setData(data: Resource<S>): void;\n toJSON(): Resource<S>;\n buildParams(): KeyShape<S, K extends readonly (keyof Resource<S>)[] ? K : readonly []>;\n getMobxAnnotations?(): AnnotationsMap<any, never>;\n /** Type guard: true when the discriminator equals `value`, revealing that variant's fields on this same instance. */\n is<V extends Resource<S>[D]>(value: V): this is this & VariantFields<S, D, V>;\n /** This instance narrowed to the `value` variant (fields exposed directly), or `undefined` if it doesn't match. */\n as<V extends Resource<S>[D]>(value: V): (this & VariantFields<S, D, V>) | undefined;\n}\n\n// Base instance exposes only the shared fields (a single object type, so it can\n// be subclassed). Variant-specific fields exist at runtime but are revealed on\n// the type only through `is`/`as`.\ntype UnionModelInstance<\n S extends UnionSchema,\n D extends keyof Resource<S>,\n K extends readonly any[],\n Cfg,\n> = SharedFields<S> & UnionModelMembers<S, D, K> & ModelMethods<K, Cfg>;\n\nexport type UnionModelConstructor<\n S extends UnionSchema,\n D extends keyof Resource<S>,\n K extends readonly any[],\n Cfg,\n> = {\n new (data: Resource<S>, store?: ModelStore<any>): UnionModelInstance<S, D, K, Cfg>;\n readonly schema: S;\n readonly discriminator: D;\n};\n\nexport function makeUnionModel<S extends UnionSchema, D extends keyof Resource<S> & string>(\n schema: S,\n discriminator: D,\n): UnionModelConstructor<S, D, readonly [], {}>;\nexport function makeUnionModel<\n S extends UnionSchema,\n D extends keyof Resource<S> & string,\n K extends readonly (keyof Resource<S>)[],\n Cfg extends ModelConfig<S, K>,\n>(schema: S, discriminator: D, config: Cfg): UnionModelConstructor<S, D, K, Cfg>;\nexport function makeUnionModel(\n schema: UnionSchema,\n discriminator: string,\n config?: ModelConfig<any, any>,\n): any {\n const ModelClass = createModelClass(schema, config);\n ModelClass.discriminator = discriminator;\n\n const proto = ModelClass.prototype as any;\n proto.is = function (value: unknown): boolean {\n return (this as any)[discriminator] === value;\n };\n proto.as = function (value: unknown): unknown {\n return (this as any)[discriminator] === value ? this : undefined;\n };\n\n return ModelClass;\n}\n\nexport type { AnnotationsMap };\n","import * as T from \"typebox\";\nimport { lazyObservableArray, type LazyObservableArray } from \"../lazy-observable/lazy-observable\";\nimport { action, makeObservable, runInAction } from \"mobx\";\nimport type { ModelSchema } from \"./make-model\";\n\n// -----------------------------------------------------------------------------\n// Type plumbing\n// -----------------------------------------------------------------------------\n\n// Replace a function's Promise return with Promise<R> — used so get/create\n// return models rather than the raw API response.\ntype ReplaceReturn<F, R> = F extends (...args: infer A) => Promise<any>\n ? (...args: A) => Promise<R>\n : never;\n\n// The structural shape passed as `this` inside `transform` — the only member\n// transform ever needs is `remove`, since it's about to construct a model and\n// hand the store reference along to its constructor.\ntype StoreThis<M> = {\n remove(model: M): void;\n};\n\n// Infer M from config: when transform is present use its return type, else use R.\ntype InferModel<R, Cfg> = Cfg extends { transform: (...args: any[]) => infer M } ? M : R;\n\nexport interface StoreConfig<R> {\n transform?: (data: R, store: { remove(model: any): void }) => any;\n get?: (...args: any[]) => Promise<R>;\n getAll?: (...args: any[]) => Promise<R[]>;\n create?: (...args: any[]) => Promise<R>;\n}\n\n// Final store-instance shape, derived from config slots.\ntype StoreInstance<M, Cfg> = {\n remove(model: M): void;\n} & (Cfg extends { get: infer G } ? { get: ReplaceReturn<G, M> } : {}) &\n (Cfg extends { getAll: any } ? { getAll: () => Promise<M[]>; all: LazyObservableArray<M> } : {}) &\n (Cfg extends { create: infer C } ? { create: ReplaceReturn<C, M> } : {});\n\nexport type StoreConstructor<M, Cfg> = {\n new (): StoreInstance<M, Cfg>;\n};\n\n// -----------------------------------------------------------------------------\n// makeStore\n// -----------------------------------------------------------------------------\n\nexport function makeStore<S extends ModelSchema>(schema: S): StoreConstructor<T.Static<S>, {}>;\nexport function makeStore<S extends ModelSchema, Cfg extends StoreConfig<T.Static<S>>>(\n schema: S,\n config: Cfg & ThisType<StoreThis<InferModel<T.Static<S>, Cfg>>>,\n): StoreConstructor<InferModel<T.Static<S>, Cfg>, Cfg>;\nexport function makeStore<S extends ModelSchema>(\n schema: S,\n config?: StoreConfig<T.Static<S>>,\n): StoreConstructor<any, any> {\n type R = T.Static<S>;\n\n class Store {\n private readonly _transform: (data: R) => any;\n\n all?: LazyObservableArray<any>;\n\n constructor() {\n const rawTransform = config?.transform;\n this._transform = rawTransform\n ? (data: R) => rawTransform.call(this, data, this)\n : (data: R) => data as any;\n\n makeObservable<this, \"_transform\">(this, {\n _transform: false,\n remove: action,\n });\n\n if (config?.getAll) {\n this.all = lazyObservableArray(async () => {\n const items = await (config.getAll as (...args: any[]) => Promise<R[]>)();\n return items.map((item) => this._transform(item));\n });\n }\n }\n\n remove(model: any): void {\n this.all?.value?.remove?.(model);\n }\n }\n\n const proto = Store.prototype as any;\n\n if (config?.get) {\n const get = config.get;\n proto.get = function (...args: any[]) {\n return get(...args).then((data: R) => this._transform(data));\n };\n }\n\n if (config?.getAll) {\n proto.getAll = function () {\n return this.all.getOrLoad();\n };\n }\n\n if (config?.create) {\n const create = config.create;\n proto.create = async function (...args: any[]) {\n const data = await create(...args);\n return runInAction(() => {\n const model = this._transform(data);\n if (this.all) this.all.value.unshift(model);\n return model;\n });\n };\n }\n\n return Store as unknown as StoreConstructor<any, any>;\n}\n\nexport type { LazyObservableArray };\n"],"mappings":"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"}
|