@diphyx/harlemify 5.3.0 → 5.4.0

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.
@@ -1,77 +1,14 @@
1
1
  import { defu } from "defu";
2
2
  import { z } from "zod";
3
3
  import { isPlainObject, isEmptyRecord } from "./base.js";
4
- function resolveStringZeroValue() {
5
- return "";
6
- }
7
- function resolveNumberZeroValue() {
8
- return 0;
9
- }
10
- function resolveBooleanZeroValue() {
11
- return false;
12
- }
13
- function resolveBigintZeroValue() {
14
- return BigInt(0);
15
- }
16
- function resolveDateZeroValue() {
17
- return /* @__PURE__ */ new Date(0);
18
- }
19
- function resolveArrayZeroValue() {
20
- return [];
21
- }
22
- function resolveRecordZeroValue() {
23
- return {};
24
- }
25
- function resolveMapZeroValue() {
26
- return /* @__PURE__ */ new Map();
27
- }
28
- function resolveSetZeroValue() {
29
- return /* @__PURE__ */ new Set();
30
- }
31
- function resolveObjectZeroValue(definition) {
32
- const output = {};
33
- if (definition.shape) {
34
- for (const [key, value] of Object.entries(definition.shape)) {
35
- output[key] = resolveZeroValue(value);
36
- }
37
- }
38
- return output;
39
- }
40
- function resolveEnumZeroValue(definition) {
41
- if (definition.entries) {
42
- return Object.values(definition.entries)[0];
43
- }
44
- return void 0;
45
- }
46
- function resolveLiteralZeroValue(definition) {
47
- if (definition.values) {
48
- return definition.values[0];
49
- }
50
- return void 0;
51
- }
52
- function resolveTupleZeroValue(definition) {
53
- if (definition.items) {
54
- return definition.items.map(resolveZeroValue);
55
- }
56
- return [];
57
- }
58
- function resolveUnionZeroValue(definition) {
59
- if (definition.options?.[0]) {
60
- return resolveZeroValue(definition.options[0]);
61
- }
62
- return void 0;
63
- }
64
- function resolveDefaultZeroValue(definition) {
65
- if (typeof definition.defaultValue === "function") {
66
- return definition.defaultValue();
4
+ function resolveShapeFields(shape) {
5
+ if (!("shape" in shape) || typeof shape.shape !== "object" || !shape.shape) {
6
+ return void 0;
67
7
  }
68
- return definition.defaultValue;
8
+ return shape.shape;
69
9
  }
70
- function resolveInnerZeroValue(definition) {
71
- if (definition.innerType) {
72
- return resolveZeroValue(definition.innerType);
73
- }
74
- return void 0;
10
+ function resolveFieldMeta(field) {
11
+ return field?.meta?.();
75
12
  }
76
13
  function resolveZeroValue(field) {
77
14
  const definition = field.def;
@@ -80,93 +17,83 @@ function resolveZeroValue(field) {
80
17
  }
81
18
  switch (definition.type) {
82
19
  case "string": {
83
- return resolveStringZeroValue();
20
+ return "";
84
21
  }
85
22
  case "number": {
86
- return resolveNumberZeroValue();
23
+ return 0;
87
24
  }
88
25
  case "boolean": {
89
- return resolveBooleanZeroValue();
26
+ return false;
90
27
  }
91
28
  case "bigint": {
92
- return resolveBigintZeroValue();
29
+ return BigInt(0);
93
30
  }
94
31
  case "date": {
95
- return resolveDateZeroValue();
32
+ return /* @__PURE__ */ new Date(0);
96
33
  }
97
34
  case "array": {
98
- return resolveArrayZeroValue();
35
+ return [];
99
36
  }
100
37
  case "record": {
101
- return resolveRecordZeroValue();
38
+ return {};
102
39
  }
103
40
  case "map": {
104
- return resolveMapZeroValue();
41
+ return /* @__PURE__ */ new Map();
105
42
  }
106
43
  case "set": {
107
- return resolveSetZeroValue();
44
+ return /* @__PURE__ */ new Set();
108
45
  }
109
46
  case "object": {
110
- return resolveObjectZeroValue(definition);
47
+ const output = {};
48
+ if (definition.shape) {
49
+ for (const [key, value] of Object.entries(definition.shape)) {
50
+ output[key] = resolveZeroValue(value);
51
+ }
52
+ }
53
+ return output;
111
54
  }
112
55
  case "enum": {
113
- return resolveEnumZeroValue(definition);
56
+ if (definition.entries) {
57
+ return Object.values(definition.entries)[0];
58
+ }
59
+ return void 0;
114
60
  }
115
61
  case "literal": {
116
- return resolveLiteralZeroValue(definition);
62
+ if (definition.values) {
63
+ return definition.values[0];
64
+ }
65
+ return void 0;
117
66
  }
118
67
  case "tuple": {
119
- return resolveTupleZeroValue(definition);
68
+ if (definition.items) {
69
+ return definition.items.map(resolveZeroValue);
70
+ }
71
+ return [];
120
72
  }
121
73
  case "union": {
122
- return resolveUnionZeroValue(definition);
74
+ if (definition.options?.[0]) {
75
+ return resolveZeroValue(definition.options[0]);
76
+ }
77
+ return void 0;
123
78
  }
124
79
  case "default": {
125
- return resolveDefaultZeroValue(definition);
80
+ if (typeof definition.defaultValue === "function") {
81
+ return definition.defaultValue();
82
+ }
83
+ return definition.defaultValue;
126
84
  }
127
85
  case "optional":
128
86
  case "nullable": {
129
- return resolveInnerZeroValue(definition);
87
+ if (definition.innerType) {
88
+ return resolveZeroValue(definition.innerType);
89
+ }
90
+ return void 0;
130
91
  }
131
92
  default: {
132
93
  return void 0;
133
94
  }
134
95
  }
135
96
  }
136
- export function resolveShape(shape) {
137
- const resolved = {
138
- identifier: void 0,
139
- defaults: {},
140
- fields: [],
141
- aliases: {}
142
- };
143
- for (const [key, field] of Object.entries(shape.shape)) {
144
- resolved.fields.push(key);
145
- const fieldMeta = field.meta();
146
- if (fieldMeta?.identifier) {
147
- resolved.identifier = key;
148
- }
149
- if (fieldMeta?.alias) {
150
- resolved.aliases[key] = fieldMeta.alias;
151
- }
152
- const fieldDefinition = field.def;
153
- if (fieldDefinition?.defaultValue !== void 0) {
154
- if (typeof fieldDefinition.defaultValue === "function") {
155
- resolved.defaults[key] = fieldDefinition.defaultValue();
156
- continue;
157
- }
158
- resolved.defaults[key] = fieldDefinition.defaultValue;
159
- }
160
- }
161
- if (!resolved.identifier) {
162
- if (resolved.fields.includes("id")) {
163
- resolved.identifier = "id";
164
- } else if (resolved.fields.includes("_id")) {
165
- resolved.identifier = "_id";
166
- }
167
- }
168
- return resolved;
169
- }
170
97
  function resolveAliasObject(data, mapping) {
171
98
  const output = {};
172
99
  for (const [key, value] of Object.entries(data)) {
@@ -212,21 +139,54 @@ export function resolveAliasOutbound(data, aliases) {
212
139
  }
213
140
  return data;
214
141
  }
215
- export function resolveDefaults(shape, overrides) {
142
+ export function resolveShapeIdentifier(shape, ...overrides) {
143
+ for (const key of overrides) {
144
+ if (key) {
145
+ return key;
146
+ }
147
+ }
148
+ const fields = resolveShapeFields(shape);
149
+ if (!fields) {
150
+ return "id";
151
+ }
152
+ for (const [key, field] of Object.entries(fields)) {
153
+ const meta = resolveFieldMeta(field);
154
+ if (meta?.identifier) {
155
+ return key;
156
+ }
157
+ }
158
+ return "id";
159
+ }
160
+ export function resolveShapeAliases(shape) {
161
+ const output = {};
162
+ const fields = resolveShapeFields(shape);
163
+ if (!fields) {
164
+ return output;
165
+ }
166
+ for (const [key, field] of Object.entries(fields)) {
167
+ const meta = resolveFieldMeta(field);
168
+ if (meta?.alias) {
169
+ output[key] = meta.alias;
170
+ }
171
+ }
172
+ return output;
173
+ }
174
+ export function resolveZeroValues(shape) {
216
175
  const output = {};
217
176
  for (const [key, field] of Object.entries(shape.shape)) {
218
177
  output[key] = resolveZeroValue(field);
219
178
  }
220
- if (overrides) {
221
- return defu(overrides, output);
222
- }
223
179
  return output;
224
180
  }
225
181
  export function createShape(definition) {
226
182
  const object = z.object(definition);
227
183
  const shape = Object.assign(object, {
228
184
  defaults(overrides) {
229
- return resolveDefaults(object, overrides);
185
+ const zero = resolveZeroValues(object);
186
+ if (overrides) {
187
+ return defu(overrides, zero);
188
+ }
189
+ return zero;
230
190
  }
231
191
  });
232
192
  return shape;
@@ -1,5 +1,5 @@
1
1
  import type { createStore as createSourceStore } from "@harlem/core";
2
- import { type ModelDefinitions, type ModelDefinitionsInfer, type StoreModel } from "../types/model.js";
2
+ import type { ModelDefinitions, ModelDefinitionsInfer, StoreModel } from "../types/model.js";
3
3
  import type { ViewDefinitions, StoreView } from "../types/view.js";
4
4
  import type { ActionDefinition, ActionDefinitions, StoreAction } from "../types/action.js";
5
5
  export declare function createStoreState<MD extends ModelDefinitions>(modelDefinitions: MD): ModelDefinitionsInfer<MD>;
@@ -1,19 +1,10 @@
1
1
  import { createModel } from "./model.js";
2
2
  import { createView } from "./view.js";
3
3
  import { createAction } from "./action.js";
4
- import {
5
- ModelType,
6
- ModelManyKind
7
- } from "../types/model.js";
8
4
  export function createStoreState(modelDefinitions) {
9
5
  const output = {};
10
- for (const [key, { options, type }] of Object.entries(modelDefinitions)) {
11
- if (type === ModelType.ONE) {
12
- output[key] = options?.default ?? null;
13
- } else {
14
- const manyDefault = options?.kind === ModelManyKind.RECORD ? {} : [];
15
- output[key] = options?.default ?? manyDefault;
16
- }
6
+ for (const [key, definition] of Object.entries(modelDefinitions)) {
7
+ output[key] = definition.default();
17
8
  }
18
9
  return output;
19
10
  }
@@ -1,6 +1,6 @@
1
1
  import { ViewClone } from "../types/view.js";
2
2
  function resolveClonedValue(definition, value) {
3
- if (!definition.options?.clone || value === null || value === void 0) {
3
+ if (!definition.options?.clone) {
4
4
  return value;
5
5
  }
6
6
  if (definition.options.clone === ViewClone.DEEP) {
@@ -2,13 +2,16 @@ export { createStore } from "./core/store.js";
2
2
  export type { Store, StoreConfig } from "./core/types/store.js";
3
3
  export { shape } from "./core/layers/shape.js";
4
4
  export type { ShapeInfer } from "./core/types/shape.js";
5
- export { ModelType, ModelManyKind, ModelOneMode, ModelManyMode } from "./core/types/model.js";
5
+ export { ModelType, ModelManyKind, ModelOneMode, ModelManyMode, ModelSilent } from "./core/types/model.js";
6
6
  export type { ModelOneCommitOptions, ModelManyCommitOptions } from "./core/types/model.js";
7
7
  export { ViewClone } from "./core/types/view.js";
8
8
  export type { ViewDefinitionOptions } from "./core/types/view.js";
9
9
  export { ActionStatus, ActionConcurrent, ActionType, ActionApiMethod } from "./core/types/action.js";
10
10
  export type { ActionCall, ActionApiCall, ActionHandlerCall, ActionCallOptions, ActionCallBaseOptions, ActionApiCallOptions, ActionHandlerCallOptions, ActionCallTransformerOptions, ActionCallBindOptions, ActionCallCommitOptions, ActionHandlerOptions, ActionResolvedApi, } from "./core/types/action.js";
11
11
  export { ActionApiError, ActionHandlerError, ActionCommitError, ActionConcurrentError } from "./core/utils/error.js";
12
+ export type { ComposeCallback, ComposeCall, ComposeDefinitions, ComposeContext, StoreCompose, } from "./core/types/compose.js";
13
+ export { useStoreCompose } from "./composables/compose.js";
14
+ export type { UseStoreCompose } from "./composables/compose.js";
12
15
  export { useIsolatedActionStatus, useIsolatedActionError, useStoreAction } from "./composables/action.js";
13
16
  export type { UseStoreActionOptions, UseStoreAction } from "./composables/action.js";
14
17
  export { useStoreModel } from "./composables/model.js";
@@ -1,9 +1,10 @@
1
1
  export { createStore } from "./core/store.js";
2
2
  export { shape } from "./core/layers/shape.js";
3
- export { ModelType, ModelManyKind, ModelOneMode, ModelManyMode } from "./core/types/model.js";
3
+ export { ModelType, ModelManyKind, ModelOneMode, ModelManyMode, ModelSilent } from "./core/types/model.js";
4
4
  export { ViewClone } from "./core/types/view.js";
5
5
  export { ActionStatus, ActionConcurrent, ActionType, ActionApiMethod } from "./core/types/action.js";
6
6
  export { ActionApiError, ActionHandlerError, ActionCommitError, ActionConcurrentError } from "./core/utils/error.js";
7
+ export { useStoreCompose } from "./composables/compose.js";
7
8
  export { useIsolatedActionStatus, useIsolatedActionError, useStoreAction } from "./composables/action.js";
8
9
  export { useStoreModel } from "./composables/model.js";
9
10
  export { useStoreView } from "./composables/view.js";
@@ -1,23 +1,9 @@
1
1
  import { createVuePlugin } from "@harlem/core";
2
- import { createServerSSRPlugin, createClientSSRPlugin, getBridgingScript } from "@harlem/plugin-ssr";
3
- import { defineNuxtPlugin, useHead } from "#imports";
2
+ import { defineNuxtPlugin } from "#imports";
3
+ import { createServerSideRenderingPlugin } from "./plugins/ssr.js";
4
4
  export default defineNuxtPlugin((nuxtApp) => {
5
- const plugins = [];
6
- if (import.meta.server) {
7
- plugins.push(createServerSSRPlugin());
8
- }
9
- if (import.meta.client && window["__harlemState"]) {
10
- plugins.push(createClientSSRPlugin());
11
- }
12
5
  const harlem = createVuePlugin({
13
- plugins
6
+ plugins: [createServerSideRenderingPlugin(nuxtApp)]
14
7
  });
15
8
  nuxtApp.vueApp.use(harlem);
16
- useHead({
17
- script: [
18
- {
19
- innerHTML: getBridgingScript()
20
- }
21
- ]
22
- });
23
9
  });
@@ -0,0 +1,9 @@
1
+ import type { HarlemPlugin } from "@harlem/core";
2
+ interface ServerSideRenderingContext {
3
+ hooks: {
4
+ hook: (name: "app:rendered", fn: () => void) => void;
5
+ };
6
+ payload: Record<string, unknown>;
7
+ }
8
+ export declare function createServerSideRenderingPlugin(nuxtApp: ServerSideRenderingContext): HarlemPlugin;
9
+ export {};
@@ -0,0 +1,53 @@
1
+ function handleServer(nuxtApp, stores) {
2
+ for (const store of stores.values()) {
3
+ const mutations = store.registrations["mutations"];
4
+ if (!mutations) {
5
+ continue;
6
+ }
7
+ for (const [name, registration] of mutations) {
8
+ if (!name.endsWith(":reset")) {
9
+ continue;
10
+ }
11
+ registration.producer()({});
12
+ }
13
+ }
14
+ nuxtApp.hooks.hook("app:rendered", () => {
15
+ const snapshot = {};
16
+ for (const store of stores.values()) {
17
+ snapshot[store.name] = store.state;
18
+ }
19
+ nuxtApp.payload.harlemifyState = snapshot;
20
+ });
21
+ }
22
+ function handleClient(nuxtApp, eventEmitter, stores) {
23
+ const harlemifyState = nuxtApp.payload.harlemifyState;
24
+ if (!harlemifyState) {
25
+ return;
26
+ }
27
+ eventEmitter.on("ssr:init:client", (payload) => {
28
+ if (!payload) {
29
+ return;
30
+ }
31
+ const store = stores.get(payload.store);
32
+ if (!store) {
33
+ return;
34
+ }
35
+ const snapshot = harlemifyState[store.name];
36
+ if (!snapshot) {
37
+ return;
38
+ }
39
+ store.write("harlemify:ssr:init", "harlemify", (state) => {
40
+ Object.assign(state, snapshot);
41
+ });
42
+ });
43
+ }
44
+ export function createServerSideRenderingPlugin(nuxtApp) {
45
+ return (app, eventEmitter, stores) => {
46
+ if (import.meta.server) {
47
+ handleServer(nuxtApp, stores);
48
+ }
49
+ if (import.meta.client) {
50
+ handleClient(nuxtApp, eventEmitter, stores);
51
+ }
52
+ };
53
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@diphyx/harlemify",
3
- "version": "5.3.0",
3
+ "version": "5.4.0",
4
4
  "description": "API state management for Nuxt powered by Harlem",
5
5
  "keywords": [
6
6
  "nuxt",
@@ -41,26 +41,25 @@
41
41
  "dist"
42
42
  ],
43
43
  "dependencies": {
44
- "@harlem/core": "^3.0.0",
45
- "@harlem/plugin-ssr": "^3.0.0",
46
- "@nuxt/kit": "^3.14.0",
47
- "consola": "^3.0.0",
48
- "defu": "^6.0.0",
49
- "zod": "^4.0.0"
44
+ "@harlem/core": "^3.1.8",
45
+ "@nuxt/kit": "^3.14.0 || ^4.0.0",
46
+ "consola": "^3.4.2",
47
+ "defu": "^6.1.4",
48
+ "zod": "^4.3.6"
50
49
  },
51
50
  "devDependencies": {
52
- "@nuxt/eslint-config": "^0.6.0",
53
- "@nuxt/module-builder": "^0.8.0",
54
- "@nuxt/schema": "^3.14.0",
55
- "@nuxt/test-utils": "^3.14.0",
56
- "@playwright/test": "^1.50.0",
57
- "@types/node": "^22.0.0",
51
+ "@nuxt/eslint-config": "^0.6.2",
52
+ "@nuxt/module-builder": "^0.8.4",
53
+ "@nuxt/schema": "^4.3.1",
54
+ "@nuxt/test-utils": "^3.23.0",
55
+ "@playwright/test": "^1.58.2",
56
+ "@types/node": "^22.19.11",
58
57
  "@vitest/coverage-v8": "^2.1.9",
59
- "eslint": "^9.0.0",
60
- "nuxt": "^3.14.0",
61
- "typescript": "^5.6.0",
62
- "vitest": "^2.0.0",
63
- "vue": "^3.5.26"
58
+ "eslint": "^9.39.2",
59
+ "nuxt": "^4.3.1",
60
+ "typescript": "^5.9.3",
61
+ "vitest": "^2.1.9",
62
+ "vue": "^3.5.28"
64
63
  },
65
64
  "scripts": {
66
65
  "cleanup": "pnpm nuxt cleanup && pnpm nuxt cleanup playground",