simplentity 0.1.2 → 1.0.0-alpha.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/README.md CHANGED
@@ -16,28 +16,27 @@ npm install simplentity
16
16
  ## Usage
17
17
 
18
18
  ```typescript
19
- import {entity, number, string, boolean} from 'simplentity';
19
+ import {createEntity, number, string, boolean} from 'simplentity';
20
20
 
21
21
  // Define a user entity
22
- class User extends entity({
22
+ const userFactory = createEntity({
23
23
  id: string().defaultFn(() => randomUUID()), // randomUUID is a third-party library. Not included.
24
24
  name: string(),
25
25
  age: number().notRequired(),
26
26
  isActive: boolean().default(true),
27
- }) {
28
- activate(): void {
27
+ }, ({ set }) => ({
28
+ activate: () => {
29
29
  // You can get suggestions for the properties of the entity.
30
- this.set('isActive', true);
30
+ set('isActive', true)
31
+ },
32
+ deactivate: () => {
33
+ set('isActive', false)
31
34
  }
32
-
33
- deactivate(): void {
34
- this.set('isActive', false);
35
- }
36
- }
35
+ }))
37
36
 
38
37
  // Properties that have NotRequired or Default(Fn) are optional.
39
38
  // If a property has a default value, it is automatically assigned to the property when you create the entity instance.
40
- const user = new User({
39
+ const user = userFactory.create({
41
40
  name: 'John Doe',
42
41
  })
43
42
  /*
@@ -14,29 +14,59 @@ type EntityPropInputResolver<T extends EntityConfig> = {
14
14
  } & {
15
15
  [K in keyof Omit<T, RequiredFieldKeys<T>>]?: FieldTypeResolver<T[K]>;
16
16
  };
17
+ type MethodDefinition = {
18
+ [key: string]: (...args: any[]) => any;
19
+ };
20
+ interface EntityInterface<C extends EntityConfig> {
21
+ get: <K extends keyof C>(key: K) => EntityConfigTypeResolver<C>[K];
22
+ toJSON: () => EntityConfigTypeResolver<C>;
23
+ }
24
+ declare class EntityFactory<C extends EntityConfig, D extends MethodDefinition> {
25
+ private readonly fields;
26
+ private readonly methodDefinitionFunction?;
27
+ readonly $infer: EntityInterface<C> & D;
28
+ constructor(fields: C, methodDefinitionFunction?: (params: {
29
+ set: <K extends keyof C>(key: K, value: EntityConfigTypeResolver<C>[K]) => void;
30
+ get: <K extends keyof C>(key: K) => EntityConfigTypeResolver<C>[K];
31
+ }) => D);
32
+ create(props: EntityPropInputResolver<C>): EntityInterface<C> & D;
33
+ }
17
34
  /**
18
- * Create an entity class with the given fields
19
- * @param fields
35
+ * Creates an entity factory function that allows defining fields and optional methods for an entity.
36
+ * The returned factory provides a `create` method to instantiate entities with the specified fields
37
+ * and methods, while also supporting default values and runtime property manipulation.
38
+ *
39
+ * @template C - The configuration type for the entity fields.
40
+ * @template D - The type of the methods defined for the entity.
41
+ *
42
+ * @param fields - An object defining the fields of the entity. Each field should include its configuration
43
+ * and a method to retrieve its default value.
44
+ * @param methodDefinitionFunction - An optional function that defines additional methods for the entity.
45
+ * It receives an object with `set` and `get` functions to manipulate
46
+ * the entity's properties.
47
+ *
48
+ * @returns An object with a `create` method. The `create` method accepts an input object to initialize
49
+ * the entity's properties and returns an entity instance with the defined fields, methods,
50
+ * and utility functions (`get`, `set`, `toJSON`).
51
+ *
52
+ * @example
53
+ * ```typescript
54
+ * const userFactory = createEntity({
55
+ * name: string(),
56
+ * age: number().default(18),
57
+ * isActive: boolean().default(true),
58
+ * }, ({ set, get }) => ({
59
+ * incrementAge: () => set('age', get('age') + 1),
60
+ * }));
61
+ *
62
+ * const user = userFactory.create({ name: 'John' });
63
+ * console.log(user.props); // { name: 'John', age: 18, isActive: true }
64
+ * user.incrementAge();
65
+ * console.log(user.props.age); // 19
66
+ * ```
20
67
  */
21
- export declare const entity: <Config extends EntityConfig>(fields: Config) => {
22
- new (props: EntityPropInputResolver<Config>): {
23
- readonly "__#1@#entityConfig": Config;
24
- "__#1@#props": EntityConfigTypeResolver<Config>;
25
- /**
26
- * Get the value of the field by key
27
- * @param key
28
- */
29
- get<K extends keyof Config>(key: K): EntityConfigTypeResolver<Config>[K];
30
- /**
31
- * Set the value of the field by key
32
- *
33
- * WARNING: This method should be called only from the methods of the entity.
34
- * Its accessor should be protected but TypeScript declaration does not allow protected methods in exported classes.
35
- * @param key
36
- * @param value
37
- */
38
- set<K extends keyof Config>(key: K, value: EntityConfigTypeResolver<Config>[K]): void;
39
- toJSON(): EntityConfigTypeResolver<Config>;
40
- };
41
- };
68
+ export declare function createEntity<C extends EntityConfig, D extends MethodDefinition>(fields: C, methodDefinitionFunction?: (params: {
69
+ set: <K extends keyof C>(key: K, value: EntityConfigTypeResolver<C>[K]) => void;
70
+ get: <K extends keyof C>(key: K) => EntityConfigTypeResolver<C>[K];
71
+ }) => D): EntityFactory<C, D>;
42
72
  export {};
@@ -24,7 +24,7 @@ type HasDefault<T extends ConfigurableFieldBase<unknown>> = T & {
24
24
  };
25
25
  export declare abstract class Field<T> implements ConfigurableFieldBase<T> {
26
26
  _: FieldConfig<T>;
27
- protected config: FieldRuntimeConfig<T>;
27
+ config: FieldRuntimeConfig<T>;
28
28
  constructor();
29
29
  notRequired(): NotRequired<this>;
30
30
  default(value: T): HasDefault<this>;
@@ -1,3 +1,3 @@
1
- import { entity } from "./entity.ts";
2
- import { boolean, date, number, string } from "./fields/index.ts";
3
- export { entity, string, number, boolean, date };
1
+ import { createEntity } from "./entity.ts";
2
+ import { boolean, date, number, string, entity } from "./fields/index.ts";
3
+ export { createEntity, string, number, boolean, date, entity };
package/dist/bun/index.js CHANGED
@@ -1,2 +1,2 @@
1
1
  // @bun
2
- class f{#u;#t;constructor(t,o){this.#u=o,Object.freeze(this.#u),this.#t=Object.entries(o).reduce((e,[n,a])=>{let r=t[n]??a.getDefaultValue();if(a.getConfig().hasDefault&&r===void 0)throw new Error(`The field "${n}" has a default value but undefined was provided.`);return e[n]=r,e},{})}get(t){return this.#t[t]}set(t,o){this.#t[t]=o}toJSON(){return this.#t}}var R=(t)=>{return class extends f{constructor(o){super(o,t)}}};class u{config;constructor(){this.config={notRequired:!1,hasDefault:!1,default:void 0}}notRequired(){return this.config.notRequired=!0,this}default(t){return this.config.default=t,this.config.hasDefault=!0,this}defaultFn(t){return this.config.defaultFn=t,this.config.hasDefault=!0,this}getConfig(){return this.config}getDefaultValue(){return this.config.default??this.config.defaultFn?.()}}class i extends u{}var s=()=>{return new i};class h extends u{}var T=()=>{return new h};class g extends u{}var m=()=>{return new g};class d extends u{}var l=()=>{return new d};export{l as string,m as number,R as entity,T as date,s as boolean};
2
+ class _{fields;methodDefinitionFunction;constructor(u,R){this.fields=u,this.methodDefinitionFunction=R}create(u){let R=Object.entries(this.fields).reduce((q,[x,C])=>{let D=u[x]??C.getDefaultValue();if(C.getConfig().hasDefault&&D===void 0)q[x]=C.getDefaultValue();else q[x]=D;return q},{}),W=(q,x)=>{R[q]=x},H=(q)=>{return R[q]},X=()=>{return R},Y=this.methodDefinitionFunction?.({set:W,get:H})??{};return{get:H,toJSON:X,...Y}}}function Z(u,R){return new _(u,R)}class T{config;constructor(){this.config={notRequired:!1,hasDefault:!1,default:void 0}}notRequired(){return this.config.notRequired=!0,this}default(u){return this.config.default=u,this.config.hasDefault=!0,this}defaultFn(u){return this.config.defaultFn=u,this.config.hasDefault=!0,this}getConfig(){return this.config}getDefaultValue(){return this.config.default??this.config.defaultFn?.()}}class b extends T{}var f=()=>{return new b};class B extends T{}var V=()=>{return new B};class G extends T{}var K=()=>{return new G};class L extends T{}var M=()=>{return new L};class Q extends T{}var U=()=>{return new Q};export{M as string,K as number,U as entity,V as date,Z as createEntity,f as boolean};
@@ -20,58 +20,50 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/entity.ts
21
21
  var entity_exports = {};
22
22
  __export(entity_exports, {
23
- entity: () => entity
23
+ createEntity: () => createEntity
24
24
  });
25
25
  module.exports = __toCommonJS(entity_exports);
26
- var Entity = class {
27
- #entityConfig;
28
- #props;
29
- constructor(props, entityConfig) {
30
- this.#entityConfig = entityConfig;
31
- Object.freeze(this.#entityConfig);
32
- this.#props = Object.entries(entityConfig).reduce(
26
+ var EntityFactory = class {
27
+ fields;
28
+ methodDefinitionFunction;
29
+ constructor(fields, methodDefinitionFunction) {
30
+ this.fields = fields;
31
+ this.methodDefinitionFunction = methodDefinitionFunction;
32
+ }
33
+ create(props) {
34
+ const assignedProps = Object.entries(this.fields).reduce(
33
35
  (acc, [key, field]) => {
34
36
  const value = props[key] ?? field.getDefaultValue();
35
37
  if (field.getConfig().hasDefault && value === void 0) {
36
- throw new Error(`The field "${key}" has a default value but undefined was provided.`);
38
+ acc[key] = field.getDefaultValue();
39
+ } else {
40
+ acc[key] = value;
37
41
  }
38
- acc[key] = value;
39
42
  return acc;
40
43
  },
41
44
  {}
42
45
  );
46
+ const set = (key, value) => {
47
+ assignedProps[key] = value;
48
+ };
49
+ const get = (key) => {
50
+ return assignedProps[key];
51
+ };
52
+ const toJSON = () => {
53
+ return assignedProps;
54
+ };
55
+ const methods = this.methodDefinitionFunction?.({ set, get }) ?? {};
56
+ return {
57
+ get,
58
+ toJSON,
59
+ ...methods
60
+ };
43
61
  }
44
- /**
45
- * Get the value of the field by key
46
- * @param key
47
- */
48
- get(key) {
49
- return this.#props[key];
50
- }
51
- /**
52
- * Set the value of the field by key
53
- *
54
- * WARNING: This method should be called only from the methods of the entity.
55
- * Its accessor should be protected but TypeScript declaration does not allow protected methods in exported classes.
56
- * @param key
57
- * @param value
58
- */
59
- set(key, value) {
60
- this.#props[key] = value;
61
- }
62
- // biome-ignore lint/style/useNamingConvention: toJSON is a name to be used in JSON.stringify
63
- toJSON() {
64
- return this.#props;
65
- }
66
- };
67
- var entity = (fields) => {
68
- return class extends Entity {
69
- constructor(props) {
70
- super(props, fields);
71
- }
72
- };
73
62
  };
63
+ function createEntity(fields, methodDefinitionFunction) {
64
+ return new EntityFactory(fields, methodDefinitionFunction);
65
+ }
74
66
  // Annotate the CommonJS export names for ESM import in node:
75
67
  0 && (module.exports = {
76
- entity
68
+ createEntity
77
69
  });
@@ -14,29 +14,59 @@ type EntityPropInputResolver<T extends EntityConfig> = {
14
14
  } & {
15
15
  [K in keyof Omit<T, RequiredFieldKeys<T>>]?: FieldTypeResolver<T[K]>;
16
16
  };
17
+ type MethodDefinition = {
18
+ [key: string]: (...args: any[]) => any;
19
+ };
20
+ interface EntityInterface<C extends EntityConfig> {
21
+ get: <K extends keyof C>(key: K) => EntityConfigTypeResolver<C>[K];
22
+ toJSON: () => EntityConfigTypeResolver<C>;
23
+ }
24
+ declare class EntityFactory<C extends EntityConfig, D extends MethodDefinition> {
25
+ private readonly fields;
26
+ private readonly methodDefinitionFunction?;
27
+ readonly $infer: EntityInterface<C> & D;
28
+ constructor(fields: C, methodDefinitionFunction?: (params: {
29
+ set: <K extends keyof C>(key: K, value: EntityConfigTypeResolver<C>[K]) => void;
30
+ get: <K extends keyof C>(key: K) => EntityConfigTypeResolver<C>[K];
31
+ }) => D);
32
+ create(props: EntityPropInputResolver<C>): EntityInterface<C> & D;
33
+ }
17
34
  /**
18
- * Create an entity class with the given fields
19
- * @param fields
35
+ * Creates an entity factory function that allows defining fields and optional methods for an entity.
36
+ * The returned factory provides a `create` method to instantiate entities with the specified fields
37
+ * and methods, while also supporting default values and runtime property manipulation.
38
+ *
39
+ * @template C - The configuration type for the entity fields.
40
+ * @template D - The type of the methods defined for the entity.
41
+ *
42
+ * @param fields - An object defining the fields of the entity. Each field should include its configuration
43
+ * and a method to retrieve its default value.
44
+ * @param methodDefinitionFunction - An optional function that defines additional methods for the entity.
45
+ * It receives an object with `set` and `get` functions to manipulate
46
+ * the entity's properties.
47
+ *
48
+ * @returns An object with a `create` method. The `create` method accepts an input object to initialize
49
+ * the entity's properties and returns an entity instance with the defined fields, methods,
50
+ * and utility functions (`get`, `set`, `toJSON`).
51
+ *
52
+ * @example
53
+ * ```typescript
54
+ * const userFactory = createEntity({
55
+ * name: string(),
56
+ * age: number().default(18),
57
+ * isActive: boolean().default(true),
58
+ * }, ({ set, get }) => ({
59
+ * incrementAge: () => set('age', get('age') + 1),
60
+ * }));
61
+ *
62
+ * const user = userFactory.create({ name: 'John' });
63
+ * console.log(user.props); // { name: 'John', age: 18, isActive: true }
64
+ * user.incrementAge();
65
+ * console.log(user.props.age); // 19
66
+ * ```
20
67
  */
21
- export declare const entity: <Config extends EntityConfig>(fields: Config) => {
22
- new (props: EntityPropInputResolver<Config>): {
23
- readonly "__#1@#entityConfig": Config;
24
- "__#1@#props": EntityConfigTypeResolver<Config>;
25
- /**
26
- * Get the value of the field by key
27
- * @param key
28
- */
29
- get<K extends keyof Config>(key: K): EntityConfigTypeResolver<Config>[K];
30
- /**
31
- * Set the value of the field by key
32
- *
33
- * WARNING: This method should be called only from the methods of the entity.
34
- * Its accessor should be protected but TypeScript declaration does not allow protected methods in exported classes.
35
- * @param key
36
- * @param value
37
- */
38
- set<K extends keyof Config>(key: K, value: EntityConfigTypeResolver<Config>[K]): void;
39
- toJSON(): EntityConfigTypeResolver<Config>;
40
- };
41
- };
68
+ export declare function createEntity<C extends EntityConfig, D extends MethodDefinition>(fields: C, methodDefinitionFunction?: (params: {
69
+ set: <K extends keyof C>(key: K, value: EntityConfigTypeResolver<C>[K]) => void;
70
+ get: <K extends keyof C>(key: K) => EntityConfigTypeResolver<C>[K];
71
+ }) => D): EntityFactory<C, D>;
42
72
  export {};
@@ -24,7 +24,7 @@ type HasDefault<T extends ConfigurableFieldBase<unknown>> = T & {
24
24
  };
25
25
  export declare abstract class Field<T> implements ConfigurableFieldBase<T> {
26
26
  _: FieldConfig<T>;
27
- protected config: FieldRuntimeConfig<T>;
27
+ config: FieldRuntimeConfig<T>;
28
28
  constructor();
29
29
  notRequired(): NotRequired<this>;
30
30
  default(value: T): HasDefault<this>;
@@ -0,0 +1,68 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/fields/entity.ts
21
+ var entity_exports = {};
22
+ __export(entity_exports, {
23
+ entity: () => entity
24
+ });
25
+ module.exports = __toCommonJS(entity_exports);
26
+
27
+ // src/field.ts
28
+ var Field = class {
29
+ config;
30
+ constructor() {
31
+ this.config = {
32
+ notRequired: false,
33
+ hasDefault: false,
34
+ default: void 0
35
+ };
36
+ }
37
+ notRequired() {
38
+ this.config.notRequired = true;
39
+ return this;
40
+ }
41
+ default(value) {
42
+ this.config.default = value;
43
+ this.config.hasDefault = true;
44
+ return this;
45
+ }
46
+ defaultFn(fn) {
47
+ this.config.defaultFn = fn;
48
+ this.config.hasDefault = true;
49
+ return this;
50
+ }
51
+ getConfig() {
52
+ return this.config;
53
+ }
54
+ getDefaultValue() {
55
+ return this.config.default ?? this.config.defaultFn?.();
56
+ }
57
+ };
58
+
59
+ // src/fields/entity.ts
60
+ var EntityField = class extends Field {
61
+ };
62
+ var entity = () => {
63
+ return new EntityField();
64
+ };
65
+ // Annotate the CommonJS export names for ESM import in node:
66
+ 0 && (module.exports = {
67
+ entity
68
+ });
@@ -22,6 +22,7 @@ var fields_exports = {};
22
22
  __export(fields_exports, {
23
23
  boolean: () => boolean,
24
24
  date: () => date,
25
+ entity: () => entity,
25
26
  number: () => number,
26
27
  string: () => string
27
28
  });
@@ -86,10 +87,18 @@ var StringField = class extends Field {
86
87
  var string = () => {
87
88
  return new StringField();
88
89
  };
90
+
91
+ // src/fields/entity.ts
92
+ var EntityField = class extends Field {
93
+ };
94
+ var entity = () => {
95
+ return new EntityField();
96
+ };
89
97
  // Annotate the CommonJS export names for ESM import in node:
90
98
  0 && (module.exports = {
91
99
  boolean,
92
100
  date,
101
+ entity,
93
102
  number,
94
103
  string
95
104
  });
@@ -21,6 +21,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
21
21
  var index_exports = {};
22
22
  __export(index_exports, {
23
23
  boolean: () => boolean,
24
+ createEntity: () => createEntity,
24
25
  date: () => date,
25
26
  entity: () => entity,
26
27
  number: () => number,
@@ -29,54 +30,46 @@ __export(index_exports, {
29
30
  module.exports = __toCommonJS(index_exports);
30
31
 
31
32
  // src/entity.ts
32
- var Entity = class {
33
- #entityConfig;
34
- #props;
35
- constructor(props, entityConfig) {
36
- this.#entityConfig = entityConfig;
37
- Object.freeze(this.#entityConfig);
38
- this.#props = Object.entries(entityConfig).reduce(
33
+ var EntityFactory = class {
34
+ fields;
35
+ methodDefinitionFunction;
36
+ constructor(fields, methodDefinitionFunction) {
37
+ this.fields = fields;
38
+ this.methodDefinitionFunction = methodDefinitionFunction;
39
+ }
40
+ create(props) {
41
+ const assignedProps = Object.entries(this.fields).reduce(
39
42
  (acc, [key, field]) => {
40
43
  const value = props[key] ?? field.getDefaultValue();
41
44
  if (field.getConfig().hasDefault && value === void 0) {
42
- throw new Error(`The field "${key}" has a default value but undefined was provided.`);
45
+ acc[key] = field.getDefaultValue();
46
+ } else {
47
+ acc[key] = value;
43
48
  }
44
- acc[key] = value;
45
49
  return acc;
46
50
  },
47
51
  {}
48
52
  );
53
+ const set = (key, value) => {
54
+ assignedProps[key] = value;
55
+ };
56
+ const get = (key) => {
57
+ return assignedProps[key];
58
+ };
59
+ const toJSON = () => {
60
+ return assignedProps;
61
+ };
62
+ const methods = this.methodDefinitionFunction?.({ set, get }) ?? {};
63
+ return {
64
+ get,
65
+ toJSON,
66
+ ...methods
67
+ };
49
68
  }
50
- /**
51
- * Get the value of the field by key
52
- * @param key
53
- */
54
- get(key) {
55
- return this.#props[key];
56
- }
57
- /**
58
- * Set the value of the field by key
59
- *
60
- * WARNING: This method should be called only from the methods of the entity.
61
- * Its accessor should be protected but TypeScript declaration does not allow protected methods in exported classes.
62
- * @param key
63
- * @param value
64
- */
65
- set(key, value) {
66
- this.#props[key] = value;
67
- }
68
- // biome-ignore lint/style/useNamingConvention: toJSON is a name to be used in JSON.stringify
69
- toJSON() {
70
- return this.#props;
71
- }
72
- };
73
- var entity = (fields) => {
74
- return class extends Entity {
75
- constructor(props) {
76
- super(props, fields);
77
- }
78
- };
79
69
  };
70
+ function createEntity(fields, methodDefinitionFunction) {
71
+ return new EntityFactory(fields, methodDefinitionFunction);
72
+ }
80
73
 
81
74
  // src/field.ts
82
75
  var Field = class {
@@ -137,9 +130,17 @@ var StringField = class extends Field {
137
130
  var string = () => {
138
131
  return new StringField();
139
132
  };
133
+
134
+ // src/fields/entity.ts
135
+ var EntityField = class extends Field {
136
+ };
137
+ var entity = () => {
138
+ return new EntityField();
139
+ };
140
140
  // Annotate the CommonJS export names for ESM import in node:
141
141
  0 && (module.exports = {
142
142
  boolean,
143
+ createEntity,
143
144
  date,
144
145
  entity,
145
146
  number,
@@ -1,3 +1,3 @@
1
- import { entity } from "./entity.ts";
2
- import { boolean, date, number, string } from "./fields/index.ts";
3
- export { entity, string, number, boolean, date };
1
+ import { createEntity } from "./entity.ts";
2
+ import { boolean, date, number, string, entity } from "./fields/index.ts";
3
+ export { createEntity, string, number, boolean, date, entity };
package/dist/entity.d.ts CHANGED
@@ -14,29 +14,59 @@ type EntityPropInputResolver<T extends EntityConfig> = {
14
14
  } & {
15
15
  [K in keyof Omit<T, RequiredFieldKeys<T>>]?: FieldTypeResolver<T[K]>;
16
16
  };
17
+ type MethodDefinition = {
18
+ [key: string]: (...args: any[]) => any;
19
+ };
20
+ interface EntityInterface<C extends EntityConfig> {
21
+ get: <K extends keyof C>(key: K) => EntityConfigTypeResolver<C>[K];
22
+ toJSON: () => EntityConfigTypeResolver<C>;
23
+ }
24
+ declare class EntityFactory<C extends EntityConfig, D extends MethodDefinition> {
25
+ private readonly fields;
26
+ private readonly methodDefinitionFunction?;
27
+ readonly $infer: EntityInterface<C> & D;
28
+ constructor(fields: C, methodDefinitionFunction?: (params: {
29
+ set: <K extends keyof C>(key: K, value: EntityConfigTypeResolver<C>[K]) => void;
30
+ get: <K extends keyof C>(key: K) => EntityConfigTypeResolver<C>[K];
31
+ }) => D);
32
+ create(props: EntityPropInputResolver<C>): EntityInterface<C> & D;
33
+ }
17
34
  /**
18
- * Create an entity class with the given fields
19
- * @param fields
35
+ * Creates an entity factory function that allows defining fields and optional methods for an entity.
36
+ * The returned factory provides a `create` method to instantiate entities with the specified fields
37
+ * and methods, while also supporting default values and runtime property manipulation.
38
+ *
39
+ * @template C - The configuration type for the entity fields.
40
+ * @template D - The type of the methods defined for the entity.
41
+ *
42
+ * @param fields - An object defining the fields of the entity. Each field should include its configuration
43
+ * and a method to retrieve its default value.
44
+ * @param methodDefinitionFunction - An optional function that defines additional methods for the entity.
45
+ * It receives an object with `set` and `get` functions to manipulate
46
+ * the entity's properties.
47
+ *
48
+ * @returns An object with a `create` method. The `create` method accepts an input object to initialize
49
+ * the entity's properties and returns an entity instance with the defined fields, methods,
50
+ * and utility functions (`get`, `set`, `toJSON`).
51
+ *
52
+ * @example
53
+ * ```typescript
54
+ * const userFactory = createEntity({
55
+ * name: string(),
56
+ * age: number().default(18),
57
+ * isActive: boolean().default(true),
58
+ * }, ({ set, get }) => ({
59
+ * incrementAge: () => set('age', get('age') + 1),
60
+ * }));
61
+ *
62
+ * const user = userFactory.create({ name: 'John' });
63
+ * console.log(user.props); // { name: 'John', age: 18, isActive: true }
64
+ * user.incrementAge();
65
+ * console.log(user.props.age); // 19
66
+ * ```
20
67
  */
21
- export declare const entity: <Config extends EntityConfig>(fields: Config) => {
22
- new (props: EntityPropInputResolver<Config>): {
23
- readonly "__#1@#entityConfig": Config;
24
- "__#1@#props": EntityConfigTypeResolver<Config>;
25
- /**
26
- * Get the value of the field by key
27
- * @param key
28
- */
29
- get<K extends keyof Config>(key: K): EntityConfigTypeResolver<Config>[K];
30
- /**
31
- * Set the value of the field by key
32
- *
33
- * WARNING: This method should be called only from the methods of the entity.
34
- * Its accessor should be protected but TypeScript declaration does not allow protected methods in exported classes.
35
- * @param key
36
- * @param value
37
- */
38
- set<K extends keyof Config>(key: K, value: EntityConfigTypeResolver<Config>[K]): void;
39
- toJSON(): EntityConfigTypeResolver<Config>;
40
- };
41
- };
68
+ export declare function createEntity<C extends EntityConfig, D extends MethodDefinition>(fields: C, methodDefinitionFunction?: (params: {
69
+ set: <K extends keyof C>(key: K, value: EntityConfigTypeResolver<C>[K]) => void;
70
+ get: <K extends keyof C>(key: K) => EntityConfigTypeResolver<C>[K];
71
+ }) => D): EntityFactory<C, D>;
42
72
  export {};
package/dist/entity.js CHANGED
@@ -1,52 +1,44 @@
1
1
  // src/entity.ts
2
- var Entity = class {
3
- #entityConfig;
4
- #props;
5
- constructor(props, entityConfig) {
6
- this.#entityConfig = entityConfig;
7
- Object.freeze(this.#entityConfig);
8
- this.#props = Object.entries(entityConfig).reduce(
2
+ var EntityFactory = class {
3
+ fields;
4
+ methodDefinitionFunction;
5
+ constructor(fields, methodDefinitionFunction) {
6
+ this.fields = fields;
7
+ this.methodDefinitionFunction = methodDefinitionFunction;
8
+ }
9
+ create(props) {
10
+ const assignedProps = Object.entries(this.fields).reduce(
9
11
  (acc, [key, field]) => {
10
12
  const value = props[key] ?? field.getDefaultValue();
11
13
  if (field.getConfig().hasDefault && value === void 0) {
12
- throw new Error(`The field "${key}" has a default value but undefined was provided.`);
14
+ acc[key] = field.getDefaultValue();
15
+ } else {
16
+ acc[key] = value;
13
17
  }
14
- acc[key] = value;
15
18
  return acc;
16
19
  },
17
20
  {}
18
21
  );
22
+ const set = (key, value) => {
23
+ assignedProps[key] = value;
24
+ };
25
+ const get = (key) => {
26
+ return assignedProps[key];
27
+ };
28
+ const toJSON = () => {
29
+ return assignedProps;
30
+ };
31
+ const methods = this.methodDefinitionFunction?.({ set, get }) ?? {};
32
+ return {
33
+ get,
34
+ toJSON,
35
+ ...methods
36
+ };
19
37
  }
20
- /**
21
- * Get the value of the field by key
22
- * @param key
23
- */
24
- get(key) {
25
- return this.#props[key];
26
- }
27
- /**
28
- * Set the value of the field by key
29
- *
30
- * WARNING: This method should be called only from the methods of the entity.
31
- * Its accessor should be protected but TypeScript declaration does not allow protected methods in exported classes.
32
- * @param key
33
- * @param value
34
- */
35
- set(key, value) {
36
- this.#props[key] = value;
37
- }
38
- // biome-ignore lint/style/useNamingConvention: toJSON is a name to be used in JSON.stringify
39
- toJSON() {
40
- return this.#props;
41
- }
42
- };
43
- var entity = (fields) => {
44
- return class extends Entity {
45
- constructor(props) {
46
- super(props, fields);
47
- }
48
- };
49
38
  };
39
+ function createEntity(fields, methodDefinitionFunction) {
40
+ return new EntityFactory(fields, methodDefinitionFunction);
41
+ }
50
42
  export {
51
- entity
43
+ createEntity
52
44
  };
package/dist/field.d.ts CHANGED
@@ -24,7 +24,7 @@ type HasDefault<T extends ConfigurableFieldBase<unknown>> = T & {
24
24
  };
25
25
  export declare abstract class Field<T> implements ConfigurableFieldBase<T> {
26
26
  _: FieldConfig<T>;
27
- protected config: FieldRuntimeConfig<T>;
27
+ config: FieldRuntimeConfig<T>;
28
28
  constructor();
29
29
  notRequired(): NotRequired<this>;
30
30
  default(value: T): HasDefault<this>;
@@ -0,0 +1,5 @@
1
+ import { Field } from "../field.ts";
2
+ declare class EntityField<T> extends Field<T> {
3
+ }
4
+ export declare const entity: <T>() => EntityField<T>;
5
+ export {};
@@ -0,0 +1,41 @@
1
+ // src/field.ts
2
+ var Field = class {
3
+ config;
4
+ constructor() {
5
+ this.config = {
6
+ notRequired: false,
7
+ hasDefault: false,
8
+ default: void 0
9
+ };
10
+ }
11
+ notRequired() {
12
+ this.config.notRequired = true;
13
+ return this;
14
+ }
15
+ default(value) {
16
+ this.config.default = value;
17
+ this.config.hasDefault = true;
18
+ return this;
19
+ }
20
+ defaultFn(fn) {
21
+ this.config.defaultFn = fn;
22
+ this.config.hasDefault = true;
23
+ return this;
24
+ }
25
+ getConfig() {
26
+ return this.config;
27
+ }
28
+ getDefaultValue() {
29
+ return this.config.default ?? this.config.defaultFn?.();
30
+ }
31
+ };
32
+
33
+ // src/fields/entity.ts
34
+ var EntityField = class extends Field {
35
+ };
36
+ var entity = () => {
37
+ return new EntityField();
38
+ };
39
+ export {
40
+ entity
41
+ };
@@ -2,4 +2,5 @@ import { boolean } from "./boolean.ts";
2
2
  import { date } from "./date.ts";
3
3
  import { number } from "./number.ts";
4
4
  import { string } from "./string.ts";
5
- export { string, number, boolean, date };
5
+ import { entity } from "./entity.ts";
6
+ export { string, number, boolean, date, entity };
@@ -57,9 +57,17 @@ var StringField = class extends Field {
57
57
  var string = () => {
58
58
  return new StringField();
59
59
  };
60
+
61
+ // src/fields/entity.ts
62
+ var EntityField = class extends Field {
63
+ };
64
+ var entity = () => {
65
+ return new EntityField();
66
+ };
60
67
  export {
61
68
  boolean,
62
69
  date,
70
+ entity,
63
71
  number,
64
72
  string
65
73
  };
package/dist/index.d.ts CHANGED
@@ -1,3 +1,3 @@
1
- import { entity } from "./entity.ts";
2
- import { boolean, date, number, string } from "./fields/index.ts";
3
- export { entity, string, number, boolean, date };
1
+ import { createEntity } from "./entity.ts";
2
+ import { boolean, date, number, string, entity } from "./fields/index.ts";
3
+ export { createEntity, string, number, boolean, date, entity };
package/dist/index.js CHANGED
@@ -1,52 +1,44 @@
1
1
  // src/entity.ts
2
- var Entity = class {
3
- #entityConfig;
4
- #props;
5
- constructor(props, entityConfig) {
6
- this.#entityConfig = entityConfig;
7
- Object.freeze(this.#entityConfig);
8
- this.#props = Object.entries(entityConfig).reduce(
2
+ var EntityFactory = class {
3
+ fields;
4
+ methodDefinitionFunction;
5
+ constructor(fields, methodDefinitionFunction) {
6
+ this.fields = fields;
7
+ this.methodDefinitionFunction = methodDefinitionFunction;
8
+ }
9
+ create(props) {
10
+ const assignedProps = Object.entries(this.fields).reduce(
9
11
  (acc, [key, field]) => {
10
12
  const value = props[key] ?? field.getDefaultValue();
11
13
  if (field.getConfig().hasDefault && value === void 0) {
12
- throw new Error(`The field "${key}" has a default value but undefined was provided.`);
14
+ acc[key] = field.getDefaultValue();
15
+ } else {
16
+ acc[key] = value;
13
17
  }
14
- acc[key] = value;
15
18
  return acc;
16
19
  },
17
20
  {}
18
21
  );
22
+ const set = (key, value) => {
23
+ assignedProps[key] = value;
24
+ };
25
+ const get = (key) => {
26
+ return assignedProps[key];
27
+ };
28
+ const toJSON = () => {
29
+ return assignedProps;
30
+ };
31
+ const methods = this.methodDefinitionFunction?.({ set, get }) ?? {};
32
+ return {
33
+ get,
34
+ toJSON,
35
+ ...methods
36
+ };
19
37
  }
20
- /**
21
- * Get the value of the field by key
22
- * @param key
23
- */
24
- get(key) {
25
- return this.#props[key];
26
- }
27
- /**
28
- * Set the value of the field by key
29
- *
30
- * WARNING: This method should be called only from the methods of the entity.
31
- * Its accessor should be protected but TypeScript declaration does not allow protected methods in exported classes.
32
- * @param key
33
- * @param value
34
- */
35
- set(key, value) {
36
- this.#props[key] = value;
37
- }
38
- // biome-ignore lint/style/useNamingConvention: toJSON is a name to be used in JSON.stringify
39
- toJSON() {
40
- return this.#props;
41
- }
42
- };
43
- var entity = (fields) => {
44
- return class extends Entity {
45
- constructor(props) {
46
- super(props, fields);
47
- }
48
- };
49
38
  };
39
+ function createEntity(fields, methodDefinitionFunction) {
40
+ return new EntityFactory(fields, methodDefinitionFunction);
41
+ }
50
42
 
51
43
  // src/field.ts
52
44
  var Field = class {
@@ -107,8 +99,16 @@ var StringField = class extends Field {
107
99
  var string = () => {
108
100
  return new StringField();
109
101
  };
102
+
103
+ // src/fields/entity.ts
104
+ var EntityField = class extends Field {
105
+ };
106
+ var entity = () => {
107
+ return new EntityField();
108
+ };
110
109
  export {
111
110
  boolean,
111
+ createEntity,
112
112
  date,
113
113
  entity,
114
114
  number,
package/package.json CHANGED
@@ -1,6 +1,7 @@
1
1
  {
2
2
  "name": "simplentity",
3
- "version": "0.1.2",
3
+ "version": "1.0.0-alpha.1",
4
+ "description": "A type-safe entity library for TypeScript.",
4
5
  "type": "module",
5
6
  "module": "./dist/index.js",
6
7
  "types": "./dist/index.d.ts",