akanjs 2.2.4-rc.3 → 2.2.4-rc.5

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.
@@ -6,7 +6,7 @@ export const getDefault = <T>(fieldObj: FieldObject): DefaultOf<T> => {
6
6
  const result: Record<string, unknown> = {};
7
7
  for (const [key, field] of Object.entries(fieldObj)) {
8
8
  if (field.fieldType === "hidden") result[key] = null;
9
- else if (field.default) {
9
+ else if (field.default !== undefined && field.default !== null) {
10
10
  if (typeof field.default === "function") result[key] = (field.default as () => object)();
11
11
  else result[key] = field.default as object;
12
12
  } else if (field.isArray) result[key] = [];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "akanjs",
3
- "version": "2.2.4-rc.3",
3
+ "version": "2.2.4-rc.5",
4
4
  "sourceType": "module",
5
5
  "type": "module",
6
6
  "publishConfig": {
@@ -4,7 +4,7 @@ import { mkdir } from "node:fs/promises";
4
4
  import path from "node:path";
5
5
  import type { InArgs, InValue, Client as LibsqlClient } from "@libsql/client";
6
6
  import { type BaseEnv, dayjs, FIELD_META, type PromiseOrObject } from "akanjs/base";
7
- import type { ConstantModel } from "akanjs/constant";
7
+ import { type ConstantModel, getDefault } from "akanjs/constant";
8
8
  import {
9
9
  createDocumentId,
10
10
  type DatabaseModel,
@@ -300,6 +300,13 @@ const jsonPath = (path: string) =>
300
300
  .map((part) => part.replaceAll('"', '\\"'))
301
301
  .join(".")}`;
302
302
  const encodeSqlValue = (value: unknown) => encodeDocumentValue(value);
303
+
304
+ const decodeDateValue = (value: unknown) => {
305
+ if (value === null || value === undefined) return value;
306
+ if (typeof value === "number") return dayjs(value);
307
+ const epoch = Number(value);
308
+ return Number.isNaN(epoch) ? dayjs(value as never) : dayjs(epoch);
309
+ };
303
310
  const QUERY_OPERATOR_KEYS = new Set([
304
311
  "eq",
305
312
  "ne",
@@ -773,6 +780,9 @@ export class SqliteDocumentStore {
773
780
  } else {
774
781
  doc[key] = value;
775
782
  }
783
+ if (doc[key] !== undefined && doc[key] !== null) {
784
+ doc[key] = this.normalizeWriteValue(doc[key], props);
785
+ }
776
786
  if (props.enum && doc[key] !== undefined && doc[key] !== null) {
777
787
  const values = Array.isArray(doc[key]) ? doc[key] : [doc[key]];
778
788
  const fieldEnum = props.enum as { has: (value: unknown) => boolean } | undefined;
@@ -958,12 +968,28 @@ export class SqliteDocumentStore {
958
968
 
959
969
  private decodeDocumentPayload(payload: Record<string, unknown>) {
960
970
  const fields = this.database.doc[FIELD_META] as unknown as FieldMap;
961
- return Object.fromEntries(
962
- Object.entries(payload).map(([key, value]) => {
963
- const props = fields[key]?.getProps?.();
964
- return [key, props ? this.decodeFieldValue(value, props) : value];
965
- }),
966
- );
971
+ const result: Record<string, unknown> = {};
972
+ for (const [key, fieldMeta] of Object.entries(fields)) {
973
+ if (BASE_COLUMNS.has(key)) continue;
974
+ const props = fieldMeta.getProps();
975
+ const value = payload[key];
976
+ if (value === undefined) {
977
+ const def = props.default;
978
+ if (def !== undefined && def !== null) {
979
+ result[key] = typeof def === "function" ? (def as (data: unknown) => unknown)(payload) : def;
980
+ } else if (props.nullable) {
981
+ result[key] = null;
982
+ }
983
+ } else {
984
+ result[key] = this.decodeFieldValue(value, props);
985
+ }
986
+ }
987
+ for (const [key, value] of Object.entries(payload)) {
988
+ if (key in result || BASE_COLUMNS.has(key)) continue;
989
+ const props = fields[key]?.getProps?.();
990
+ result[key] = props ? this.decodeFieldValue(value, props) : value;
991
+ }
992
+ return result;
967
993
  }
968
994
 
969
995
  private decodeFieldValue(value: unknown, props: Record<string, unknown>): unknown {
@@ -973,8 +999,8 @@ export class SqliteDocumentStore {
973
999
  return new Map(entries.map(([key, item]) => [key, this.decodeMapValue(item, props)]));
974
1000
  }
975
1001
  if (props.modelRef === Date) {
976
- if (Array.isArray(value)) return value.map((item) => (item === null ? item : dayjs(Number(item))));
977
- return dayjs(Number(value));
1002
+ if (Array.isArray(value)) return value.map((item) => (item === null ? item : decodeDateValue(item)));
1003
+ return decodeDateValue(value);
978
1004
  }
979
1005
  if (Array.isArray(value)) return value.map((item) => this.decodeNestedValue(item, props));
980
1006
  return this.decodeNestedValue(value, props);
@@ -982,7 +1008,7 @@ export class SqliteDocumentStore {
982
1008
 
983
1009
  private decodeMapValue(value: unknown, props: Record<string, unknown>) {
984
1010
  if (value === undefined || value === null) return value;
985
- if (props.of === Date) return dayjs(Number(value));
1011
+ if (props.of === Date) return decodeDateValue(value);
986
1012
  return value;
987
1013
  }
988
1014
 
@@ -991,12 +1017,44 @@ export class SqliteDocumentStore {
991
1017
  if (!props.isClass || !props.isScalar) return value;
992
1018
  const scalarFields = (props.modelRef as { [FIELD_META]?: FieldMap } | undefined)?.[FIELD_META];
993
1019
  if (!scalarFields) return value;
994
- return Object.fromEntries(
995
- Object.entries(value as Record<string, unknown>).map(([key, nested]) => {
996
- const nestedProps = scalarFields[key]?.getProps?.();
997
- return [key, nestedProps ? this.decodeFieldValue(nested, nestedProps) : nested];
998
- }),
999
- );
1020
+ const source = value as Record<string, unknown>;
1021
+ const defaults = getDefault(scalarFields as never) as Record<string, unknown>;
1022
+ const result: Record<string, unknown> = {};
1023
+ for (const [key, fieldMeta] of Object.entries(scalarFields)) {
1024
+ const nestedProps = fieldMeta.getProps();
1025
+ const nested = source[key];
1026
+ result[key] = nested === undefined ? defaults[key] : this.decodeFieldValue(nested, nestedProps);
1027
+ }
1028
+ for (const [key, nested] of Object.entries(source)) {
1029
+ if (!(key in result)) result[key] = nested;
1030
+ }
1031
+ return result;
1032
+ }
1033
+
1034
+ private normalizeWriteValue(value: unknown, props: Record<string, unknown>): unknown {
1035
+ if (value === undefined || value === null) return value;
1036
+ if (props.modelRef === Date) {
1037
+ if (Array.isArray(value))
1038
+ return value.map((item) => (item === null || item === undefined ? item : dayjs(item as never)));
1039
+ return dayjs(value as never);
1040
+ }
1041
+ if (!props.isClass || !props.isScalar) return value;
1042
+ if (Array.isArray(value)) return value.map((item) => this.fillScalarDefaults(item, props));
1043
+ return this.fillScalarDefaults(value, props);
1044
+ }
1045
+
1046
+ private fillScalarDefaults(value: unknown, props: Record<string, unknown>): unknown {
1047
+ if (!value || typeof value !== "object") return value;
1048
+ const scalarFields = (props.modelRef as { [FIELD_META]?: FieldMap } | undefined)?.[FIELD_META];
1049
+ if (!scalarFields) return value;
1050
+ const defaults = getDefault(scalarFields as never) as Record<string, unknown>;
1051
+ const result = { ...(value as Record<string, unknown>) };
1052
+ for (const [key, fieldMeta] of Object.entries(scalarFields)) {
1053
+ const nestedProps = fieldMeta.getProps();
1054
+ if (result[key] === undefined) result[key] = defaults[key];
1055
+ else result[key] = this.normalizeWriteValue(result[key], nestedProps);
1056
+ }
1057
+ return result;
1000
1058
  }
1001
1059
 
1002
1060
  hydrate(data: DocumentRecord, originalData: DocumentRecord = data) {
@@ -1,7 +1,7 @@
1
1
  import { Database } from "bun:sqlite";
2
2
  import type { Client as LibsqlClient } from "@libsql/client";
3
3
  import { type BaseEnv, type PromiseOrObject } from "akanjs/base";
4
- import type { ConstantModel } from "akanjs/constant";
4
+ import { type ConstantModel } from "akanjs/constant";
5
5
  import { type DatabaseModel, type DocumentQuery, type DocumentSchema, type DocumentUpdate, type DocumentUpdateOptions, type SchemaOf } from "akanjs/document";
6
6
  import type { Sql } from "postgres";
7
7
  export interface SqliteDatabaseConfig {
@@ -256,6 +256,8 @@ export declare class SqliteDocumentStore {
256
256
  private decodeFieldValue;
257
257
  private decodeMapValue;
258
258
  private decodeNestedValue;
259
+ private normalizeWriteValue;
260
+ private fillScalarDefaults;
259
261
  hydrate(data: DocumentRecord, originalData?: DocumentRecord): any;
260
262
  private runHooks;
261
263
  private insertStmt;
@@ -185,7 +185,7 @@ export const Modal = ({ className, bodyClassName, confirmClose, children, onCanc
185
185
  return createPortal(
186
186
  <>
187
187
  <div
188
- className={clsx("fixed inset-0 z-10", showBackground && "animate-fadeIn bg-base-content/50 backdrop-blur-md")}
188
+ className={clsx("fixed inset-0 z-10", showBackground && "animate-fadeIn bg-black/50 backdrop-blur-md")}
189
189
  onClick={(event) => {
190
190
  if (event.target !== event.currentTarget) return;
191
191
  requestClose();