@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 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":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;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"]}
@@ -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
@@ -0,0 +1,7 @@
1
+ import {
2
+ CRUDModule
3
+ } from "./chunk-SCBUT2ZQ.js";
4
+ export {
5
+ CRUDModule
6
+ };
7
+ //# sourceMappingURL=Crud.js.map
@@ -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":"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","names":["module","id"]}
@@ -0,0 +1,3 @@
1
+ export { CRUDModule, CrudAction, CrudActionMap, CrudApi, CrudController, CrudDefaultQueryInput, CrudHandleExt, CrudModule, CrudQueryResult, CrudServices, CrudShape, CrudSpec, CrudState } from './Crud.cjs';
2
+ import '@logixjs/core';
3
+ import 'effect';
@@ -0,0 +1,3 @@
1
+ export { CRUDModule, CrudAction, CrudActionMap, CrudApi, CrudController, CrudDefaultQueryInput, CrudHandleExt, CrudModule, CrudQueryResult, CrudServices, CrudShape, CrudSpec, CrudState } from './Crud.js';
2
+ import '@logixjs/core';
3
+ import 'effect';
package/dist/index.js ADDED
@@ -0,0 +1,7 @@
1
+ import {
2
+ CRUDModule
3
+ } from "./chunk-SCBUT2ZQ.js";
4
+ export {
5
+ CRUDModule
6
+ };
7
+ //# sourceMappingURL=index.js.map
@@ -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
+ }