@logixjs/domain 0.0.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/Crud.cjs +301 -0
- package/dist/Crud.cjs.map +1 -0
- package/dist/Crud.d.cts +80 -0
- package/dist/Crud.d.ts +80 -0
- package/dist/Crud.js +7 -0
- package/dist/Crud.js.map +1 -0
- package/dist/chunk-SCBUT2ZQ.js +265 -0
- package/dist/chunk-SCBUT2ZQ.js.map +1 -0
- package/dist/index.cjs +301 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +3 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -0
- package/package.json +55 -0
package/dist/Crud.cjs
ADDED
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/Crud.ts
|
|
31
|
+
var Crud_exports = {};
|
|
32
|
+
__export(Crud_exports, {
|
|
33
|
+
CRUDModule: () => CRUDModule
|
|
34
|
+
});
|
|
35
|
+
module.exports = __toCommonJS(Crud_exports);
|
|
36
|
+
|
|
37
|
+
// src/internal/crud/Crud.ts
|
|
38
|
+
var Logix = __toESM(require("@logixjs/core"), 1);
|
|
39
|
+
var import_effect = require("effect");
|
|
40
|
+
var DefaultQueryInputSchema = import_effect.Schema.Struct({
|
|
41
|
+
pageSize: import_effect.Schema.Number
|
|
42
|
+
});
|
|
43
|
+
var makeActions = (entity, query, id) => ({
|
|
44
|
+
query,
|
|
45
|
+
querySucceeded: import_effect.Schema.Struct({
|
|
46
|
+
items: import_effect.Schema.Array(entity),
|
|
47
|
+
total: import_effect.Schema.optional(import_effect.Schema.Number)
|
|
48
|
+
}),
|
|
49
|
+
queryFailed: import_effect.Schema.String,
|
|
50
|
+
save: entity,
|
|
51
|
+
saveSucceeded: entity,
|
|
52
|
+
saveFailed: import_effect.Schema.String,
|
|
53
|
+
remove: id,
|
|
54
|
+
removeSucceeded: id,
|
|
55
|
+
removeFailed: import_effect.Schema.String,
|
|
56
|
+
clearError: import_effect.Schema.Void
|
|
57
|
+
});
|
|
58
|
+
var toErrorMessage = (error) => {
|
|
59
|
+
if (error === null || error === void 0) return "unknown error";
|
|
60
|
+
if (typeof error === "string") return error;
|
|
61
|
+
if (error instanceof Error && typeof error.message === "string" && error.message.length > 0) {
|
|
62
|
+
return error.message;
|
|
63
|
+
}
|
|
64
|
+
if (typeof error === "object") {
|
|
65
|
+
if ("message" in error) {
|
|
66
|
+
const message = error.message;
|
|
67
|
+
if (typeof message === "string" && message.length > 0) return message;
|
|
68
|
+
}
|
|
69
|
+
try {
|
|
70
|
+
const json = JSON.stringify(error);
|
|
71
|
+
if (typeof json === "string" && json.length > 0) return json;
|
|
72
|
+
} catch {
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return String(error);
|
|
76
|
+
};
|
|
77
|
+
var upsertByIdField = (items, entity, idField) => {
|
|
78
|
+
const id = entity[idField];
|
|
79
|
+
const idx = items.findIndex((x) => x[idField] === id);
|
|
80
|
+
if (idx < 0) return [...items, entity];
|
|
81
|
+
return items.map((x, i) => i === idx ? entity : x);
|
|
82
|
+
};
|
|
83
|
+
var removeByIdField = (items, id, idField) => items.filter((x) => x[idField] !== id);
|
|
84
|
+
var defineCrud = (id, spec, extend) => {
|
|
85
|
+
const QuerySchema = spec.query ?? DefaultQueryInputSchema;
|
|
86
|
+
const IdSchema = spec.id ?? import_effect.Schema.String;
|
|
87
|
+
const idField = spec.idField ?? "id";
|
|
88
|
+
class Api extends import_effect.Context.Tag(`${id}/crud/api`)() {
|
|
89
|
+
}
|
|
90
|
+
const services = { api: Api };
|
|
91
|
+
const Actions = makeActions(spec.entity, QuerySchema, IdSchema);
|
|
92
|
+
const StateSchema = import_effect.Schema.Struct({
|
|
93
|
+
items: import_effect.Schema.Array(spec.entity),
|
|
94
|
+
loading: import_effect.Schema.Boolean,
|
|
95
|
+
error: import_effect.Schema.UndefinedOr(import_effect.Schema.String),
|
|
96
|
+
lastQuery: import_effect.Schema.UndefinedOr(QuerySchema),
|
|
97
|
+
total: import_effect.Schema.UndefinedOr(import_effect.Schema.Number)
|
|
98
|
+
});
|
|
99
|
+
const reducers = {
|
|
100
|
+
query: (state, action, sink) => {
|
|
101
|
+
sink?.("loading");
|
|
102
|
+
sink?.("error");
|
|
103
|
+
sink?.("lastQuery");
|
|
104
|
+
return {
|
|
105
|
+
...state,
|
|
106
|
+
loading: true,
|
|
107
|
+
error: void 0,
|
|
108
|
+
lastQuery: action.payload
|
|
109
|
+
};
|
|
110
|
+
},
|
|
111
|
+
querySucceeded: (state, action, sink) => {
|
|
112
|
+
sink?.("loading");
|
|
113
|
+
sink?.("error");
|
|
114
|
+
sink?.("items");
|
|
115
|
+
sink?.("total");
|
|
116
|
+
return {
|
|
117
|
+
...state,
|
|
118
|
+
loading: false,
|
|
119
|
+
error: void 0,
|
|
120
|
+
items: action.payload.items,
|
|
121
|
+
total: action.payload.total
|
|
122
|
+
};
|
|
123
|
+
},
|
|
124
|
+
queryFailed: (state, action, sink) => {
|
|
125
|
+
sink?.("loading");
|
|
126
|
+
sink?.("error");
|
|
127
|
+
return {
|
|
128
|
+
...state,
|
|
129
|
+
loading: false,
|
|
130
|
+
error: action.payload
|
|
131
|
+
};
|
|
132
|
+
},
|
|
133
|
+
save: (state, _action, sink) => {
|
|
134
|
+
sink?.("loading");
|
|
135
|
+
sink?.("error");
|
|
136
|
+
return {
|
|
137
|
+
...state,
|
|
138
|
+
loading: true,
|
|
139
|
+
error: void 0
|
|
140
|
+
};
|
|
141
|
+
},
|
|
142
|
+
saveSucceeded: (state, action, sink) => {
|
|
143
|
+
sink?.("loading");
|
|
144
|
+
sink?.("error");
|
|
145
|
+
sink?.("items");
|
|
146
|
+
return {
|
|
147
|
+
...state,
|
|
148
|
+
loading: false,
|
|
149
|
+
error: void 0,
|
|
150
|
+
items: upsertByIdField(state.items, action.payload, idField)
|
|
151
|
+
};
|
|
152
|
+
},
|
|
153
|
+
saveFailed: (state, action, sink) => {
|
|
154
|
+
sink?.("loading");
|
|
155
|
+
sink?.("error");
|
|
156
|
+
return {
|
|
157
|
+
...state,
|
|
158
|
+
loading: false,
|
|
159
|
+
error: action.payload
|
|
160
|
+
};
|
|
161
|
+
},
|
|
162
|
+
remove: (state, _action, sink) => {
|
|
163
|
+
sink?.("loading");
|
|
164
|
+
sink?.("error");
|
|
165
|
+
return {
|
|
166
|
+
...state,
|
|
167
|
+
loading: true,
|
|
168
|
+
error: void 0
|
|
169
|
+
};
|
|
170
|
+
},
|
|
171
|
+
removeSucceeded: (state, action, sink) => {
|
|
172
|
+
sink?.("loading");
|
|
173
|
+
sink?.("error");
|
|
174
|
+
sink?.("items");
|
|
175
|
+
return {
|
|
176
|
+
...state,
|
|
177
|
+
loading: false,
|
|
178
|
+
error: void 0,
|
|
179
|
+
items: removeByIdField(state.items, action.payload, idField)
|
|
180
|
+
};
|
|
181
|
+
},
|
|
182
|
+
removeFailed: (state, action, sink) => {
|
|
183
|
+
sink?.("loading");
|
|
184
|
+
sink?.("error");
|
|
185
|
+
return {
|
|
186
|
+
...state,
|
|
187
|
+
loading: false,
|
|
188
|
+
error: action.payload
|
|
189
|
+
};
|
|
190
|
+
},
|
|
191
|
+
clearError: (state, _action, sink) => {
|
|
192
|
+
sink?.("error");
|
|
193
|
+
return {
|
|
194
|
+
...state,
|
|
195
|
+
error: void 0
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
};
|
|
199
|
+
const def = {
|
|
200
|
+
state: StateSchema,
|
|
201
|
+
actions: Actions,
|
|
202
|
+
reducers,
|
|
203
|
+
schemas: { entity: spec.entity },
|
|
204
|
+
meta: { kind: "crud", idField },
|
|
205
|
+
services
|
|
206
|
+
};
|
|
207
|
+
const module2 = extend ? Logix.Module.make(
|
|
208
|
+
id,
|
|
209
|
+
def,
|
|
210
|
+
extend
|
|
211
|
+
) : Logix.Module.make(id, def);
|
|
212
|
+
const install = module2.logic(
|
|
213
|
+
($) => import_effect.Effect.gen(function* () {
|
|
214
|
+
yield* $.onAction("query").runFork(
|
|
215
|
+
(action) => import_effect.Effect.gen(function* () {
|
|
216
|
+
const apiOpt = yield* import_effect.Effect.serviceOption(services.api);
|
|
217
|
+
if (import_effect.Option.isNone(apiOpt)) {
|
|
218
|
+
yield* $.dispatchers.queryFailed(
|
|
219
|
+
`[CRUDModule] Missing services.api; provide Layer.succeed(${id}.services.api, impl) via withLayer/withLayers/Runtime layer.`
|
|
220
|
+
);
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
const api = apiOpt.value;
|
|
224
|
+
const result = yield* api.list(action.payload);
|
|
225
|
+
yield* $.dispatchers.querySucceeded(result);
|
|
226
|
+
}).pipe(import_effect.Effect.catchAll((e) => $.dispatchers.queryFailed(toErrorMessage(e))))
|
|
227
|
+
);
|
|
228
|
+
yield* $.onAction("save").runFork(
|
|
229
|
+
(action) => import_effect.Effect.gen(function* () {
|
|
230
|
+
const apiOpt = yield* import_effect.Effect.serviceOption(services.api);
|
|
231
|
+
if (import_effect.Option.isNone(apiOpt)) {
|
|
232
|
+
yield* $.dispatchers.saveFailed(
|
|
233
|
+
`[CRUDModule] Missing services.api; provide Layer.succeed(${id}.services.api, impl) via withLayer/withLayers/Runtime layer.`
|
|
234
|
+
);
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
const api = apiOpt.value;
|
|
238
|
+
const entity = yield* api.save(action.payload);
|
|
239
|
+
yield* $.dispatchers.saveSucceeded(entity);
|
|
240
|
+
}).pipe(import_effect.Effect.catchAll((e) => $.dispatchers.saveFailed(toErrorMessage(e))))
|
|
241
|
+
);
|
|
242
|
+
yield* $.onAction("remove").runFork(
|
|
243
|
+
(action) => import_effect.Effect.gen(function* () {
|
|
244
|
+
const apiOpt = yield* import_effect.Effect.serviceOption(services.api);
|
|
245
|
+
if (import_effect.Option.isNone(apiOpt)) {
|
|
246
|
+
yield* $.dispatchers.removeFailed(
|
|
247
|
+
`[CRUDModule] Missing services.api; provide Layer.succeed(${id}.services.api, impl) via withLayer/withLayers/Runtime layer.`
|
|
248
|
+
);
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
const api = apiOpt.value;
|
|
252
|
+
yield* api.remove(action.payload);
|
|
253
|
+
yield* $.dispatchers.removeSucceeded(action.payload);
|
|
254
|
+
}).pipe(import_effect.Effect.catchAll((e) => $.dispatchers.removeFailed(toErrorMessage(e))))
|
|
255
|
+
);
|
|
256
|
+
}),
|
|
257
|
+
{ id: "install" }
|
|
258
|
+
);
|
|
259
|
+
const controller = {
|
|
260
|
+
make: (runtime) => ({
|
|
261
|
+
runtime,
|
|
262
|
+
getState: runtime.getState,
|
|
263
|
+
dispatch: runtime.dispatch,
|
|
264
|
+
controller: {
|
|
265
|
+
list: (input) => runtime.dispatch({ _tag: "query", payload: input }),
|
|
266
|
+
save: (entity) => runtime.dispatch({ _tag: "save", payload: entity }),
|
|
267
|
+
remove: (id2) => runtime.dispatch({ _tag: "remove", payload: id2 }),
|
|
268
|
+
clearError: () => runtime.dispatch({ _tag: "clearError" }),
|
|
269
|
+
idField
|
|
270
|
+
}
|
|
271
|
+
})
|
|
272
|
+
};
|
|
273
|
+
const EXTEND_HANDLE = /* @__PURE__ */ Symbol.for("logix.module.handle.extend");
|
|
274
|
+
module2.tag[EXTEND_HANDLE] = (runtime, base) => {
|
|
275
|
+
const crud = controller.make(runtime);
|
|
276
|
+
return {
|
|
277
|
+
...base,
|
|
278
|
+
controller: crud.controller,
|
|
279
|
+
services
|
|
280
|
+
};
|
|
281
|
+
};
|
|
282
|
+
return module2.implement({
|
|
283
|
+
initial: {
|
|
284
|
+
items: Array.from(spec.initial ?? []),
|
|
285
|
+
loading: false,
|
|
286
|
+
error: void 0,
|
|
287
|
+
lastQuery: void 0,
|
|
288
|
+
total: void 0
|
|
289
|
+
},
|
|
290
|
+
logics: [install]
|
|
291
|
+
});
|
|
292
|
+
};
|
|
293
|
+
var CRUDModule = Logix.Module.Manage.make({
|
|
294
|
+
kind: "crud",
|
|
295
|
+
define: defineCrud
|
|
296
|
+
});
|
|
297
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
298
|
+
0 && (module.exports = {
|
|
299
|
+
CRUDModule
|
|
300
|
+
});
|
|
301
|
+
//# sourceMappingURL=Crud.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/Crud.ts","../src/internal/crud/Crud.ts"],"sourcesContent":["export * from './internal/crud/Crud.js'\nexport type * from './internal/crud/Crud.js'\n","import * as Logix from '@logixjs/core'\nimport { Context, Effect, Option, Schema } from 'effect'\n\nconst DefaultQueryInputSchema = Schema.Struct({\n pageSize: Schema.Number,\n})\n\nexport type CrudDefaultQueryInput = Schema.Schema.Type<typeof DefaultQueryInputSchema>\n\nexport interface CrudQueryResult<Entity> {\n readonly items: ReadonlyArray<Entity>\n readonly total?: number\n}\n\nexport interface CrudApi<Entity extends object, QueryInput, Id> {\n readonly list: (input: QueryInput) => Effect.Effect<CrudQueryResult<Entity>, unknown, never>\n readonly save: (entity: Entity) => Effect.Effect<Entity, unknown, never>\n readonly remove: (id: Id) => Effect.Effect<void, unknown, never>\n}\n\nexport interface CrudSpec<Entity extends object, QueryInput = CrudDefaultQueryInput, Id = string> {\n readonly entity: Schema.Schema<Entity>\n readonly query?: Schema.Schema<QueryInput>\n readonly id?: Schema.Schema<Id>\n readonly initial?: ReadonlyArray<Entity>\n /**\n * idField:\n * - The default primary key field for upsert/remove in reducers (default: \"id\").\n * - For more complex primary-key strategies, prefer handling results in a custom upper-layer Logic and writing back to state.\n */\n readonly idField?: string\n}\n\nexport type CrudState<Entity, QueryInput> = {\n readonly items: ReadonlyArray<Entity>\n readonly loading: boolean\n readonly error: string | undefined\n readonly lastQuery: QueryInput | undefined\n readonly total: number | undefined\n}\n\nexport type CrudActionMap<\n Entity extends object,\n QueryInput,\n Id,\n ExtraActions extends Record<string, Logix.AnySchema> = {},\n> = {\n readonly query: Schema.Schema<QueryInput>\n readonly querySucceeded: Schema.Schema<CrudQueryResult<Entity>>\n readonly queryFailed: Schema.Schema<string>\n\n readonly save: Schema.Schema<Entity>\n readonly saveSucceeded: Schema.Schema<Entity>\n readonly saveFailed: Schema.Schema<string>\n\n readonly remove: Schema.Schema<Id>\n readonly removeSucceeded: Schema.Schema<Id>\n readonly removeFailed: Schema.Schema<string>\n\n readonly clearError: Schema.Schema<void>\n} & ExtraActions\n\nexport type CrudAction<\n Entity extends object,\n QueryInput,\n Id,\n ExtraActions extends Record<string, Logix.AnySchema> = {},\n> = Logix.ActionsFromMap<CrudActionMap<Entity, QueryInput, Id, ExtraActions>>\n\nexport type CrudShape<\n Entity extends object,\n QueryInput,\n Id,\n ExtraActions extends Record<string, Logix.AnySchema> = {},\n> = Logix.Shape<Schema.Schema<CrudState<Entity, QueryInput>>, CrudActionMap<Entity, QueryInput, Id, ExtraActions>>\n\nexport type CrudServices<Entity extends object, QueryInput, Id> = {\n readonly api: Logix.State.Tag<CrudApi<Entity, QueryInput, Id>>\n}\n\nexport type CrudHandleExt<\n Entity extends object,\n QueryInput,\n Id,\n ExtraActions extends Record<string, Logix.AnySchema> = {},\n> = {\n readonly controller: CrudController<Entity, QueryInput, Id, ExtraActions>['controller']\n readonly services: CrudServices<Entity, QueryInput, Id>\n}\n\nexport interface CrudController<\n Entity extends object,\n QueryInput,\n Id,\n ExtraActions extends Record<string, Logix.AnySchema> = {},\n> {\n readonly runtime: Logix.ModuleRuntime<CrudState<Entity, QueryInput>, CrudAction<Entity, QueryInput, Id, ExtraActions>>\n readonly getState: Effect.Effect<CrudState<Entity, QueryInput>>\n readonly dispatch: (action: CrudAction<Entity, QueryInput, Id, ExtraActions>) => Effect.Effect<void>\n readonly controller: {\n readonly list: (input: QueryInput) => Effect.Effect<void>\n readonly save: (entity: Entity) => Effect.Effect<void>\n readonly remove: (id: Id) => Effect.Effect<void>\n readonly clearError: () => Effect.Effect<void>\n readonly idField: string\n }\n}\n\nconst makeActions = <Entity extends object, QueryInput, Id>(\n entity: Schema.Schema<Entity>,\n query: Schema.Schema<QueryInput>,\n id: Schema.Schema<Id>,\n): CrudActionMap<Entity, QueryInput, Id> =>\n ({\n query,\n querySucceeded: Schema.Struct({\n items: Schema.Array(entity),\n total: Schema.optional(Schema.Number),\n }) as Schema.Schema<CrudQueryResult<Entity>>,\n queryFailed: Schema.String,\n\n save: entity,\n saveSucceeded: entity,\n saveFailed: Schema.String,\n\n remove: id,\n removeSucceeded: id,\n removeFailed: Schema.String,\n\n clearError: Schema.Void,\n }) satisfies CrudActionMap<Entity, QueryInput, Id>\n\nconst toErrorMessage = (error: unknown): string => {\n if (error === null || error === undefined) return 'unknown error'\n if (typeof error === 'string') return error\n if (error instanceof Error && typeof error.message === 'string' && error.message.length > 0) {\n return error.message\n }\n if (typeof error === 'object') {\n if ('message' in error) {\n const message = (error as { readonly message?: unknown }).message\n if (typeof message === 'string' && message.length > 0) return message\n }\n try {\n const json = JSON.stringify(error)\n if (typeof json === 'string' && json.length > 0) return json\n } catch {\n // ignore\n }\n }\n return String(error)\n}\n\nconst upsertByIdField = <Entity extends object>(\n items: ReadonlyArray<Entity>,\n entity: Entity,\n idField: string,\n): ReadonlyArray<Entity> => {\n const id = (entity as Record<string, unknown>)[idField]\n const idx = items.findIndex((x) => (x as Record<string, unknown>)[idField] === id)\n if (idx < 0) return [...items, entity]\n return items.map((x, i) => (i === idx ? entity : x))\n}\n\nconst removeByIdField = <Entity extends object>(\n items: ReadonlyArray<Entity>,\n id: unknown,\n idField: string,\n): ReadonlyArray<Entity> => items.filter((x) => (x as Record<string, unknown>)[idField] !== id)\n\nexport type CrudModule<\n Id extends string,\n Entity extends object,\n QueryInput,\n EntityId,\n ExtraActions extends Record<string, Logix.AnySchema> = {},\n> = Logix.Module.Module<\n Id,\n CrudShape<Entity, QueryInput, EntityId, ExtraActions>,\n CrudHandleExt<Entity, QueryInput, EntityId, ExtraActions>,\n unknown\n> & {\n readonly _kind: 'Module'\n readonly services: CrudServices<Entity, QueryInput, EntityId>\n}\n\nconst defineCrud = <\n Id extends string,\n Entity extends object,\n QueryInput = CrudDefaultQueryInput,\n EntityId = string,\n ExtraActions extends Record<string, Logix.AnySchema> = {},\n>(\n id: Id,\n spec: CrudSpec<Entity, QueryInput, EntityId>,\n extend?: Logix.Module.MakeExtendDef<\n Schema.Schema<CrudState<Entity, QueryInput>>,\n CrudActionMap<Entity, QueryInput, EntityId>,\n ExtraActions\n >,\n): CrudModule<Id, Entity, QueryInput, EntityId, ExtraActions> => {\n const QuerySchema = (spec.query ?? DefaultQueryInputSchema) as Schema.Schema<QueryInput>\n const IdSchema = (spec.id ?? Schema.String) as Schema.Schema<EntityId>\n\n const idField = spec.idField ?? 'id'\n\n class Api extends Context.Tag(`${id}/crud/api`)<Api, CrudApi<Entity, QueryInput, EntityId>>() {}\n\n const services = { api: Api } as const satisfies CrudServices<Entity, QueryInput, EntityId>\n\n const Actions = makeActions(spec.entity, QuerySchema, IdSchema)\n\n const StateSchema = Schema.Struct({\n items: Schema.Array(spec.entity),\n loading: Schema.Boolean,\n error: Schema.UndefinedOr(Schema.String),\n lastQuery: Schema.UndefinedOr(QuerySchema),\n total: Schema.UndefinedOr(Schema.Number),\n })\n\n type Reducers = Logix.ReducersFromMap<typeof StateSchema, CrudActionMap<Entity, QueryInput, EntityId>>\n\n const reducers: Reducers = {\n query: (state, action, sink) => {\n sink?.('loading')\n sink?.('error')\n sink?.('lastQuery')\n return {\n ...state,\n loading: true,\n error: undefined,\n lastQuery: action.payload,\n }\n },\n querySucceeded: (state, action, sink) => {\n sink?.('loading')\n sink?.('error')\n sink?.('items')\n sink?.('total')\n return {\n ...state,\n loading: false,\n error: undefined,\n items: action.payload.items,\n total: action.payload.total,\n }\n },\n queryFailed: (state, action, sink) => {\n sink?.('loading')\n sink?.('error')\n return {\n ...state,\n loading: false,\n error: action.payload,\n }\n },\n\n save: (state, _action, sink) => {\n sink?.('loading')\n sink?.('error')\n return {\n ...state,\n loading: true,\n error: undefined,\n }\n },\n saveSucceeded: (state, action, sink) => {\n sink?.('loading')\n sink?.('error')\n sink?.('items')\n return {\n ...state,\n loading: false,\n error: undefined,\n items: upsertByIdField(state.items, action.payload as Entity, idField),\n }\n },\n saveFailed: (state, action, sink) => {\n sink?.('loading')\n sink?.('error')\n return {\n ...state,\n loading: false,\n error: action.payload,\n }\n },\n\n remove: (state, _action, sink) => {\n sink?.('loading')\n sink?.('error')\n return {\n ...state,\n loading: true,\n error: undefined,\n }\n },\n removeSucceeded: (state, action, sink) => {\n sink?.('loading')\n sink?.('error')\n sink?.('items')\n return {\n ...state,\n loading: false,\n error: undefined,\n items: removeByIdField(state.items, action.payload, idField),\n }\n },\n removeFailed: (state, action, sink) => {\n sink?.('loading')\n sink?.('error')\n return {\n ...state,\n loading: false,\n error: action.payload,\n }\n },\n\n clearError: (state, _action, sink) => {\n sink?.('error')\n return {\n ...state,\n error: undefined,\n }\n },\n }\n\n const def = {\n state: StateSchema,\n actions: Actions,\n reducers,\n schemas: { entity: spec.entity },\n meta: { kind: 'crud', idField },\n services,\n }\n\n const module = extend\n ? Logix.Module.make<\n Id,\n typeof StateSchema,\n CrudActionMap<Entity, QueryInput, EntityId>,\n CrudHandleExt<Entity, QueryInput, EntityId, ExtraActions>\n >(\n id,\n def,\n extend as unknown as Logix.Module.MakeExtendDef<\n typeof StateSchema,\n CrudActionMap<Entity, QueryInput, EntityId>,\n ExtraActions\n >,\n )\n : Logix.Module.make<\n Id,\n typeof StateSchema,\n CrudActionMap<Entity, QueryInput, EntityId>,\n CrudHandleExt<Entity, QueryInput, EntityId, ExtraActions>\n >(id, def)\n\n const install = module.logic(\n ($) =>\n Effect.gen(function* () {\n yield* $.onAction('query').runFork((action) =>\n Effect.gen(function* () {\n const apiOpt = yield* Effect.serviceOption(services.api)\n if (Option.isNone(apiOpt)) {\n yield* $.dispatchers.queryFailed(\n `[CRUDModule] Missing services.api; provide Layer.succeed(${id}.services.api, impl) via withLayer/withLayers/Runtime layer.`,\n )\n return\n }\n const api = apiOpt.value\n\n const result = yield* api.list(action.payload as QueryInput)\n yield* $.dispatchers.querySucceeded(result)\n }).pipe(Effect.catchAll((e) => $.dispatchers.queryFailed(toErrorMessage(e)))),\n )\n\n yield* $.onAction('save').runFork((action) =>\n Effect.gen(function* () {\n const apiOpt = yield* Effect.serviceOption(services.api)\n if (Option.isNone(apiOpt)) {\n yield* $.dispatchers.saveFailed(\n `[CRUDModule] Missing services.api; provide Layer.succeed(${id}.services.api, impl) via withLayer/withLayers/Runtime layer.`,\n )\n return\n }\n const api = apiOpt.value\n\n const entity = yield* api.save(action.payload as Entity)\n yield* $.dispatchers.saveSucceeded(entity)\n }).pipe(Effect.catchAll((e) => $.dispatchers.saveFailed(toErrorMessage(e)))),\n )\n\n yield* $.onAction('remove').runFork((action) =>\n Effect.gen(function* () {\n const apiOpt = yield* Effect.serviceOption(services.api)\n if (Option.isNone(apiOpt)) {\n yield* $.dispatchers.removeFailed(\n `[CRUDModule] Missing services.api; provide Layer.succeed(${id}.services.api, impl) via withLayer/withLayers/Runtime layer.`,\n )\n return\n }\n const api = apiOpt.value\n\n yield* api.remove(action.payload as EntityId)\n yield* $.dispatchers.removeSucceeded(action.payload as EntityId)\n }).pipe(Effect.catchAll((e) => $.dispatchers.removeFailed(toErrorMessage(e)))),\n )\n }),\n { id: 'install' },\n )\n\n const controller = {\n make: (\n runtime: Logix.ModuleRuntime<\n CrudState<Entity, QueryInput>,\n CrudAction<Entity, QueryInput, EntityId, ExtraActions>\n >,\n ): CrudController<Entity, QueryInput, EntityId, ExtraActions> => ({\n runtime,\n getState: runtime.getState,\n dispatch: runtime.dispatch,\n controller: {\n list: (input: QueryInput) =>\n runtime.dispatch({ _tag: 'query', payload: input } as CrudAction<Entity, QueryInput, EntityId, ExtraActions>),\n save: (entity: Entity) =>\n runtime.dispatch({ _tag: 'save', payload: entity } as CrudAction<Entity, QueryInput, EntityId, ExtraActions>),\n remove: (id: EntityId) =>\n runtime.dispatch({ _tag: 'remove', payload: id } as CrudAction<Entity, QueryInput, EntityId, ExtraActions>),\n clearError: () =>\n runtime.dispatch({ _tag: 'clearError' } as CrudAction<Entity, QueryInput, EntityId, ExtraActions>),\n idField,\n },\n }),\n }\n\n const EXTEND_HANDLE = Symbol.for('logix.module.handle.extend')\n ;(module.tag as unknown as Record<PropertyKey, unknown>)[EXTEND_HANDLE] = (\n runtime: Logix.ModuleRuntime<CrudState<Entity, QueryInput>, CrudAction<Entity, QueryInput, EntityId, ExtraActions>>,\n base: Logix.ModuleHandle<Logix.AnyModuleShape>,\n ) => {\n const crud = controller.make(runtime)\n return {\n ...base,\n controller: crud.controller,\n services,\n }\n }\n\n return module.implement({\n initial: {\n items: Array.from(spec.initial ?? []),\n loading: false,\n error: undefined,\n lastQuery: undefined,\n total: undefined,\n } as CrudState<Entity, QueryInput>,\n logics: [install],\n }) as unknown as CrudModule<Id, Entity, QueryInput, EntityId, ExtraActions>\n}\n\nexport const CRUDModule = Logix.Module.Manage.make({\n kind: 'crud',\n define: defineCrud,\n})\n"],"mappings":"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","names":["module","id"]}
|
package/dist/Crud.d.cts
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import * as Logix from '@logixjs/core';
|
|
2
|
+
import { Schema, Effect } from 'effect';
|
|
3
|
+
|
|
4
|
+
declare const DefaultQueryInputSchema: Schema.Struct<{
|
|
5
|
+
pageSize: typeof Schema.Number;
|
|
6
|
+
}>;
|
|
7
|
+
type CrudDefaultQueryInput = Schema.Schema.Type<typeof DefaultQueryInputSchema>;
|
|
8
|
+
interface CrudQueryResult<Entity> {
|
|
9
|
+
readonly items: ReadonlyArray<Entity>;
|
|
10
|
+
readonly total?: number;
|
|
11
|
+
}
|
|
12
|
+
interface CrudApi<Entity extends object, QueryInput, Id> {
|
|
13
|
+
readonly list: (input: QueryInput) => Effect.Effect<CrudQueryResult<Entity>, unknown, never>;
|
|
14
|
+
readonly save: (entity: Entity) => Effect.Effect<Entity, unknown, never>;
|
|
15
|
+
readonly remove: (id: Id) => Effect.Effect<void, unknown, never>;
|
|
16
|
+
}
|
|
17
|
+
interface CrudSpec<Entity extends object, QueryInput = CrudDefaultQueryInput, Id = string> {
|
|
18
|
+
readonly entity: Schema.Schema<Entity>;
|
|
19
|
+
readonly query?: Schema.Schema<QueryInput>;
|
|
20
|
+
readonly id?: Schema.Schema<Id>;
|
|
21
|
+
readonly initial?: ReadonlyArray<Entity>;
|
|
22
|
+
/**
|
|
23
|
+
* idField:
|
|
24
|
+
* - The default primary key field for upsert/remove in reducers (default: "id").
|
|
25
|
+
* - For more complex primary-key strategies, prefer handling results in a custom upper-layer Logic and writing back to state.
|
|
26
|
+
*/
|
|
27
|
+
readonly idField?: string;
|
|
28
|
+
}
|
|
29
|
+
type CrudState<Entity, QueryInput> = {
|
|
30
|
+
readonly items: ReadonlyArray<Entity>;
|
|
31
|
+
readonly loading: boolean;
|
|
32
|
+
readonly error: string | undefined;
|
|
33
|
+
readonly lastQuery: QueryInput | undefined;
|
|
34
|
+
readonly total: number | undefined;
|
|
35
|
+
};
|
|
36
|
+
type CrudActionMap<Entity extends object, QueryInput, Id, ExtraActions extends Record<string, Logix.AnySchema> = {}> = {
|
|
37
|
+
readonly query: Schema.Schema<QueryInput>;
|
|
38
|
+
readonly querySucceeded: Schema.Schema<CrudQueryResult<Entity>>;
|
|
39
|
+
readonly queryFailed: Schema.Schema<string>;
|
|
40
|
+
readonly save: Schema.Schema<Entity>;
|
|
41
|
+
readonly saveSucceeded: Schema.Schema<Entity>;
|
|
42
|
+
readonly saveFailed: Schema.Schema<string>;
|
|
43
|
+
readonly remove: Schema.Schema<Id>;
|
|
44
|
+
readonly removeSucceeded: Schema.Schema<Id>;
|
|
45
|
+
readonly removeFailed: Schema.Schema<string>;
|
|
46
|
+
readonly clearError: Schema.Schema<void>;
|
|
47
|
+
} & ExtraActions;
|
|
48
|
+
type CrudAction<Entity extends object, QueryInput, Id, ExtraActions extends Record<string, Logix.AnySchema> = {}> = Logix.ActionsFromMap<CrudActionMap<Entity, QueryInput, Id, ExtraActions>>;
|
|
49
|
+
type CrudShape<Entity extends object, QueryInput, Id, ExtraActions extends Record<string, Logix.AnySchema> = {}> = Logix.Shape<Schema.Schema<CrudState<Entity, QueryInput>>, CrudActionMap<Entity, QueryInput, Id, ExtraActions>>;
|
|
50
|
+
type CrudServices<Entity extends object, QueryInput, Id> = {
|
|
51
|
+
readonly api: Logix.State.Tag<CrudApi<Entity, QueryInput, Id>>;
|
|
52
|
+
};
|
|
53
|
+
type CrudHandleExt<Entity extends object, QueryInput, Id, ExtraActions extends Record<string, Logix.AnySchema> = {}> = {
|
|
54
|
+
readonly controller: CrudController<Entity, QueryInput, Id, ExtraActions>['controller'];
|
|
55
|
+
readonly services: CrudServices<Entity, QueryInput, Id>;
|
|
56
|
+
};
|
|
57
|
+
interface CrudController<Entity extends object, QueryInput, Id, ExtraActions extends Record<string, Logix.AnySchema> = {}> {
|
|
58
|
+
readonly runtime: Logix.ModuleRuntime<CrudState<Entity, QueryInput>, CrudAction<Entity, QueryInput, Id, ExtraActions>>;
|
|
59
|
+
readonly getState: Effect.Effect<CrudState<Entity, QueryInput>>;
|
|
60
|
+
readonly dispatch: (action: CrudAction<Entity, QueryInput, Id, ExtraActions>) => Effect.Effect<void>;
|
|
61
|
+
readonly controller: {
|
|
62
|
+
readonly list: (input: QueryInput) => Effect.Effect<void>;
|
|
63
|
+
readonly save: (entity: Entity) => Effect.Effect<void>;
|
|
64
|
+
readonly remove: (id: Id) => Effect.Effect<void>;
|
|
65
|
+
readonly clearError: () => Effect.Effect<void>;
|
|
66
|
+
readonly idField: string;
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
type CrudModule<Id extends string, Entity extends object, QueryInput, EntityId, ExtraActions extends Record<string, Logix.AnySchema> = {}> = Logix.Module.Module<Id, CrudShape<Entity, QueryInput, EntityId, ExtraActions>, CrudHandleExt<Entity, QueryInput, EntityId, ExtraActions>, unknown> & {
|
|
70
|
+
readonly _kind: 'Module';
|
|
71
|
+
readonly services: CrudServices<Entity, QueryInput, EntityId>;
|
|
72
|
+
};
|
|
73
|
+
declare const CRUDModule: {
|
|
74
|
+
readonly kind?: string;
|
|
75
|
+
readonly make: <Id extends string, Entity extends object, QueryInput = {
|
|
76
|
+
readonly pageSize: number;
|
|
77
|
+
}, EntityId = string, ExtraActions extends Record<string, Logix.AnySchema> = {}>(id: Id, spec: CrudSpec<Entity, QueryInput, EntityId>, extend?: Logix.Module.MakeExtendDef<Schema.Schema<CrudState<Entity, QueryInput>>, CrudActionMap<Entity, QueryInput, EntityId>, ExtraActions>) => CrudModule<Id, Entity, QueryInput, EntityId, ExtraActions>;
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
export { CRUDModule, type CrudAction, type CrudActionMap, type CrudApi, type CrudController, type CrudDefaultQueryInput, type CrudHandleExt, type CrudModule, type CrudQueryResult, type CrudServices, type CrudShape, type CrudSpec, type CrudState };
|
package/dist/Crud.d.ts
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import * as Logix from '@logixjs/core';
|
|
2
|
+
import { Schema, Effect } from 'effect';
|
|
3
|
+
|
|
4
|
+
declare const DefaultQueryInputSchema: Schema.Struct<{
|
|
5
|
+
pageSize: typeof Schema.Number;
|
|
6
|
+
}>;
|
|
7
|
+
type CrudDefaultQueryInput = Schema.Schema.Type<typeof DefaultQueryInputSchema>;
|
|
8
|
+
interface CrudQueryResult<Entity> {
|
|
9
|
+
readonly items: ReadonlyArray<Entity>;
|
|
10
|
+
readonly total?: number;
|
|
11
|
+
}
|
|
12
|
+
interface CrudApi<Entity extends object, QueryInput, Id> {
|
|
13
|
+
readonly list: (input: QueryInput) => Effect.Effect<CrudQueryResult<Entity>, unknown, never>;
|
|
14
|
+
readonly save: (entity: Entity) => Effect.Effect<Entity, unknown, never>;
|
|
15
|
+
readonly remove: (id: Id) => Effect.Effect<void, unknown, never>;
|
|
16
|
+
}
|
|
17
|
+
interface CrudSpec<Entity extends object, QueryInput = CrudDefaultQueryInput, Id = string> {
|
|
18
|
+
readonly entity: Schema.Schema<Entity>;
|
|
19
|
+
readonly query?: Schema.Schema<QueryInput>;
|
|
20
|
+
readonly id?: Schema.Schema<Id>;
|
|
21
|
+
readonly initial?: ReadonlyArray<Entity>;
|
|
22
|
+
/**
|
|
23
|
+
* idField:
|
|
24
|
+
* - The default primary key field for upsert/remove in reducers (default: "id").
|
|
25
|
+
* - For more complex primary-key strategies, prefer handling results in a custom upper-layer Logic and writing back to state.
|
|
26
|
+
*/
|
|
27
|
+
readonly idField?: string;
|
|
28
|
+
}
|
|
29
|
+
type CrudState<Entity, QueryInput> = {
|
|
30
|
+
readonly items: ReadonlyArray<Entity>;
|
|
31
|
+
readonly loading: boolean;
|
|
32
|
+
readonly error: string | undefined;
|
|
33
|
+
readonly lastQuery: QueryInput | undefined;
|
|
34
|
+
readonly total: number | undefined;
|
|
35
|
+
};
|
|
36
|
+
type CrudActionMap<Entity extends object, QueryInput, Id, ExtraActions extends Record<string, Logix.AnySchema> = {}> = {
|
|
37
|
+
readonly query: Schema.Schema<QueryInput>;
|
|
38
|
+
readonly querySucceeded: Schema.Schema<CrudQueryResult<Entity>>;
|
|
39
|
+
readonly queryFailed: Schema.Schema<string>;
|
|
40
|
+
readonly save: Schema.Schema<Entity>;
|
|
41
|
+
readonly saveSucceeded: Schema.Schema<Entity>;
|
|
42
|
+
readonly saveFailed: Schema.Schema<string>;
|
|
43
|
+
readonly remove: Schema.Schema<Id>;
|
|
44
|
+
readonly removeSucceeded: Schema.Schema<Id>;
|
|
45
|
+
readonly removeFailed: Schema.Schema<string>;
|
|
46
|
+
readonly clearError: Schema.Schema<void>;
|
|
47
|
+
} & ExtraActions;
|
|
48
|
+
type CrudAction<Entity extends object, QueryInput, Id, ExtraActions extends Record<string, Logix.AnySchema> = {}> = Logix.ActionsFromMap<CrudActionMap<Entity, QueryInput, Id, ExtraActions>>;
|
|
49
|
+
type CrudShape<Entity extends object, QueryInput, Id, ExtraActions extends Record<string, Logix.AnySchema> = {}> = Logix.Shape<Schema.Schema<CrudState<Entity, QueryInput>>, CrudActionMap<Entity, QueryInput, Id, ExtraActions>>;
|
|
50
|
+
type CrudServices<Entity extends object, QueryInput, Id> = {
|
|
51
|
+
readonly api: Logix.State.Tag<CrudApi<Entity, QueryInput, Id>>;
|
|
52
|
+
};
|
|
53
|
+
type CrudHandleExt<Entity extends object, QueryInput, Id, ExtraActions extends Record<string, Logix.AnySchema> = {}> = {
|
|
54
|
+
readonly controller: CrudController<Entity, QueryInput, Id, ExtraActions>['controller'];
|
|
55
|
+
readonly services: CrudServices<Entity, QueryInput, Id>;
|
|
56
|
+
};
|
|
57
|
+
interface CrudController<Entity extends object, QueryInput, Id, ExtraActions extends Record<string, Logix.AnySchema> = {}> {
|
|
58
|
+
readonly runtime: Logix.ModuleRuntime<CrudState<Entity, QueryInput>, CrudAction<Entity, QueryInput, Id, ExtraActions>>;
|
|
59
|
+
readonly getState: Effect.Effect<CrudState<Entity, QueryInput>>;
|
|
60
|
+
readonly dispatch: (action: CrudAction<Entity, QueryInput, Id, ExtraActions>) => Effect.Effect<void>;
|
|
61
|
+
readonly controller: {
|
|
62
|
+
readonly list: (input: QueryInput) => Effect.Effect<void>;
|
|
63
|
+
readonly save: (entity: Entity) => Effect.Effect<void>;
|
|
64
|
+
readonly remove: (id: Id) => Effect.Effect<void>;
|
|
65
|
+
readonly clearError: () => Effect.Effect<void>;
|
|
66
|
+
readonly idField: string;
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
type CrudModule<Id extends string, Entity extends object, QueryInput, EntityId, ExtraActions extends Record<string, Logix.AnySchema> = {}> = Logix.Module.Module<Id, CrudShape<Entity, QueryInput, EntityId, ExtraActions>, CrudHandleExt<Entity, QueryInput, EntityId, ExtraActions>, unknown> & {
|
|
70
|
+
readonly _kind: 'Module';
|
|
71
|
+
readonly services: CrudServices<Entity, QueryInput, EntityId>;
|
|
72
|
+
};
|
|
73
|
+
declare const CRUDModule: {
|
|
74
|
+
readonly kind?: string;
|
|
75
|
+
readonly make: <Id extends string, Entity extends object, QueryInput = {
|
|
76
|
+
readonly pageSize: number;
|
|
77
|
+
}, EntityId = string, ExtraActions extends Record<string, Logix.AnySchema> = {}>(id: Id, spec: CrudSpec<Entity, QueryInput, EntityId>, extend?: Logix.Module.MakeExtendDef<Schema.Schema<CrudState<Entity, QueryInput>>, CrudActionMap<Entity, QueryInput, EntityId>, ExtraActions>) => CrudModule<Id, Entity, QueryInput, EntityId, ExtraActions>;
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
export { CRUDModule, type CrudAction, type CrudActionMap, type CrudApi, type CrudController, type CrudDefaultQueryInput, type CrudHandleExt, type CrudModule, type CrudQueryResult, type CrudServices, type CrudShape, type CrudSpec, type CrudState };
|
package/dist/Crud.js
ADDED
package/dist/Crud.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
// src/internal/crud/Crud.ts
|
|
2
|
+
import * as Logix from "@logixjs/core";
|
|
3
|
+
import { Context, Effect, Option, Schema } from "effect";
|
|
4
|
+
var DefaultQueryInputSchema = Schema.Struct({
|
|
5
|
+
pageSize: Schema.Number
|
|
6
|
+
});
|
|
7
|
+
var makeActions = (entity, query, id) => ({
|
|
8
|
+
query,
|
|
9
|
+
querySucceeded: Schema.Struct({
|
|
10
|
+
items: Schema.Array(entity),
|
|
11
|
+
total: Schema.optional(Schema.Number)
|
|
12
|
+
}),
|
|
13
|
+
queryFailed: Schema.String,
|
|
14
|
+
save: entity,
|
|
15
|
+
saveSucceeded: entity,
|
|
16
|
+
saveFailed: Schema.String,
|
|
17
|
+
remove: id,
|
|
18
|
+
removeSucceeded: id,
|
|
19
|
+
removeFailed: Schema.String,
|
|
20
|
+
clearError: Schema.Void
|
|
21
|
+
});
|
|
22
|
+
var toErrorMessage = (error) => {
|
|
23
|
+
if (error === null || error === void 0) return "unknown error";
|
|
24
|
+
if (typeof error === "string") return error;
|
|
25
|
+
if (error instanceof Error && typeof error.message === "string" && error.message.length > 0) {
|
|
26
|
+
return error.message;
|
|
27
|
+
}
|
|
28
|
+
if (typeof error === "object") {
|
|
29
|
+
if ("message" in error) {
|
|
30
|
+
const message = error.message;
|
|
31
|
+
if (typeof message === "string" && message.length > 0) return message;
|
|
32
|
+
}
|
|
33
|
+
try {
|
|
34
|
+
const json = JSON.stringify(error);
|
|
35
|
+
if (typeof json === "string" && json.length > 0) return json;
|
|
36
|
+
} catch {
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return String(error);
|
|
40
|
+
};
|
|
41
|
+
var upsertByIdField = (items, entity, idField) => {
|
|
42
|
+
const id = entity[idField];
|
|
43
|
+
const idx = items.findIndex((x) => x[idField] === id);
|
|
44
|
+
if (idx < 0) return [...items, entity];
|
|
45
|
+
return items.map((x, i) => i === idx ? entity : x);
|
|
46
|
+
};
|
|
47
|
+
var removeByIdField = (items, id, idField) => items.filter((x) => x[idField] !== id);
|
|
48
|
+
var defineCrud = (id, spec, extend) => {
|
|
49
|
+
const QuerySchema = spec.query ?? DefaultQueryInputSchema;
|
|
50
|
+
const IdSchema = spec.id ?? Schema.String;
|
|
51
|
+
const idField = spec.idField ?? "id";
|
|
52
|
+
class Api extends Context.Tag(`${id}/crud/api`)() {
|
|
53
|
+
}
|
|
54
|
+
const services = { api: Api };
|
|
55
|
+
const Actions = makeActions(spec.entity, QuerySchema, IdSchema);
|
|
56
|
+
const StateSchema = Schema.Struct({
|
|
57
|
+
items: Schema.Array(spec.entity),
|
|
58
|
+
loading: Schema.Boolean,
|
|
59
|
+
error: Schema.UndefinedOr(Schema.String),
|
|
60
|
+
lastQuery: Schema.UndefinedOr(QuerySchema),
|
|
61
|
+
total: Schema.UndefinedOr(Schema.Number)
|
|
62
|
+
});
|
|
63
|
+
const reducers = {
|
|
64
|
+
query: (state, action, sink) => {
|
|
65
|
+
sink?.("loading");
|
|
66
|
+
sink?.("error");
|
|
67
|
+
sink?.("lastQuery");
|
|
68
|
+
return {
|
|
69
|
+
...state,
|
|
70
|
+
loading: true,
|
|
71
|
+
error: void 0,
|
|
72
|
+
lastQuery: action.payload
|
|
73
|
+
};
|
|
74
|
+
},
|
|
75
|
+
querySucceeded: (state, action, sink) => {
|
|
76
|
+
sink?.("loading");
|
|
77
|
+
sink?.("error");
|
|
78
|
+
sink?.("items");
|
|
79
|
+
sink?.("total");
|
|
80
|
+
return {
|
|
81
|
+
...state,
|
|
82
|
+
loading: false,
|
|
83
|
+
error: void 0,
|
|
84
|
+
items: action.payload.items,
|
|
85
|
+
total: action.payload.total
|
|
86
|
+
};
|
|
87
|
+
},
|
|
88
|
+
queryFailed: (state, action, sink) => {
|
|
89
|
+
sink?.("loading");
|
|
90
|
+
sink?.("error");
|
|
91
|
+
return {
|
|
92
|
+
...state,
|
|
93
|
+
loading: false,
|
|
94
|
+
error: action.payload
|
|
95
|
+
};
|
|
96
|
+
},
|
|
97
|
+
save: (state, _action, sink) => {
|
|
98
|
+
sink?.("loading");
|
|
99
|
+
sink?.("error");
|
|
100
|
+
return {
|
|
101
|
+
...state,
|
|
102
|
+
loading: true,
|
|
103
|
+
error: void 0
|
|
104
|
+
};
|
|
105
|
+
},
|
|
106
|
+
saveSucceeded: (state, action, sink) => {
|
|
107
|
+
sink?.("loading");
|
|
108
|
+
sink?.("error");
|
|
109
|
+
sink?.("items");
|
|
110
|
+
return {
|
|
111
|
+
...state,
|
|
112
|
+
loading: false,
|
|
113
|
+
error: void 0,
|
|
114
|
+
items: upsertByIdField(state.items, action.payload, idField)
|
|
115
|
+
};
|
|
116
|
+
},
|
|
117
|
+
saveFailed: (state, action, sink) => {
|
|
118
|
+
sink?.("loading");
|
|
119
|
+
sink?.("error");
|
|
120
|
+
return {
|
|
121
|
+
...state,
|
|
122
|
+
loading: false,
|
|
123
|
+
error: action.payload
|
|
124
|
+
};
|
|
125
|
+
},
|
|
126
|
+
remove: (state, _action, sink) => {
|
|
127
|
+
sink?.("loading");
|
|
128
|
+
sink?.("error");
|
|
129
|
+
return {
|
|
130
|
+
...state,
|
|
131
|
+
loading: true,
|
|
132
|
+
error: void 0
|
|
133
|
+
};
|
|
134
|
+
},
|
|
135
|
+
removeSucceeded: (state, action, sink) => {
|
|
136
|
+
sink?.("loading");
|
|
137
|
+
sink?.("error");
|
|
138
|
+
sink?.("items");
|
|
139
|
+
return {
|
|
140
|
+
...state,
|
|
141
|
+
loading: false,
|
|
142
|
+
error: void 0,
|
|
143
|
+
items: removeByIdField(state.items, action.payload, idField)
|
|
144
|
+
};
|
|
145
|
+
},
|
|
146
|
+
removeFailed: (state, action, sink) => {
|
|
147
|
+
sink?.("loading");
|
|
148
|
+
sink?.("error");
|
|
149
|
+
return {
|
|
150
|
+
...state,
|
|
151
|
+
loading: false,
|
|
152
|
+
error: action.payload
|
|
153
|
+
};
|
|
154
|
+
},
|
|
155
|
+
clearError: (state, _action, sink) => {
|
|
156
|
+
sink?.("error");
|
|
157
|
+
return {
|
|
158
|
+
...state,
|
|
159
|
+
error: void 0
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
};
|
|
163
|
+
const def = {
|
|
164
|
+
state: StateSchema,
|
|
165
|
+
actions: Actions,
|
|
166
|
+
reducers,
|
|
167
|
+
schemas: { entity: spec.entity },
|
|
168
|
+
meta: { kind: "crud", idField },
|
|
169
|
+
services
|
|
170
|
+
};
|
|
171
|
+
const module = extend ? Logix.Module.make(
|
|
172
|
+
id,
|
|
173
|
+
def,
|
|
174
|
+
extend
|
|
175
|
+
) : Logix.Module.make(id, def);
|
|
176
|
+
const install = module.logic(
|
|
177
|
+
($) => Effect.gen(function* () {
|
|
178
|
+
yield* $.onAction("query").runFork(
|
|
179
|
+
(action) => Effect.gen(function* () {
|
|
180
|
+
const apiOpt = yield* Effect.serviceOption(services.api);
|
|
181
|
+
if (Option.isNone(apiOpt)) {
|
|
182
|
+
yield* $.dispatchers.queryFailed(
|
|
183
|
+
`[CRUDModule] Missing services.api; provide Layer.succeed(${id}.services.api, impl) via withLayer/withLayers/Runtime layer.`
|
|
184
|
+
);
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
const api = apiOpt.value;
|
|
188
|
+
const result = yield* api.list(action.payload);
|
|
189
|
+
yield* $.dispatchers.querySucceeded(result);
|
|
190
|
+
}).pipe(Effect.catchAll((e) => $.dispatchers.queryFailed(toErrorMessage(e))))
|
|
191
|
+
);
|
|
192
|
+
yield* $.onAction("save").runFork(
|
|
193
|
+
(action) => Effect.gen(function* () {
|
|
194
|
+
const apiOpt = yield* Effect.serviceOption(services.api);
|
|
195
|
+
if (Option.isNone(apiOpt)) {
|
|
196
|
+
yield* $.dispatchers.saveFailed(
|
|
197
|
+
`[CRUDModule] Missing services.api; provide Layer.succeed(${id}.services.api, impl) via withLayer/withLayers/Runtime layer.`
|
|
198
|
+
);
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
const api = apiOpt.value;
|
|
202
|
+
const entity = yield* api.save(action.payload);
|
|
203
|
+
yield* $.dispatchers.saveSucceeded(entity);
|
|
204
|
+
}).pipe(Effect.catchAll((e) => $.dispatchers.saveFailed(toErrorMessage(e))))
|
|
205
|
+
);
|
|
206
|
+
yield* $.onAction("remove").runFork(
|
|
207
|
+
(action) => Effect.gen(function* () {
|
|
208
|
+
const apiOpt = yield* Effect.serviceOption(services.api);
|
|
209
|
+
if (Option.isNone(apiOpt)) {
|
|
210
|
+
yield* $.dispatchers.removeFailed(
|
|
211
|
+
`[CRUDModule] Missing services.api; provide Layer.succeed(${id}.services.api, impl) via withLayer/withLayers/Runtime layer.`
|
|
212
|
+
);
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
const api = apiOpt.value;
|
|
216
|
+
yield* api.remove(action.payload);
|
|
217
|
+
yield* $.dispatchers.removeSucceeded(action.payload);
|
|
218
|
+
}).pipe(Effect.catchAll((e) => $.dispatchers.removeFailed(toErrorMessage(e))))
|
|
219
|
+
);
|
|
220
|
+
}),
|
|
221
|
+
{ id: "install" }
|
|
222
|
+
);
|
|
223
|
+
const controller = {
|
|
224
|
+
make: (runtime) => ({
|
|
225
|
+
runtime,
|
|
226
|
+
getState: runtime.getState,
|
|
227
|
+
dispatch: runtime.dispatch,
|
|
228
|
+
controller: {
|
|
229
|
+
list: (input) => runtime.dispatch({ _tag: "query", payload: input }),
|
|
230
|
+
save: (entity) => runtime.dispatch({ _tag: "save", payload: entity }),
|
|
231
|
+
remove: (id2) => runtime.dispatch({ _tag: "remove", payload: id2 }),
|
|
232
|
+
clearError: () => runtime.dispatch({ _tag: "clearError" }),
|
|
233
|
+
idField
|
|
234
|
+
}
|
|
235
|
+
})
|
|
236
|
+
};
|
|
237
|
+
const EXTEND_HANDLE = /* @__PURE__ */ Symbol.for("logix.module.handle.extend");
|
|
238
|
+
module.tag[EXTEND_HANDLE] = (runtime, base) => {
|
|
239
|
+
const crud = controller.make(runtime);
|
|
240
|
+
return {
|
|
241
|
+
...base,
|
|
242
|
+
controller: crud.controller,
|
|
243
|
+
services
|
|
244
|
+
};
|
|
245
|
+
};
|
|
246
|
+
return module.implement({
|
|
247
|
+
initial: {
|
|
248
|
+
items: Array.from(spec.initial ?? []),
|
|
249
|
+
loading: false,
|
|
250
|
+
error: void 0,
|
|
251
|
+
lastQuery: void 0,
|
|
252
|
+
total: void 0
|
|
253
|
+
},
|
|
254
|
+
logics: [install]
|
|
255
|
+
});
|
|
256
|
+
};
|
|
257
|
+
var CRUDModule = Logix.Module.Manage.make({
|
|
258
|
+
kind: "crud",
|
|
259
|
+
define: defineCrud
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
export {
|
|
263
|
+
CRUDModule
|
|
264
|
+
};
|
|
265
|
+
//# sourceMappingURL=chunk-SCBUT2ZQ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/internal/crud/Crud.ts"],"sourcesContent":["import * as Logix from '@logixjs/core'\nimport { Context, Effect, Option, Schema } from 'effect'\n\nconst DefaultQueryInputSchema = Schema.Struct({\n pageSize: Schema.Number,\n})\n\nexport type CrudDefaultQueryInput = Schema.Schema.Type<typeof DefaultQueryInputSchema>\n\nexport interface CrudQueryResult<Entity> {\n readonly items: ReadonlyArray<Entity>\n readonly total?: number\n}\n\nexport interface CrudApi<Entity extends object, QueryInput, Id> {\n readonly list: (input: QueryInput) => Effect.Effect<CrudQueryResult<Entity>, unknown, never>\n readonly save: (entity: Entity) => Effect.Effect<Entity, unknown, never>\n readonly remove: (id: Id) => Effect.Effect<void, unknown, never>\n}\n\nexport interface CrudSpec<Entity extends object, QueryInput = CrudDefaultQueryInput, Id = string> {\n readonly entity: Schema.Schema<Entity>\n readonly query?: Schema.Schema<QueryInput>\n readonly id?: Schema.Schema<Id>\n readonly initial?: ReadonlyArray<Entity>\n /**\n * idField:\n * - The default primary key field for upsert/remove in reducers (default: \"id\").\n * - For more complex primary-key strategies, prefer handling results in a custom upper-layer Logic and writing back to state.\n */\n readonly idField?: string\n}\n\nexport type CrudState<Entity, QueryInput> = {\n readonly items: ReadonlyArray<Entity>\n readonly loading: boolean\n readonly error: string | undefined\n readonly lastQuery: QueryInput | undefined\n readonly total: number | undefined\n}\n\nexport type CrudActionMap<\n Entity extends object,\n QueryInput,\n Id,\n ExtraActions extends Record<string, Logix.AnySchema> = {},\n> = {\n readonly query: Schema.Schema<QueryInput>\n readonly querySucceeded: Schema.Schema<CrudQueryResult<Entity>>\n readonly queryFailed: Schema.Schema<string>\n\n readonly save: Schema.Schema<Entity>\n readonly saveSucceeded: Schema.Schema<Entity>\n readonly saveFailed: Schema.Schema<string>\n\n readonly remove: Schema.Schema<Id>\n readonly removeSucceeded: Schema.Schema<Id>\n readonly removeFailed: Schema.Schema<string>\n\n readonly clearError: Schema.Schema<void>\n} & ExtraActions\n\nexport type CrudAction<\n Entity extends object,\n QueryInput,\n Id,\n ExtraActions extends Record<string, Logix.AnySchema> = {},\n> = Logix.ActionsFromMap<CrudActionMap<Entity, QueryInput, Id, ExtraActions>>\n\nexport type CrudShape<\n Entity extends object,\n QueryInput,\n Id,\n ExtraActions extends Record<string, Logix.AnySchema> = {},\n> = Logix.Shape<Schema.Schema<CrudState<Entity, QueryInput>>, CrudActionMap<Entity, QueryInput, Id, ExtraActions>>\n\nexport type CrudServices<Entity extends object, QueryInput, Id> = {\n readonly api: Logix.State.Tag<CrudApi<Entity, QueryInput, Id>>\n}\n\nexport type CrudHandleExt<\n Entity extends object,\n QueryInput,\n Id,\n ExtraActions extends Record<string, Logix.AnySchema> = {},\n> = {\n readonly controller: CrudController<Entity, QueryInput, Id, ExtraActions>['controller']\n readonly services: CrudServices<Entity, QueryInput, Id>\n}\n\nexport interface CrudController<\n Entity extends object,\n QueryInput,\n Id,\n ExtraActions extends Record<string, Logix.AnySchema> = {},\n> {\n readonly runtime: Logix.ModuleRuntime<CrudState<Entity, QueryInput>, CrudAction<Entity, QueryInput, Id, ExtraActions>>\n readonly getState: Effect.Effect<CrudState<Entity, QueryInput>>\n readonly dispatch: (action: CrudAction<Entity, QueryInput, Id, ExtraActions>) => Effect.Effect<void>\n readonly controller: {\n readonly list: (input: QueryInput) => Effect.Effect<void>\n readonly save: (entity: Entity) => Effect.Effect<void>\n readonly remove: (id: Id) => Effect.Effect<void>\n readonly clearError: () => Effect.Effect<void>\n readonly idField: string\n }\n}\n\nconst makeActions = <Entity extends object, QueryInput, Id>(\n entity: Schema.Schema<Entity>,\n query: Schema.Schema<QueryInput>,\n id: Schema.Schema<Id>,\n): CrudActionMap<Entity, QueryInput, Id> =>\n ({\n query,\n querySucceeded: Schema.Struct({\n items: Schema.Array(entity),\n total: Schema.optional(Schema.Number),\n }) as Schema.Schema<CrudQueryResult<Entity>>,\n queryFailed: Schema.String,\n\n save: entity,\n saveSucceeded: entity,\n saveFailed: Schema.String,\n\n remove: id,\n removeSucceeded: id,\n removeFailed: Schema.String,\n\n clearError: Schema.Void,\n }) satisfies CrudActionMap<Entity, QueryInput, Id>\n\nconst toErrorMessage = (error: unknown): string => {\n if (error === null || error === undefined) return 'unknown error'\n if (typeof error === 'string') return error\n if (error instanceof Error && typeof error.message === 'string' && error.message.length > 0) {\n return error.message\n }\n if (typeof error === 'object') {\n if ('message' in error) {\n const message = (error as { readonly message?: unknown }).message\n if (typeof message === 'string' && message.length > 0) return message\n }\n try {\n const json = JSON.stringify(error)\n if (typeof json === 'string' && json.length > 0) return json\n } catch {\n // ignore\n }\n }\n return String(error)\n}\n\nconst upsertByIdField = <Entity extends object>(\n items: ReadonlyArray<Entity>,\n entity: Entity,\n idField: string,\n): ReadonlyArray<Entity> => {\n const id = (entity as Record<string, unknown>)[idField]\n const idx = items.findIndex((x) => (x as Record<string, unknown>)[idField] === id)\n if (idx < 0) return [...items, entity]\n return items.map((x, i) => (i === idx ? entity : x))\n}\n\nconst removeByIdField = <Entity extends object>(\n items: ReadonlyArray<Entity>,\n id: unknown,\n idField: string,\n): ReadonlyArray<Entity> => items.filter((x) => (x as Record<string, unknown>)[idField] !== id)\n\nexport type CrudModule<\n Id extends string,\n Entity extends object,\n QueryInput,\n EntityId,\n ExtraActions extends Record<string, Logix.AnySchema> = {},\n> = Logix.Module.Module<\n Id,\n CrudShape<Entity, QueryInput, EntityId, ExtraActions>,\n CrudHandleExt<Entity, QueryInput, EntityId, ExtraActions>,\n unknown\n> & {\n readonly _kind: 'Module'\n readonly services: CrudServices<Entity, QueryInput, EntityId>\n}\n\nconst defineCrud = <\n Id extends string,\n Entity extends object,\n QueryInput = CrudDefaultQueryInput,\n EntityId = string,\n ExtraActions extends Record<string, Logix.AnySchema> = {},\n>(\n id: Id,\n spec: CrudSpec<Entity, QueryInput, EntityId>,\n extend?: Logix.Module.MakeExtendDef<\n Schema.Schema<CrudState<Entity, QueryInput>>,\n CrudActionMap<Entity, QueryInput, EntityId>,\n ExtraActions\n >,\n): CrudModule<Id, Entity, QueryInput, EntityId, ExtraActions> => {\n const QuerySchema = (spec.query ?? DefaultQueryInputSchema) as Schema.Schema<QueryInput>\n const IdSchema = (spec.id ?? Schema.String) as Schema.Schema<EntityId>\n\n const idField = spec.idField ?? 'id'\n\n class Api extends Context.Tag(`${id}/crud/api`)<Api, CrudApi<Entity, QueryInput, EntityId>>() {}\n\n const services = { api: Api } as const satisfies CrudServices<Entity, QueryInput, EntityId>\n\n const Actions = makeActions(spec.entity, QuerySchema, IdSchema)\n\n const StateSchema = Schema.Struct({\n items: Schema.Array(spec.entity),\n loading: Schema.Boolean,\n error: Schema.UndefinedOr(Schema.String),\n lastQuery: Schema.UndefinedOr(QuerySchema),\n total: Schema.UndefinedOr(Schema.Number),\n })\n\n type Reducers = Logix.ReducersFromMap<typeof StateSchema, CrudActionMap<Entity, QueryInput, EntityId>>\n\n const reducers: Reducers = {\n query: (state, action, sink) => {\n sink?.('loading')\n sink?.('error')\n sink?.('lastQuery')\n return {\n ...state,\n loading: true,\n error: undefined,\n lastQuery: action.payload,\n }\n },\n querySucceeded: (state, action, sink) => {\n sink?.('loading')\n sink?.('error')\n sink?.('items')\n sink?.('total')\n return {\n ...state,\n loading: false,\n error: undefined,\n items: action.payload.items,\n total: action.payload.total,\n }\n },\n queryFailed: (state, action, sink) => {\n sink?.('loading')\n sink?.('error')\n return {\n ...state,\n loading: false,\n error: action.payload,\n }\n },\n\n save: (state, _action, sink) => {\n sink?.('loading')\n sink?.('error')\n return {\n ...state,\n loading: true,\n error: undefined,\n }\n },\n saveSucceeded: (state, action, sink) => {\n sink?.('loading')\n sink?.('error')\n sink?.('items')\n return {\n ...state,\n loading: false,\n error: undefined,\n items: upsertByIdField(state.items, action.payload as Entity, idField),\n }\n },\n saveFailed: (state, action, sink) => {\n sink?.('loading')\n sink?.('error')\n return {\n ...state,\n loading: false,\n error: action.payload,\n }\n },\n\n remove: (state, _action, sink) => {\n sink?.('loading')\n sink?.('error')\n return {\n ...state,\n loading: true,\n error: undefined,\n }\n },\n removeSucceeded: (state, action, sink) => {\n sink?.('loading')\n sink?.('error')\n sink?.('items')\n return {\n ...state,\n loading: false,\n error: undefined,\n items: removeByIdField(state.items, action.payload, idField),\n }\n },\n removeFailed: (state, action, sink) => {\n sink?.('loading')\n sink?.('error')\n return {\n ...state,\n loading: false,\n error: action.payload,\n }\n },\n\n clearError: (state, _action, sink) => {\n sink?.('error')\n return {\n ...state,\n error: undefined,\n }\n },\n }\n\n const def = {\n state: StateSchema,\n actions: Actions,\n reducers,\n schemas: { entity: spec.entity },\n meta: { kind: 'crud', idField },\n services,\n }\n\n const module = extend\n ? Logix.Module.make<\n Id,\n typeof StateSchema,\n CrudActionMap<Entity, QueryInput, EntityId>,\n CrudHandleExt<Entity, QueryInput, EntityId, ExtraActions>\n >(\n id,\n def,\n extend as unknown as Logix.Module.MakeExtendDef<\n typeof StateSchema,\n CrudActionMap<Entity, QueryInput, EntityId>,\n ExtraActions\n >,\n )\n : Logix.Module.make<\n Id,\n typeof StateSchema,\n CrudActionMap<Entity, QueryInput, EntityId>,\n CrudHandleExt<Entity, QueryInput, EntityId, ExtraActions>\n >(id, def)\n\n const install = module.logic(\n ($) =>\n Effect.gen(function* () {\n yield* $.onAction('query').runFork((action) =>\n Effect.gen(function* () {\n const apiOpt = yield* Effect.serviceOption(services.api)\n if (Option.isNone(apiOpt)) {\n yield* $.dispatchers.queryFailed(\n `[CRUDModule] Missing services.api; provide Layer.succeed(${id}.services.api, impl) via withLayer/withLayers/Runtime layer.`,\n )\n return\n }\n const api = apiOpt.value\n\n const result = yield* api.list(action.payload as QueryInput)\n yield* $.dispatchers.querySucceeded(result)\n }).pipe(Effect.catchAll((e) => $.dispatchers.queryFailed(toErrorMessage(e)))),\n )\n\n yield* $.onAction('save').runFork((action) =>\n Effect.gen(function* () {\n const apiOpt = yield* Effect.serviceOption(services.api)\n if (Option.isNone(apiOpt)) {\n yield* $.dispatchers.saveFailed(\n `[CRUDModule] Missing services.api; provide Layer.succeed(${id}.services.api, impl) via withLayer/withLayers/Runtime layer.`,\n )\n return\n }\n const api = apiOpt.value\n\n const entity = yield* api.save(action.payload as Entity)\n yield* $.dispatchers.saveSucceeded(entity)\n }).pipe(Effect.catchAll((e) => $.dispatchers.saveFailed(toErrorMessage(e)))),\n )\n\n yield* $.onAction('remove').runFork((action) =>\n Effect.gen(function* () {\n const apiOpt = yield* Effect.serviceOption(services.api)\n if (Option.isNone(apiOpt)) {\n yield* $.dispatchers.removeFailed(\n `[CRUDModule] Missing services.api; provide Layer.succeed(${id}.services.api, impl) via withLayer/withLayers/Runtime layer.`,\n )\n return\n }\n const api = apiOpt.value\n\n yield* api.remove(action.payload as EntityId)\n yield* $.dispatchers.removeSucceeded(action.payload as EntityId)\n }).pipe(Effect.catchAll((e) => $.dispatchers.removeFailed(toErrorMessage(e)))),\n )\n }),\n { id: 'install' },\n )\n\n const controller = {\n make: (\n runtime: Logix.ModuleRuntime<\n CrudState<Entity, QueryInput>,\n CrudAction<Entity, QueryInput, EntityId, ExtraActions>\n >,\n ): CrudController<Entity, QueryInput, EntityId, ExtraActions> => ({\n runtime,\n getState: runtime.getState,\n dispatch: runtime.dispatch,\n controller: {\n list: (input: QueryInput) =>\n runtime.dispatch({ _tag: 'query', payload: input } as CrudAction<Entity, QueryInput, EntityId, ExtraActions>),\n save: (entity: Entity) =>\n runtime.dispatch({ _tag: 'save', payload: entity } as CrudAction<Entity, QueryInput, EntityId, ExtraActions>),\n remove: (id: EntityId) =>\n runtime.dispatch({ _tag: 'remove', payload: id } as CrudAction<Entity, QueryInput, EntityId, ExtraActions>),\n clearError: () =>\n runtime.dispatch({ _tag: 'clearError' } as CrudAction<Entity, QueryInput, EntityId, ExtraActions>),\n idField,\n },\n }),\n }\n\n const EXTEND_HANDLE = Symbol.for('logix.module.handle.extend')\n ;(module.tag as unknown as Record<PropertyKey, unknown>)[EXTEND_HANDLE] = (\n runtime: Logix.ModuleRuntime<CrudState<Entity, QueryInput>, CrudAction<Entity, QueryInput, EntityId, ExtraActions>>,\n base: Logix.ModuleHandle<Logix.AnyModuleShape>,\n ) => {\n const crud = controller.make(runtime)\n return {\n ...base,\n controller: crud.controller,\n services,\n }\n }\n\n return module.implement({\n initial: {\n items: Array.from(spec.initial ?? []),\n loading: false,\n error: undefined,\n lastQuery: undefined,\n total: undefined,\n } as CrudState<Entity, QueryInput>,\n logics: [install],\n }) as unknown as CrudModule<Id, Entity, QueryInput, EntityId, ExtraActions>\n}\n\nexport const CRUDModule = Logix.Module.Manage.make({\n kind: 'crud',\n define: defineCrud,\n})\n"],"mappings":"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","names":["id"]}
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/index.ts
|
|
31
|
+
var index_exports = {};
|
|
32
|
+
__export(index_exports, {
|
|
33
|
+
CRUDModule: () => CRUDModule
|
|
34
|
+
});
|
|
35
|
+
module.exports = __toCommonJS(index_exports);
|
|
36
|
+
|
|
37
|
+
// src/internal/crud/Crud.ts
|
|
38
|
+
var Logix = __toESM(require("@logixjs/core"), 1);
|
|
39
|
+
var import_effect = require("effect");
|
|
40
|
+
var DefaultQueryInputSchema = import_effect.Schema.Struct({
|
|
41
|
+
pageSize: import_effect.Schema.Number
|
|
42
|
+
});
|
|
43
|
+
var makeActions = (entity, query, id) => ({
|
|
44
|
+
query,
|
|
45
|
+
querySucceeded: import_effect.Schema.Struct({
|
|
46
|
+
items: import_effect.Schema.Array(entity),
|
|
47
|
+
total: import_effect.Schema.optional(import_effect.Schema.Number)
|
|
48
|
+
}),
|
|
49
|
+
queryFailed: import_effect.Schema.String,
|
|
50
|
+
save: entity,
|
|
51
|
+
saveSucceeded: entity,
|
|
52
|
+
saveFailed: import_effect.Schema.String,
|
|
53
|
+
remove: id,
|
|
54
|
+
removeSucceeded: id,
|
|
55
|
+
removeFailed: import_effect.Schema.String,
|
|
56
|
+
clearError: import_effect.Schema.Void
|
|
57
|
+
});
|
|
58
|
+
var toErrorMessage = (error) => {
|
|
59
|
+
if (error === null || error === void 0) return "unknown error";
|
|
60
|
+
if (typeof error === "string") return error;
|
|
61
|
+
if (error instanceof Error && typeof error.message === "string" && error.message.length > 0) {
|
|
62
|
+
return error.message;
|
|
63
|
+
}
|
|
64
|
+
if (typeof error === "object") {
|
|
65
|
+
if ("message" in error) {
|
|
66
|
+
const message = error.message;
|
|
67
|
+
if (typeof message === "string" && message.length > 0) return message;
|
|
68
|
+
}
|
|
69
|
+
try {
|
|
70
|
+
const json = JSON.stringify(error);
|
|
71
|
+
if (typeof json === "string" && json.length > 0) return json;
|
|
72
|
+
} catch {
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return String(error);
|
|
76
|
+
};
|
|
77
|
+
var upsertByIdField = (items, entity, idField) => {
|
|
78
|
+
const id = entity[idField];
|
|
79
|
+
const idx = items.findIndex((x) => x[idField] === id);
|
|
80
|
+
if (idx < 0) return [...items, entity];
|
|
81
|
+
return items.map((x, i) => i === idx ? entity : x);
|
|
82
|
+
};
|
|
83
|
+
var removeByIdField = (items, id, idField) => items.filter((x) => x[idField] !== id);
|
|
84
|
+
var defineCrud = (id, spec, extend) => {
|
|
85
|
+
const QuerySchema = spec.query ?? DefaultQueryInputSchema;
|
|
86
|
+
const IdSchema = spec.id ?? import_effect.Schema.String;
|
|
87
|
+
const idField = spec.idField ?? "id";
|
|
88
|
+
class Api extends import_effect.Context.Tag(`${id}/crud/api`)() {
|
|
89
|
+
}
|
|
90
|
+
const services = { api: Api };
|
|
91
|
+
const Actions = makeActions(spec.entity, QuerySchema, IdSchema);
|
|
92
|
+
const StateSchema = import_effect.Schema.Struct({
|
|
93
|
+
items: import_effect.Schema.Array(spec.entity),
|
|
94
|
+
loading: import_effect.Schema.Boolean,
|
|
95
|
+
error: import_effect.Schema.UndefinedOr(import_effect.Schema.String),
|
|
96
|
+
lastQuery: import_effect.Schema.UndefinedOr(QuerySchema),
|
|
97
|
+
total: import_effect.Schema.UndefinedOr(import_effect.Schema.Number)
|
|
98
|
+
});
|
|
99
|
+
const reducers = {
|
|
100
|
+
query: (state, action, sink) => {
|
|
101
|
+
sink?.("loading");
|
|
102
|
+
sink?.("error");
|
|
103
|
+
sink?.("lastQuery");
|
|
104
|
+
return {
|
|
105
|
+
...state,
|
|
106
|
+
loading: true,
|
|
107
|
+
error: void 0,
|
|
108
|
+
lastQuery: action.payload
|
|
109
|
+
};
|
|
110
|
+
},
|
|
111
|
+
querySucceeded: (state, action, sink) => {
|
|
112
|
+
sink?.("loading");
|
|
113
|
+
sink?.("error");
|
|
114
|
+
sink?.("items");
|
|
115
|
+
sink?.("total");
|
|
116
|
+
return {
|
|
117
|
+
...state,
|
|
118
|
+
loading: false,
|
|
119
|
+
error: void 0,
|
|
120
|
+
items: action.payload.items,
|
|
121
|
+
total: action.payload.total
|
|
122
|
+
};
|
|
123
|
+
},
|
|
124
|
+
queryFailed: (state, action, sink) => {
|
|
125
|
+
sink?.("loading");
|
|
126
|
+
sink?.("error");
|
|
127
|
+
return {
|
|
128
|
+
...state,
|
|
129
|
+
loading: false,
|
|
130
|
+
error: action.payload
|
|
131
|
+
};
|
|
132
|
+
},
|
|
133
|
+
save: (state, _action, sink) => {
|
|
134
|
+
sink?.("loading");
|
|
135
|
+
sink?.("error");
|
|
136
|
+
return {
|
|
137
|
+
...state,
|
|
138
|
+
loading: true,
|
|
139
|
+
error: void 0
|
|
140
|
+
};
|
|
141
|
+
},
|
|
142
|
+
saveSucceeded: (state, action, sink) => {
|
|
143
|
+
sink?.("loading");
|
|
144
|
+
sink?.("error");
|
|
145
|
+
sink?.("items");
|
|
146
|
+
return {
|
|
147
|
+
...state,
|
|
148
|
+
loading: false,
|
|
149
|
+
error: void 0,
|
|
150
|
+
items: upsertByIdField(state.items, action.payload, idField)
|
|
151
|
+
};
|
|
152
|
+
},
|
|
153
|
+
saveFailed: (state, action, sink) => {
|
|
154
|
+
sink?.("loading");
|
|
155
|
+
sink?.("error");
|
|
156
|
+
return {
|
|
157
|
+
...state,
|
|
158
|
+
loading: false,
|
|
159
|
+
error: action.payload
|
|
160
|
+
};
|
|
161
|
+
},
|
|
162
|
+
remove: (state, _action, sink) => {
|
|
163
|
+
sink?.("loading");
|
|
164
|
+
sink?.("error");
|
|
165
|
+
return {
|
|
166
|
+
...state,
|
|
167
|
+
loading: true,
|
|
168
|
+
error: void 0
|
|
169
|
+
};
|
|
170
|
+
},
|
|
171
|
+
removeSucceeded: (state, action, sink) => {
|
|
172
|
+
sink?.("loading");
|
|
173
|
+
sink?.("error");
|
|
174
|
+
sink?.("items");
|
|
175
|
+
return {
|
|
176
|
+
...state,
|
|
177
|
+
loading: false,
|
|
178
|
+
error: void 0,
|
|
179
|
+
items: removeByIdField(state.items, action.payload, idField)
|
|
180
|
+
};
|
|
181
|
+
},
|
|
182
|
+
removeFailed: (state, action, sink) => {
|
|
183
|
+
sink?.("loading");
|
|
184
|
+
sink?.("error");
|
|
185
|
+
return {
|
|
186
|
+
...state,
|
|
187
|
+
loading: false,
|
|
188
|
+
error: action.payload
|
|
189
|
+
};
|
|
190
|
+
},
|
|
191
|
+
clearError: (state, _action, sink) => {
|
|
192
|
+
sink?.("error");
|
|
193
|
+
return {
|
|
194
|
+
...state,
|
|
195
|
+
error: void 0
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
};
|
|
199
|
+
const def = {
|
|
200
|
+
state: StateSchema,
|
|
201
|
+
actions: Actions,
|
|
202
|
+
reducers,
|
|
203
|
+
schemas: { entity: spec.entity },
|
|
204
|
+
meta: { kind: "crud", idField },
|
|
205
|
+
services
|
|
206
|
+
};
|
|
207
|
+
const module2 = extend ? Logix.Module.make(
|
|
208
|
+
id,
|
|
209
|
+
def,
|
|
210
|
+
extend
|
|
211
|
+
) : Logix.Module.make(id, def);
|
|
212
|
+
const install = module2.logic(
|
|
213
|
+
($) => import_effect.Effect.gen(function* () {
|
|
214
|
+
yield* $.onAction("query").runFork(
|
|
215
|
+
(action) => import_effect.Effect.gen(function* () {
|
|
216
|
+
const apiOpt = yield* import_effect.Effect.serviceOption(services.api);
|
|
217
|
+
if (import_effect.Option.isNone(apiOpt)) {
|
|
218
|
+
yield* $.dispatchers.queryFailed(
|
|
219
|
+
`[CRUDModule] Missing services.api; provide Layer.succeed(${id}.services.api, impl) via withLayer/withLayers/Runtime layer.`
|
|
220
|
+
);
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
const api = apiOpt.value;
|
|
224
|
+
const result = yield* api.list(action.payload);
|
|
225
|
+
yield* $.dispatchers.querySucceeded(result);
|
|
226
|
+
}).pipe(import_effect.Effect.catchAll((e) => $.dispatchers.queryFailed(toErrorMessage(e))))
|
|
227
|
+
);
|
|
228
|
+
yield* $.onAction("save").runFork(
|
|
229
|
+
(action) => import_effect.Effect.gen(function* () {
|
|
230
|
+
const apiOpt = yield* import_effect.Effect.serviceOption(services.api);
|
|
231
|
+
if (import_effect.Option.isNone(apiOpt)) {
|
|
232
|
+
yield* $.dispatchers.saveFailed(
|
|
233
|
+
`[CRUDModule] Missing services.api; provide Layer.succeed(${id}.services.api, impl) via withLayer/withLayers/Runtime layer.`
|
|
234
|
+
);
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
const api = apiOpt.value;
|
|
238
|
+
const entity = yield* api.save(action.payload);
|
|
239
|
+
yield* $.dispatchers.saveSucceeded(entity);
|
|
240
|
+
}).pipe(import_effect.Effect.catchAll((e) => $.dispatchers.saveFailed(toErrorMessage(e))))
|
|
241
|
+
);
|
|
242
|
+
yield* $.onAction("remove").runFork(
|
|
243
|
+
(action) => import_effect.Effect.gen(function* () {
|
|
244
|
+
const apiOpt = yield* import_effect.Effect.serviceOption(services.api);
|
|
245
|
+
if (import_effect.Option.isNone(apiOpt)) {
|
|
246
|
+
yield* $.dispatchers.removeFailed(
|
|
247
|
+
`[CRUDModule] Missing services.api; provide Layer.succeed(${id}.services.api, impl) via withLayer/withLayers/Runtime layer.`
|
|
248
|
+
);
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
const api = apiOpt.value;
|
|
252
|
+
yield* api.remove(action.payload);
|
|
253
|
+
yield* $.dispatchers.removeSucceeded(action.payload);
|
|
254
|
+
}).pipe(import_effect.Effect.catchAll((e) => $.dispatchers.removeFailed(toErrorMessage(e))))
|
|
255
|
+
);
|
|
256
|
+
}),
|
|
257
|
+
{ id: "install" }
|
|
258
|
+
);
|
|
259
|
+
const controller = {
|
|
260
|
+
make: (runtime) => ({
|
|
261
|
+
runtime,
|
|
262
|
+
getState: runtime.getState,
|
|
263
|
+
dispatch: runtime.dispatch,
|
|
264
|
+
controller: {
|
|
265
|
+
list: (input) => runtime.dispatch({ _tag: "query", payload: input }),
|
|
266
|
+
save: (entity) => runtime.dispatch({ _tag: "save", payload: entity }),
|
|
267
|
+
remove: (id2) => runtime.dispatch({ _tag: "remove", payload: id2 }),
|
|
268
|
+
clearError: () => runtime.dispatch({ _tag: "clearError" }),
|
|
269
|
+
idField
|
|
270
|
+
}
|
|
271
|
+
})
|
|
272
|
+
};
|
|
273
|
+
const EXTEND_HANDLE = /* @__PURE__ */ Symbol.for("logix.module.handle.extend");
|
|
274
|
+
module2.tag[EXTEND_HANDLE] = (runtime, base) => {
|
|
275
|
+
const crud = controller.make(runtime);
|
|
276
|
+
return {
|
|
277
|
+
...base,
|
|
278
|
+
controller: crud.controller,
|
|
279
|
+
services
|
|
280
|
+
};
|
|
281
|
+
};
|
|
282
|
+
return module2.implement({
|
|
283
|
+
initial: {
|
|
284
|
+
items: Array.from(spec.initial ?? []),
|
|
285
|
+
loading: false,
|
|
286
|
+
error: void 0,
|
|
287
|
+
lastQuery: void 0,
|
|
288
|
+
total: void 0
|
|
289
|
+
},
|
|
290
|
+
logics: [install]
|
|
291
|
+
});
|
|
292
|
+
};
|
|
293
|
+
var CRUDModule = Logix.Module.Manage.make({
|
|
294
|
+
kind: "crud",
|
|
295
|
+
define: defineCrud
|
|
296
|
+
});
|
|
297
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
298
|
+
0 && (module.exports = {
|
|
299
|
+
CRUDModule
|
|
300
|
+
});
|
|
301
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/internal/crud/Crud.ts"],"sourcesContent":["export * from './Crud.js'\nexport type * from './Crud.js'\n","import * as Logix from '@logixjs/core'\nimport { Context, Effect, Option, Schema } from 'effect'\n\nconst DefaultQueryInputSchema = Schema.Struct({\n pageSize: Schema.Number,\n})\n\nexport type CrudDefaultQueryInput = Schema.Schema.Type<typeof DefaultQueryInputSchema>\n\nexport interface CrudQueryResult<Entity> {\n readonly items: ReadonlyArray<Entity>\n readonly total?: number\n}\n\nexport interface CrudApi<Entity extends object, QueryInput, Id> {\n readonly list: (input: QueryInput) => Effect.Effect<CrudQueryResult<Entity>, unknown, never>\n readonly save: (entity: Entity) => Effect.Effect<Entity, unknown, never>\n readonly remove: (id: Id) => Effect.Effect<void, unknown, never>\n}\n\nexport interface CrudSpec<Entity extends object, QueryInput = CrudDefaultQueryInput, Id = string> {\n readonly entity: Schema.Schema<Entity>\n readonly query?: Schema.Schema<QueryInput>\n readonly id?: Schema.Schema<Id>\n readonly initial?: ReadonlyArray<Entity>\n /**\n * idField:\n * - The default primary key field for upsert/remove in reducers (default: \"id\").\n * - For more complex primary-key strategies, prefer handling results in a custom upper-layer Logic and writing back to state.\n */\n readonly idField?: string\n}\n\nexport type CrudState<Entity, QueryInput> = {\n readonly items: ReadonlyArray<Entity>\n readonly loading: boolean\n readonly error: string | undefined\n readonly lastQuery: QueryInput | undefined\n readonly total: number | undefined\n}\n\nexport type CrudActionMap<\n Entity extends object,\n QueryInput,\n Id,\n ExtraActions extends Record<string, Logix.AnySchema> = {},\n> = {\n readonly query: Schema.Schema<QueryInput>\n readonly querySucceeded: Schema.Schema<CrudQueryResult<Entity>>\n readonly queryFailed: Schema.Schema<string>\n\n readonly save: Schema.Schema<Entity>\n readonly saveSucceeded: Schema.Schema<Entity>\n readonly saveFailed: Schema.Schema<string>\n\n readonly remove: Schema.Schema<Id>\n readonly removeSucceeded: Schema.Schema<Id>\n readonly removeFailed: Schema.Schema<string>\n\n readonly clearError: Schema.Schema<void>\n} & ExtraActions\n\nexport type CrudAction<\n Entity extends object,\n QueryInput,\n Id,\n ExtraActions extends Record<string, Logix.AnySchema> = {},\n> = Logix.ActionsFromMap<CrudActionMap<Entity, QueryInput, Id, ExtraActions>>\n\nexport type CrudShape<\n Entity extends object,\n QueryInput,\n Id,\n ExtraActions extends Record<string, Logix.AnySchema> = {},\n> = Logix.Shape<Schema.Schema<CrudState<Entity, QueryInput>>, CrudActionMap<Entity, QueryInput, Id, ExtraActions>>\n\nexport type CrudServices<Entity extends object, QueryInput, Id> = {\n readonly api: Logix.State.Tag<CrudApi<Entity, QueryInput, Id>>\n}\n\nexport type CrudHandleExt<\n Entity extends object,\n QueryInput,\n Id,\n ExtraActions extends Record<string, Logix.AnySchema> = {},\n> = {\n readonly controller: CrudController<Entity, QueryInput, Id, ExtraActions>['controller']\n readonly services: CrudServices<Entity, QueryInput, Id>\n}\n\nexport interface CrudController<\n Entity extends object,\n QueryInput,\n Id,\n ExtraActions extends Record<string, Logix.AnySchema> = {},\n> {\n readonly runtime: Logix.ModuleRuntime<CrudState<Entity, QueryInput>, CrudAction<Entity, QueryInput, Id, ExtraActions>>\n readonly getState: Effect.Effect<CrudState<Entity, QueryInput>>\n readonly dispatch: (action: CrudAction<Entity, QueryInput, Id, ExtraActions>) => Effect.Effect<void>\n readonly controller: {\n readonly list: (input: QueryInput) => Effect.Effect<void>\n readonly save: (entity: Entity) => Effect.Effect<void>\n readonly remove: (id: Id) => Effect.Effect<void>\n readonly clearError: () => Effect.Effect<void>\n readonly idField: string\n }\n}\n\nconst makeActions = <Entity extends object, QueryInput, Id>(\n entity: Schema.Schema<Entity>,\n query: Schema.Schema<QueryInput>,\n id: Schema.Schema<Id>,\n): CrudActionMap<Entity, QueryInput, Id> =>\n ({\n query,\n querySucceeded: Schema.Struct({\n items: Schema.Array(entity),\n total: Schema.optional(Schema.Number),\n }) as Schema.Schema<CrudQueryResult<Entity>>,\n queryFailed: Schema.String,\n\n save: entity,\n saveSucceeded: entity,\n saveFailed: Schema.String,\n\n remove: id,\n removeSucceeded: id,\n removeFailed: Schema.String,\n\n clearError: Schema.Void,\n }) satisfies CrudActionMap<Entity, QueryInput, Id>\n\nconst toErrorMessage = (error: unknown): string => {\n if (error === null || error === undefined) return 'unknown error'\n if (typeof error === 'string') return error\n if (error instanceof Error && typeof error.message === 'string' && error.message.length > 0) {\n return error.message\n }\n if (typeof error === 'object') {\n if ('message' in error) {\n const message = (error as { readonly message?: unknown }).message\n if (typeof message === 'string' && message.length > 0) return message\n }\n try {\n const json = JSON.stringify(error)\n if (typeof json === 'string' && json.length > 0) return json\n } catch {\n // ignore\n }\n }\n return String(error)\n}\n\nconst upsertByIdField = <Entity extends object>(\n items: ReadonlyArray<Entity>,\n entity: Entity,\n idField: string,\n): ReadonlyArray<Entity> => {\n const id = (entity as Record<string, unknown>)[idField]\n const idx = items.findIndex((x) => (x as Record<string, unknown>)[idField] === id)\n if (idx < 0) return [...items, entity]\n return items.map((x, i) => (i === idx ? entity : x))\n}\n\nconst removeByIdField = <Entity extends object>(\n items: ReadonlyArray<Entity>,\n id: unknown,\n idField: string,\n): ReadonlyArray<Entity> => items.filter((x) => (x as Record<string, unknown>)[idField] !== id)\n\nexport type CrudModule<\n Id extends string,\n Entity extends object,\n QueryInput,\n EntityId,\n ExtraActions extends Record<string, Logix.AnySchema> = {},\n> = Logix.Module.Module<\n Id,\n CrudShape<Entity, QueryInput, EntityId, ExtraActions>,\n CrudHandleExt<Entity, QueryInput, EntityId, ExtraActions>,\n unknown\n> & {\n readonly _kind: 'Module'\n readonly services: CrudServices<Entity, QueryInput, EntityId>\n}\n\nconst defineCrud = <\n Id extends string,\n Entity extends object,\n QueryInput = CrudDefaultQueryInput,\n EntityId = string,\n ExtraActions extends Record<string, Logix.AnySchema> = {},\n>(\n id: Id,\n spec: CrudSpec<Entity, QueryInput, EntityId>,\n extend?: Logix.Module.MakeExtendDef<\n Schema.Schema<CrudState<Entity, QueryInput>>,\n CrudActionMap<Entity, QueryInput, EntityId>,\n ExtraActions\n >,\n): CrudModule<Id, Entity, QueryInput, EntityId, ExtraActions> => {\n const QuerySchema = (spec.query ?? DefaultQueryInputSchema) as Schema.Schema<QueryInput>\n const IdSchema = (spec.id ?? Schema.String) as Schema.Schema<EntityId>\n\n const idField = spec.idField ?? 'id'\n\n class Api extends Context.Tag(`${id}/crud/api`)<Api, CrudApi<Entity, QueryInput, EntityId>>() {}\n\n const services = { api: Api } as const satisfies CrudServices<Entity, QueryInput, EntityId>\n\n const Actions = makeActions(spec.entity, QuerySchema, IdSchema)\n\n const StateSchema = Schema.Struct({\n items: Schema.Array(spec.entity),\n loading: Schema.Boolean,\n error: Schema.UndefinedOr(Schema.String),\n lastQuery: Schema.UndefinedOr(QuerySchema),\n total: Schema.UndefinedOr(Schema.Number),\n })\n\n type Reducers = Logix.ReducersFromMap<typeof StateSchema, CrudActionMap<Entity, QueryInput, EntityId>>\n\n const reducers: Reducers = {\n query: (state, action, sink) => {\n sink?.('loading')\n sink?.('error')\n sink?.('lastQuery')\n return {\n ...state,\n loading: true,\n error: undefined,\n lastQuery: action.payload,\n }\n },\n querySucceeded: (state, action, sink) => {\n sink?.('loading')\n sink?.('error')\n sink?.('items')\n sink?.('total')\n return {\n ...state,\n loading: false,\n error: undefined,\n items: action.payload.items,\n total: action.payload.total,\n }\n },\n queryFailed: (state, action, sink) => {\n sink?.('loading')\n sink?.('error')\n return {\n ...state,\n loading: false,\n error: action.payload,\n }\n },\n\n save: (state, _action, sink) => {\n sink?.('loading')\n sink?.('error')\n return {\n ...state,\n loading: true,\n error: undefined,\n }\n },\n saveSucceeded: (state, action, sink) => {\n sink?.('loading')\n sink?.('error')\n sink?.('items')\n return {\n ...state,\n loading: false,\n error: undefined,\n items: upsertByIdField(state.items, action.payload as Entity, idField),\n }\n },\n saveFailed: (state, action, sink) => {\n sink?.('loading')\n sink?.('error')\n return {\n ...state,\n loading: false,\n error: action.payload,\n }\n },\n\n remove: (state, _action, sink) => {\n sink?.('loading')\n sink?.('error')\n return {\n ...state,\n loading: true,\n error: undefined,\n }\n },\n removeSucceeded: (state, action, sink) => {\n sink?.('loading')\n sink?.('error')\n sink?.('items')\n return {\n ...state,\n loading: false,\n error: undefined,\n items: removeByIdField(state.items, action.payload, idField),\n }\n },\n removeFailed: (state, action, sink) => {\n sink?.('loading')\n sink?.('error')\n return {\n ...state,\n loading: false,\n error: action.payload,\n }\n },\n\n clearError: (state, _action, sink) => {\n sink?.('error')\n return {\n ...state,\n error: undefined,\n }\n },\n }\n\n const def = {\n state: StateSchema,\n actions: Actions,\n reducers,\n schemas: { entity: spec.entity },\n meta: { kind: 'crud', idField },\n services,\n }\n\n const module = extend\n ? Logix.Module.make<\n Id,\n typeof StateSchema,\n CrudActionMap<Entity, QueryInput, EntityId>,\n CrudHandleExt<Entity, QueryInput, EntityId, ExtraActions>\n >(\n id,\n def,\n extend as unknown as Logix.Module.MakeExtendDef<\n typeof StateSchema,\n CrudActionMap<Entity, QueryInput, EntityId>,\n ExtraActions\n >,\n )\n : Logix.Module.make<\n Id,\n typeof StateSchema,\n CrudActionMap<Entity, QueryInput, EntityId>,\n CrudHandleExt<Entity, QueryInput, EntityId, ExtraActions>\n >(id, def)\n\n const install = module.logic(\n ($) =>\n Effect.gen(function* () {\n yield* $.onAction('query').runFork((action) =>\n Effect.gen(function* () {\n const apiOpt = yield* Effect.serviceOption(services.api)\n if (Option.isNone(apiOpt)) {\n yield* $.dispatchers.queryFailed(\n `[CRUDModule] Missing services.api; provide Layer.succeed(${id}.services.api, impl) via withLayer/withLayers/Runtime layer.`,\n )\n return\n }\n const api = apiOpt.value\n\n const result = yield* api.list(action.payload as QueryInput)\n yield* $.dispatchers.querySucceeded(result)\n }).pipe(Effect.catchAll((e) => $.dispatchers.queryFailed(toErrorMessage(e)))),\n )\n\n yield* $.onAction('save').runFork((action) =>\n Effect.gen(function* () {\n const apiOpt = yield* Effect.serviceOption(services.api)\n if (Option.isNone(apiOpt)) {\n yield* $.dispatchers.saveFailed(\n `[CRUDModule] Missing services.api; provide Layer.succeed(${id}.services.api, impl) via withLayer/withLayers/Runtime layer.`,\n )\n return\n }\n const api = apiOpt.value\n\n const entity = yield* api.save(action.payload as Entity)\n yield* $.dispatchers.saveSucceeded(entity)\n }).pipe(Effect.catchAll((e) => $.dispatchers.saveFailed(toErrorMessage(e)))),\n )\n\n yield* $.onAction('remove').runFork((action) =>\n Effect.gen(function* () {\n const apiOpt = yield* Effect.serviceOption(services.api)\n if (Option.isNone(apiOpt)) {\n yield* $.dispatchers.removeFailed(\n `[CRUDModule] Missing services.api; provide Layer.succeed(${id}.services.api, impl) via withLayer/withLayers/Runtime layer.`,\n )\n return\n }\n const api = apiOpt.value\n\n yield* api.remove(action.payload as EntityId)\n yield* $.dispatchers.removeSucceeded(action.payload as EntityId)\n }).pipe(Effect.catchAll((e) => $.dispatchers.removeFailed(toErrorMessage(e)))),\n )\n }),\n { id: 'install' },\n )\n\n const controller = {\n make: (\n runtime: Logix.ModuleRuntime<\n CrudState<Entity, QueryInput>,\n CrudAction<Entity, QueryInput, EntityId, ExtraActions>\n >,\n ): CrudController<Entity, QueryInput, EntityId, ExtraActions> => ({\n runtime,\n getState: runtime.getState,\n dispatch: runtime.dispatch,\n controller: {\n list: (input: QueryInput) =>\n runtime.dispatch({ _tag: 'query', payload: input } as CrudAction<Entity, QueryInput, EntityId, ExtraActions>),\n save: (entity: Entity) =>\n runtime.dispatch({ _tag: 'save', payload: entity } as CrudAction<Entity, QueryInput, EntityId, ExtraActions>),\n remove: (id: EntityId) =>\n runtime.dispatch({ _tag: 'remove', payload: id } as CrudAction<Entity, QueryInput, EntityId, ExtraActions>),\n clearError: () =>\n runtime.dispatch({ _tag: 'clearError' } as CrudAction<Entity, QueryInput, EntityId, ExtraActions>),\n idField,\n },\n }),\n }\n\n const EXTEND_HANDLE = Symbol.for('logix.module.handle.extend')\n ;(module.tag as unknown as Record<PropertyKey, unknown>)[EXTEND_HANDLE] = (\n runtime: Logix.ModuleRuntime<CrudState<Entity, QueryInput>, CrudAction<Entity, QueryInput, EntityId, ExtraActions>>,\n base: Logix.ModuleHandle<Logix.AnyModuleShape>,\n ) => {\n const crud = controller.make(runtime)\n return {\n ...base,\n controller: crud.controller,\n services,\n }\n }\n\n return module.implement({\n initial: {\n items: Array.from(spec.initial ?? []),\n loading: false,\n error: undefined,\n lastQuery: undefined,\n total: undefined,\n } as CrudState<Entity, QueryInput>,\n logics: [install],\n }) as unknown as CrudModule<Id, Entity, QueryInput, EntityId, ExtraActions>\n}\n\nexport const CRUDModule = Logix.Module.Manage.make({\n kind: 'crud',\n define: defineCrud,\n})\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,YAAuB;AACvB,oBAAgD;AAEhD,IAAM,0BAA0B,qBAAO,OAAO;AAAA,EAC5C,UAAU,qBAAO;AACnB,CAAC;AAuGD,IAAM,cAAc,CAClB,QACA,OACA,QAEC;AAAA,EACC;AAAA,EACA,gBAAgB,qBAAO,OAAO;AAAA,IAC5B,OAAO,qBAAO,MAAM,MAAM;AAAA,IAC1B,OAAO,qBAAO,SAAS,qBAAO,MAAM;AAAA,EACtC,CAAC;AAAA,EACD,aAAa,qBAAO;AAAA,EAEpB,MAAM;AAAA,EACN,eAAe;AAAA,EACf,YAAY,qBAAO;AAAA,EAEnB,QAAQ;AAAA,EACR,iBAAiB;AAAA,EACjB,cAAc,qBAAO;AAAA,EAErB,YAAY,qBAAO;AACrB;AAEF,IAAM,iBAAiB,CAAC,UAA2B;AACjD,MAAI,UAAU,QAAQ,UAAU,OAAW,QAAO;AAClD,MAAI,OAAO,UAAU,SAAU,QAAO;AACtC,MAAI,iBAAiB,SAAS,OAAO,MAAM,YAAY,YAAY,MAAM,QAAQ,SAAS,GAAG;AAC3F,WAAO,MAAM;AAAA,EACf;AACA,MAAI,OAAO,UAAU,UAAU;AAC7B,QAAI,aAAa,OAAO;AACtB,YAAM,UAAW,MAAyC;AAC1D,UAAI,OAAO,YAAY,YAAY,QAAQ,SAAS,EAAG,QAAO;AAAA,IAChE;AACA,QAAI;AACF,YAAM,OAAO,KAAK,UAAU,KAAK;AACjC,UAAI,OAAO,SAAS,YAAY,KAAK,SAAS,EAAG,QAAO;AAAA,IAC1D,QAAQ;AAAA,IAER;AAAA,EACF;AACA,SAAO,OAAO,KAAK;AACrB;AAEA,IAAM,kBAAkB,CACtB,OACA,QACA,YAC0B;AAC1B,QAAM,KAAM,OAAmC,OAAO;AACtD,QAAM,MAAM,MAAM,UAAU,CAAC,MAAO,EAA8B,OAAO,MAAM,EAAE;AACjF,MAAI,MAAM,EAAG,QAAO,CAAC,GAAG,OAAO,MAAM;AACrC,SAAO,MAAM,IAAI,CAAC,GAAG,MAAO,MAAM,MAAM,SAAS,CAAE;AACrD;AAEA,IAAM,kBAAkB,CACtB,OACA,IACA,YAC0B,MAAM,OAAO,CAAC,MAAO,EAA8B,OAAO,MAAM,EAAE;AAkB9F,IAAM,aAAa,CAOjB,IACA,MACA,WAK+D;AAC/D,QAAM,cAAe,KAAK,SAAS;AACnC,QAAM,WAAY,KAAK,MAAM,qBAAO;AAEpC,QAAM,UAAU,KAAK,WAAW;AAAA,EAEhC,MAAM,YAAY,sBAAQ,IAAI,GAAG,EAAE,WAAW,EAA8C,EAAE;AAAA,EAAC;AAE/F,QAAM,WAAW,EAAE,KAAK,IAAI;AAE5B,QAAM,UAAU,YAAY,KAAK,QAAQ,aAAa,QAAQ;AAE9D,QAAM,cAAc,qBAAO,OAAO;AAAA,IAChC,OAAO,qBAAO,MAAM,KAAK,MAAM;AAAA,IAC/B,SAAS,qBAAO;AAAA,IAChB,OAAO,qBAAO,YAAY,qBAAO,MAAM;AAAA,IACvC,WAAW,qBAAO,YAAY,WAAW;AAAA,IACzC,OAAO,qBAAO,YAAY,qBAAO,MAAM;AAAA,EACzC,CAAC;AAID,QAAM,WAAqB;AAAA,IACzB,OAAO,CAAC,OAAO,QAAQ,SAAS;AAC9B,aAAO,SAAS;AAChB,aAAO,OAAO;AACd,aAAO,WAAW;AAClB,aAAO;AAAA,QACL,GAAG;AAAA,QACH,SAAS;AAAA,QACT,OAAO;AAAA,QACP,WAAW,OAAO;AAAA,MACpB;AAAA,IACF;AAAA,IACA,gBAAgB,CAAC,OAAO,QAAQ,SAAS;AACvC,aAAO,SAAS;AAChB,aAAO,OAAO;AACd,aAAO,OAAO;AACd,aAAO,OAAO;AACd,aAAO;AAAA,QACL,GAAG;AAAA,QACH,SAAS;AAAA,QACT,OAAO;AAAA,QACP,OAAO,OAAO,QAAQ;AAAA,QACtB,OAAO,OAAO,QAAQ;AAAA,MACxB;AAAA,IACF;AAAA,IACA,aAAa,CAAC,OAAO,QAAQ,SAAS;AACpC,aAAO,SAAS;AAChB,aAAO,OAAO;AACd,aAAO;AAAA,QACL,GAAG;AAAA,QACH,SAAS;AAAA,QACT,OAAO,OAAO;AAAA,MAChB;AAAA,IACF;AAAA,IAEA,MAAM,CAAC,OAAO,SAAS,SAAS;AAC9B,aAAO,SAAS;AAChB,aAAO,OAAO;AACd,aAAO;AAAA,QACL,GAAG;AAAA,QACH,SAAS;AAAA,QACT,OAAO;AAAA,MACT;AAAA,IACF;AAAA,IACA,eAAe,CAAC,OAAO,QAAQ,SAAS;AACtC,aAAO,SAAS;AAChB,aAAO,OAAO;AACd,aAAO,OAAO;AACd,aAAO;AAAA,QACL,GAAG;AAAA,QACH,SAAS;AAAA,QACT,OAAO;AAAA,QACP,OAAO,gBAAgB,MAAM,OAAO,OAAO,SAAmB,OAAO;AAAA,MACvE;AAAA,IACF;AAAA,IACA,YAAY,CAAC,OAAO,QAAQ,SAAS;AACnC,aAAO,SAAS;AAChB,aAAO,OAAO;AACd,aAAO;AAAA,QACL,GAAG;AAAA,QACH,SAAS;AAAA,QACT,OAAO,OAAO;AAAA,MAChB;AAAA,IACF;AAAA,IAEA,QAAQ,CAAC,OAAO,SAAS,SAAS;AAChC,aAAO,SAAS;AAChB,aAAO,OAAO;AACd,aAAO;AAAA,QACL,GAAG;AAAA,QACH,SAAS;AAAA,QACT,OAAO;AAAA,MACT;AAAA,IACF;AAAA,IACA,iBAAiB,CAAC,OAAO,QAAQ,SAAS;AACxC,aAAO,SAAS;AAChB,aAAO,OAAO;AACd,aAAO,OAAO;AACd,aAAO;AAAA,QACL,GAAG;AAAA,QACH,SAAS;AAAA,QACT,OAAO;AAAA,QACP,OAAO,gBAAgB,MAAM,OAAO,OAAO,SAAS,OAAO;AAAA,MAC7D;AAAA,IACF;AAAA,IACA,cAAc,CAAC,OAAO,QAAQ,SAAS;AACrC,aAAO,SAAS;AAChB,aAAO,OAAO;AACd,aAAO;AAAA,QACL,GAAG;AAAA,QACH,SAAS;AAAA,QACT,OAAO,OAAO;AAAA,MAChB;AAAA,IACF;AAAA,IAEA,YAAY,CAAC,OAAO,SAAS,SAAS;AACpC,aAAO,OAAO;AACd,aAAO;AAAA,QACL,GAAG;AAAA,QACH,OAAO;AAAA,MACT;AAAA,IACF;AAAA,EACF;AAEA,QAAM,MAAM;AAAA,IACV,OAAO;AAAA,IACP,SAAS;AAAA,IACT;AAAA,IACA,SAAS,EAAE,QAAQ,KAAK,OAAO;AAAA,IAC/B,MAAM,EAAE,MAAM,QAAQ,QAAQ;AAAA,IAC9B;AAAA,EACF;AAEA,QAAMA,UAAS,SACL,aAAO;AAAA,IAMX;AAAA,IACA;AAAA,IACA;AAAA,EAKF,IACM,aAAO,KAKX,IAAI,GAAG;AAEb,QAAM,UAAUA,QAAO;AAAA,IACrB,CAAC,MACC,qBAAO,IAAI,aAAa;AACtB,aAAO,EAAE,SAAS,OAAO,EAAE;AAAA,QAAQ,CAAC,WAClC,qBAAO,IAAI,aAAa;AACtB,gBAAM,SAAS,OAAO,qBAAO,cAAc,SAAS,GAAG;AACvD,cAAI,qBAAO,OAAO,MAAM,GAAG;AACzB,mBAAO,EAAE,YAAY;AAAA,cACnB,4DAA4D,EAAE;AAAA,YAChE;AACA;AAAA,UACF;AACA,gBAAM,MAAM,OAAO;AAEnB,gBAAM,SAAS,OAAO,IAAI,KAAK,OAAO,OAAqB;AAC3D,iBAAO,EAAE,YAAY,eAAe,MAAM;AAAA,QAC5C,CAAC,EAAE,KAAK,qBAAO,SAAS,CAAC,MAAM,EAAE,YAAY,YAAY,eAAe,CAAC,CAAC,CAAC,CAAC;AAAA,MAC9E;AAEA,aAAO,EAAE,SAAS,MAAM,EAAE;AAAA,QAAQ,CAAC,WACjC,qBAAO,IAAI,aAAa;AACtB,gBAAM,SAAS,OAAO,qBAAO,cAAc,SAAS,GAAG;AACvD,cAAI,qBAAO,OAAO,MAAM,GAAG;AACzB,mBAAO,EAAE,YAAY;AAAA,cACnB,4DAA4D,EAAE;AAAA,YAChE;AACA;AAAA,UACF;AACA,gBAAM,MAAM,OAAO;AAEnB,gBAAM,SAAS,OAAO,IAAI,KAAK,OAAO,OAAiB;AACvD,iBAAO,EAAE,YAAY,cAAc,MAAM;AAAA,QAC3C,CAAC,EAAE,KAAK,qBAAO,SAAS,CAAC,MAAM,EAAE,YAAY,WAAW,eAAe,CAAC,CAAC,CAAC,CAAC;AAAA,MAC7E;AAEA,aAAO,EAAE,SAAS,QAAQ,EAAE;AAAA,QAAQ,CAAC,WACnC,qBAAO,IAAI,aAAa;AACtB,gBAAM,SAAS,OAAO,qBAAO,cAAc,SAAS,GAAG;AACvD,cAAI,qBAAO,OAAO,MAAM,GAAG;AACzB,mBAAO,EAAE,YAAY;AAAA,cACnB,4DAA4D,EAAE;AAAA,YAChE;AACA;AAAA,UACF;AACA,gBAAM,MAAM,OAAO;AAEnB,iBAAO,IAAI,OAAO,OAAO,OAAmB;AAC5C,iBAAO,EAAE,YAAY,gBAAgB,OAAO,OAAmB;AAAA,QACjE,CAAC,EAAE,KAAK,qBAAO,SAAS,CAAC,MAAM,EAAE,YAAY,aAAa,eAAe,CAAC,CAAC,CAAC,CAAC;AAAA,MAC/E;AAAA,IACF,CAAC;AAAA,IACH,EAAE,IAAI,UAAU;AAAA,EAClB;AAEA,QAAM,aAAa;AAAA,IACjB,MAAM,CACJ,aAIgE;AAAA,MAChE;AAAA,MACA,UAAU,QAAQ;AAAA,MAClB,UAAU,QAAQ;AAAA,MAClB,YAAY;AAAA,QACV,MAAM,CAAC,UACL,QAAQ,SAAS,EAAE,MAAM,SAAS,SAAS,MAAM,CAA2D;AAAA,QAC9G,MAAM,CAAC,WACL,QAAQ,SAAS,EAAE,MAAM,QAAQ,SAAS,OAAO,CAA2D;AAAA,QAC9G,QAAQ,CAACC,QACP,QAAQ,SAAS,EAAE,MAAM,UAAU,SAASA,IAAG,CAA2D;AAAA,QAC5G,YAAY,MACV,QAAQ,SAAS,EAAE,MAAM,aAAa,CAA2D;AAAA,QACnG;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,QAAM,gBAAgB,uBAAO,IAAI,4BAA4B;AAC5D,EAACD,QAAO,IAAgD,aAAa,IAAI,CACxE,SACA,SACG;AACH,UAAM,OAAO,WAAW,KAAK,OAAO;AACpC,WAAO;AAAA,MACL,GAAG;AAAA,MACH,YAAY,KAAK;AAAA,MACjB;AAAA,IACF;AAAA,EACF;AAEA,SAAOA,QAAO,UAAU;AAAA,IACtB,SAAS;AAAA,MACP,OAAO,MAAM,KAAK,KAAK,WAAW,CAAC,CAAC;AAAA,MACpC,SAAS;AAAA,MACT,OAAO;AAAA,MACP,WAAW;AAAA,MACX,OAAO;AAAA,IACT;AAAA,IACA,QAAQ,CAAC,OAAO;AAAA,EAClB,CAAC;AACH;AAEO,IAAM,aAAmB,aAAO,OAAO,KAAK;AAAA,EACjD,MAAM;AAAA,EACN,QAAQ;AACV,CAAC;","names":["module","id"]}
|
package/dist/index.d.cts
ADDED
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
package/package.json
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@logixjs/domain",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Domain modules (based on Logix.Module.Manage) for Logix v3",
|
|
5
|
+
"files": [
|
|
6
|
+
"dist/**"
|
|
7
|
+
],
|
|
8
|
+
"type": "module",
|
|
9
|
+
"main": "./dist/index.cjs",
|
|
10
|
+
"module": "./dist/index.js",
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"exports": {
|
|
13
|
+
"./package.json": "./package.json",
|
|
14
|
+
".": {
|
|
15
|
+
"types": "./dist/index.d.ts",
|
|
16
|
+
"import": "./dist/index.js",
|
|
17
|
+
"require": "./dist/index.cjs"
|
|
18
|
+
},
|
|
19
|
+
"./*": {
|
|
20
|
+
"types": "./dist/*.d.ts",
|
|
21
|
+
"import": "./dist/*.js",
|
|
22
|
+
"require": "./dist/*.cjs"
|
|
23
|
+
},
|
|
24
|
+
"./internal/*": null
|
|
25
|
+
},
|
|
26
|
+
"publishConfig": {
|
|
27
|
+
"access": "public"
|
|
28
|
+
},
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"effect": "^3.19.8",
|
|
31
|
+
"@logixjs/core": "0.0.1"
|
|
32
|
+
},
|
|
33
|
+
"devDependencies": {
|
|
34
|
+
"@effect/vitest": "^0.27.0",
|
|
35
|
+
"tsup": "^8.0.0",
|
|
36
|
+
"typescript": "^5.8.2",
|
|
37
|
+
"vitest": "^4.0.15"
|
|
38
|
+
},
|
|
39
|
+
"peerDependencies": {
|
|
40
|
+
"effect": "^3.19.8",
|
|
41
|
+
"@logixjs/core": "0.0.1"
|
|
42
|
+
},
|
|
43
|
+
"scripts": {
|
|
44
|
+
"build": "tsup --format cjs,esm --dts",
|
|
45
|
+
"dev": "tsup --watch",
|
|
46
|
+
"demo:crud": "tsx demos/crud.business.ts",
|
|
47
|
+
"demo:crud:optimistic": "tsx demos/optimistic-crud/demo.ts",
|
|
48
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
49
|
+
"typecheck:test": "tsc -p tsconfig.test.json --noEmit",
|
|
50
|
+
"test": "vitest run",
|
|
51
|
+
"test:changed": "vitest run --changed",
|
|
52
|
+
"test:cache": "vitest run --cache",
|
|
53
|
+
"test:watch": "vitest"
|
|
54
|
+
}
|
|
55
|
+
}
|