@htlkg/data 0.0.2 → 0.0.9

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.
Files changed (52) hide show
  1. package/README.md +53 -0
  2. package/dist/client/index.d.ts +16 -1
  3. package/dist/client/index.js +13 -1
  4. package/dist/client/index.js.map +1 -1
  5. package/dist/hooks/index.d.ts +113 -5
  6. package/dist/hooks/index.js +155 -164
  7. package/dist/hooks/index.js.map +1 -1
  8. package/dist/index.d.ts +4 -2
  9. package/dist/index.js +263 -161
  10. package/dist/index.js.map +1 -1
  11. package/dist/queries/index.js.map +1 -1
  12. package/dist/stores/index.d.ts +106 -0
  13. package/dist/stores/index.js +108 -0
  14. package/dist/stores/index.js.map +1 -0
  15. package/package.json +64 -37
  16. package/src/client/__tests__/server.test.ts +106 -0
  17. package/src/client/client.md +91 -0
  18. package/src/client/index.test.ts +198 -0
  19. package/src/client/index.ts +145 -0
  20. package/src/client/server.ts +118 -0
  21. package/src/content-collections/content-collections.md +87 -0
  22. package/src/content-collections/generator.ts +314 -0
  23. package/src/content-collections/index.ts +32 -0
  24. package/src/content-collections/schemas.ts +75 -0
  25. package/src/content-collections/sync.ts +139 -0
  26. package/src/hooks/README.md +293 -0
  27. package/src/hooks/createDataHook.ts +208 -0
  28. package/src/hooks/data-hook-errors.property.test.ts +270 -0
  29. package/src/hooks/data-hook-filters.property.test.ts +263 -0
  30. package/src/hooks/data-hooks.property.test.ts +190 -0
  31. package/src/hooks/hooks.test.ts +76 -0
  32. package/src/hooks/index.ts +21 -0
  33. package/src/hooks/useAccounts.ts +66 -0
  34. package/src/hooks/useBrands.ts +95 -0
  35. package/src/hooks/useProducts.ts +88 -0
  36. package/src/hooks/useUsers.ts +89 -0
  37. package/src/index.ts +32 -0
  38. package/src/mutations/accounts.ts +127 -0
  39. package/src/mutations/brands.ts +133 -0
  40. package/src/mutations/index.ts +32 -0
  41. package/src/mutations/mutations.md +96 -0
  42. package/src/mutations/users.ts +136 -0
  43. package/src/queries/accounts.ts +121 -0
  44. package/src/queries/brands.ts +176 -0
  45. package/src/queries/index.ts +45 -0
  46. package/src/queries/products.ts +282 -0
  47. package/src/queries/queries.md +88 -0
  48. package/src/queries/server-helpers.ts +114 -0
  49. package/src/queries/users.ts +199 -0
  50. package/src/stores/createStores.ts +148 -0
  51. package/src/stores/index.ts +15 -0
  52. package/src/stores/stores.md +104 -0
@@ -0,0 +1,106 @@
1
+ import { WritableAtom } from 'nanostores';
2
+
3
+ /**
4
+ * Store Factory
5
+ *
6
+ * Creates request-scoped nanostores for sharing data across Vue islands
7
+ * without props. Works with @inox-tools/request-nanostores for Astro integration.
8
+ */
9
+
10
+ /**
11
+ * Type for the shared function from @it-astro:request-nanostores
12
+ */
13
+ type SharedFn = <T>(key: string, atomFactory: WritableAtom<T>) => WritableAtom<T>;
14
+ /**
15
+ * Configuration for a resource store
16
+ */
17
+ interface StoreConfig {
18
+ /** Resource name (e.g., 'accounts', 'users') */
19
+ name: string;
20
+ /** Related resources to create stores for (e.g., ['brands', 'products']) */
21
+ relations?: string[];
22
+ }
23
+ /**
24
+ * Result type for created stores
25
+ */
26
+ type ResourceStores<T, TRelations extends string = never> = {
27
+ /** Main resource store */
28
+ $data: WritableAtom<readonly T[]>;
29
+ } & {
30
+ [K in TRelations as `$${K}`]: WritableAtom<readonly any[]>;
31
+ };
32
+ /**
33
+ * Creates request-scoped stores for a resource and its relations.
34
+ *
35
+ * This factory is designed to work with Astro's request-nanostores integration,
36
+ * providing request-isolated state that syncs from server to client.
37
+ *
38
+ * @param shared - The shared function from @it-astro:request-nanostores
39
+ * @param config - Store configuration
40
+ * @returns Object containing the main store and relation stores
41
+ *
42
+ * @example
43
+ * ```typescript
44
+ * // In your store file (e.g., stores/accounts.ts)
45
+ * import { shared } from '@it-astro:request-nanostores';
46
+ * import { createResourceStores } from '@htlkg/data/stores';
47
+ *
48
+ * export const accountStores = createResourceStores(shared, {
49
+ * name: 'accounts',
50
+ * relations: ['brands'],
51
+ * });
52
+ *
53
+ * // This creates:
54
+ * // - accountStores.$data (WritableAtom<any[]>) - main accounts store
55
+ * // - accountStores.$brands (WritableAtom<any[]>) - related brands store
56
+ *
57
+ * // For cleaner exports:
58
+ * export const $accounts = accountStores.$data;
59
+ * export const $accountsBrands = accountStores.$brands;
60
+ * ```
61
+ *
62
+ * @example
63
+ * ```typescript
64
+ * // With typed stores
65
+ * interface Account { id: number; name: string; }
66
+ * interface Brand { id: number; name: string; }
67
+ *
68
+ * export const { $data: $accounts, $brands } = createResourceStores<Account, 'brands'>(
69
+ * shared,
70
+ * { name: 'accounts', relations: ['brands'] }
71
+ * );
72
+ * ```
73
+ */
74
+ declare function createResourceStores<T = any, TRelations extends string = never>(shared: SharedFn, config: StoreConfig & {
75
+ relations?: TRelations[];
76
+ }): ResourceStores<T, TRelations>;
77
+ /**
78
+ * Creates a simple single store (non-factory version for simpler use cases)
79
+ *
80
+ * @example
81
+ * ```typescript
82
+ * import { shared } from '@it-astro:request-nanostores';
83
+ * import { createStore } from '@htlkg/data/stores';
84
+ *
85
+ * export const $currentUser = createStore<User>(shared, 'currentUser');
86
+ * ```
87
+ */
88
+ declare function createStore<T>(shared: SharedFn, name: string, defaultValue?: T[]): WritableAtom<readonly T[]>;
89
+ /**
90
+ * Creates a single-value store (for non-array values like current user)
91
+ *
92
+ * @example
93
+ * ```typescript
94
+ * import { shared } from '@it-astro:request-nanostores';
95
+ * import { createSingleStore } from '@htlkg/data/stores';
96
+ *
97
+ * export const $currentUser = createSingleStore<User | null>(shared, 'currentUser', null);
98
+ * ```
99
+ */
100
+ declare function createSingleStore<T>(shared: SharedFn, name: string, defaultValue: T): WritableAtom<T>;
101
+ /**
102
+ * Helper to create typed store with proper inference
103
+ */
104
+ type InferStoreType<TStore extends WritableAtom<any>> = TStore extends WritableAtom<infer T> ? T : never;
105
+
106
+ export { type InferStoreType, type ResourceStores, type StoreConfig, createResourceStores, createSingleStore, createStore };
@@ -0,0 +1,108 @@
1
+ // ../../node_modules/.pnpm/nanostores@1.1.0/node_modules/nanostores/clean-stores/index.js
2
+ var clean = /* @__PURE__ */ Symbol("clean");
3
+
4
+ // ../../node_modules/.pnpm/nanostores@1.1.0/node_modules/nanostores/atom/index.js
5
+ var listenerQueue = [];
6
+ var lqIndex = 0;
7
+ var QUEUE_ITEMS_PER_LISTENER = 4;
8
+ var epoch = 0;
9
+ var atom = /* @__NO_SIDE_EFFECTS__ */ (initialValue) => {
10
+ let listeners = [];
11
+ let $atom = {
12
+ get() {
13
+ if (!$atom.lc) {
14
+ $atom.listen(() => {
15
+ })();
16
+ }
17
+ return $atom.value;
18
+ },
19
+ lc: 0,
20
+ listen(listener) {
21
+ $atom.lc = listeners.push(listener);
22
+ return () => {
23
+ for (let i = lqIndex + QUEUE_ITEMS_PER_LISTENER; i < listenerQueue.length; ) {
24
+ if (listenerQueue[i] === listener) {
25
+ listenerQueue.splice(i, QUEUE_ITEMS_PER_LISTENER);
26
+ } else {
27
+ i += QUEUE_ITEMS_PER_LISTENER;
28
+ }
29
+ }
30
+ let index = listeners.indexOf(listener);
31
+ if (~index) {
32
+ listeners.splice(index, 1);
33
+ if (!--$atom.lc) $atom.off();
34
+ }
35
+ };
36
+ },
37
+ notify(oldValue, changedKey) {
38
+ epoch++;
39
+ let runListenerQueue = !listenerQueue.length;
40
+ for (let listener of listeners) {
41
+ listenerQueue.push(listener, $atom.value, oldValue, changedKey);
42
+ }
43
+ if (runListenerQueue) {
44
+ for (lqIndex = 0; lqIndex < listenerQueue.length; lqIndex += QUEUE_ITEMS_PER_LISTENER) {
45
+ listenerQueue[lqIndex](
46
+ listenerQueue[lqIndex + 1],
47
+ listenerQueue[lqIndex + 2],
48
+ listenerQueue[lqIndex + 3]
49
+ );
50
+ }
51
+ listenerQueue.length = 0;
52
+ }
53
+ },
54
+ /* It will be called on last listener unsubscribing.
55
+ We will redefine it in onMount and onStop. */
56
+ off() {
57
+ },
58
+ set(newValue) {
59
+ let oldValue = $atom.value;
60
+ if (oldValue !== newValue) {
61
+ $atom.value = newValue;
62
+ $atom.notify(oldValue);
63
+ }
64
+ },
65
+ subscribe(listener) {
66
+ let unbind = $atom.listen(listener);
67
+ listener($atom.value);
68
+ return unbind;
69
+ },
70
+ value: initialValue
71
+ };
72
+ if (process.env.NODE_ENV !== "production") {
73
+ $atom[clean] = () => {
74
+ listeners = [];
75
+ $atom.lc = 0;
76
+ $atom.off();
77
+ };
78
+ }
79
+ return $atom;
80
+ };
81
+
82
+ // src/stores/createStores.ts
83
+ function capitalize(str) {
84
+ return str.charAt(0).toUpperCase() + str.slice(1);
85
+ }
86
+ function createResourceStores(shared, config) {
87
+ const { name, relations = [] } = config;
88
+ const stores = {};
89
+ stores.$data = shared(name, atom([]));
90
+ for (const relation of relations) {
91
+ const storeKey = `$${relation}`;
92
+ const sharedKey = `${name}${capitalize(relation)}`;
93
+ stores[storeKey] = shared(sharedKey, atom([]));
94
+ }
95
+ return stores;
96
+ }
97
+ function createStore(shared, name, defaultValue = []) {
98
+ return shared(name, atom(defaultValue));
99
+ }
100
+ function createSingleStore(shared, name, defaultValue) {
101
+ return shared(name, atom(defaultValue));
102
+ }
103
+ export {
104
+ createResourceStores,
105
+ createSingleStore,
106
+ createStore
107
+ };
108
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../node_modules/.pnpm/nanostores@1.1.0/node_modules/nanostores/clean-stores/index.js","../../../../node_modules/.pnpm/nanostores@1.1.0/node_modules/nanostores/atom/index.js","../../src/stores/createStores.ts"],"sourcesContent":["import { cleanTasks } from '../task/index.js'\n\nexport let clean = Symbol('clean')\n\nexport let cleanStores = (...stores) => {\n if (process.env.NODE_ENV === 'production') {\n throw new Error(\n 'cleanStores() can be used only during development or tests'\n )\n }\n cleanTasks()\n for (let $store of stores) {\n if ($store) {\n if ($store.mocked) delete $store.mocked\n if ($store[clean]) $store[clean]()\n }\n }\n}\n","import { clean } from '../clean-stores/index.js'\n\nlet listenerQueue = []\nlet lqIndex = 0\nconst QUEUE_ITEMS_PER_LISTENER = 4\nexport let epoch = 0\n\n/* @__NO_SIDE_EFFECTS__ */\nexport const atom = initialValue => {\n let listeners = []\n let $atom = {\n get() {\n if (!$atom.lc) {\n $atom.listen(() => {})()\n }\n return $atom.value\n },\n lc: 0,\n listen(listener) {\n $atom.lc = listeners.push(listener)\n\n return () => {\n for (\n let i = lqIndex + QUEUE_ITEMS_PER_LISTENER;\n i < listenerQueue.length;\n\n ) {\n if (listenerQueue[i] === listener) {\n listenerQueue.splice(i, QUEUE_ITEMS_PER_LISTENER)\n } else {\n i += QUEUE_ITEMS_PER_LISTENER\n }\n }\n\n let index = listeners.indexOf(listener)\n if (~index) {\n listeners.splice(index, 1)\n if (!--$atom.lc) $atom.off()\n }\n }\n },\n notify(oldValue, changedKey) {\n epoch++\n let runListenerQueue = !listenerQueue.length\n for (let listener of listeners) {\n listenerQueue.push(listener, $atom.value, oldValue, changedKey)\n }\n\n if (runListenerQueue) {\n for (\n lqIndex = 0;\n lqIndex < listenerQueue.length;\n lqIndex += QUEUE_ITEMS_PER_LISTENER\n ) {\n listenerQueue[lqIndex](\n listenerQueue[lqIndex + 1],\n listenerQueue[lqIndex + 2],\n listenerQueue[lqIndex + 3]\n )\n }\n listenerQueue.length = 0\n }\n },\n /* It will be called on last listener unsubscribing.\n We will redefine it in onMount and onStop. */\n off() {},\n set(newValue) {\n let oldValue = $atom.value\n if (oldValue !== newValue) {\n $atom.value = newValue\n $atom.notify(oldValue)\n }\n },\n subscribe(listener) {\n let unbind = $atom.listen(listener)\n listener($atom.value)\n return unbind\n },\n value: initialValue\n }\n\n if (process.env.NODE_ENV !== 'production') {\n $atom[clean] = () => {\n listeners = []\n $atom.lc = 0\n $atom.off()\n }\n }\n\n return $atom\n}\n\nexport const readonlyType = store => store\n","/**\n * Store Factory\n *\n * Creates request-scoped nanostores for sharing data across Vue islands\n * without props. Works with @inox-tools/request-nanostores for Astro integration.\n */\n\nimport { atom, type WritableAtom } from \"nanostores\";\n\n/**\n * Type for the shared function from @it-astro:request-nanostores\n */\ntype SharedFn = <T>(key: string, atomFactory: WritableAtom<T>) => WritableAtom<T>;\n\n/**\n * Configuration for a resource store\n */\nexport interface StoreConfig {\n\t/** Resource name (e.g., 'accounts', 'users') */\n\tname: string;\n\t/** Related resources to create stores for (e.g., ['brands', 'products']) */\n\trelations?: string[];\n}\n\n/**\n * Result type for created stores\n */\nexport type ResourceStores<T, TRelations extends string = never> = {\n\t/** Main resource store */\n\t$data: WritableAtom<readonly T[]>;\n} & {\n\t[K in TRelations as `$${K}`]: WritableAtom<readonly any[]>;\n};\n\n/**\n * Capitalize first letter of a string\n */\nfunction capitalize(str: string): string {\n\treturn str.charAt(0).toUpperCase() + str.slice(1);\n}\n\n/**\n * Creates request-scoped stores for a resource and its relations.\n *\n * This factory is designed to work with Astro's request-nanostores integration,\n * providing request-isolated state that syncs from server to client.\n *\n * @param shared - The shared function from @it-astro:request-nanostores\n * @param config - Store configuration\n * @returns Object containing the main store and relation stores\n *\n * @example\n * ```typescript\n * // In your store file (e.g., stores/accounts.ts)\n * import { shared } from '@it-astro:request-nanostores';\n * import { createResourceStores } from '@htlkg/data/stores';\n *\n * export const accountStores = createResourceStores(shared, {\n * name: 'accounts',\n * relations: ['brands'],\n * });\n *\n * // This creates:\n * // - accountStores.$data (WritableAtom<any[]>) - main accounts store\n * // - accountStores.$brands (WritableAtom<any[]>) - related brands store\n *\n * // For cleaner exports:\n * export const $accounts = accountStores.$data;\n * export const $accountsBrands = accountStores.$brands;\n * ```\n *\n * @example\n * ```typescript\n * // With typed stores\n * interface Account { id: number; name: string; }\n * interface Brand { id: number; name: string; }\n *\n * export const { $data: $accounts, $brands } = createResourceStores<Account, 'brands'>(\n * shared,\n * { name: 'accounts', relations: ['brands'] }\n * );\n * ```\n */\nexport function createResourceStores<T = any, TRelations extends string = never>(\n\tshared: SharedFn,\n\tconfig: StoreConfig & { relations?: TRelations[] },\n): ResourceStores<T, TRelations> {\n\tconst { name, relations = [] } = config;\n\n\tconst stores: Record<string, any> = {};\n\n\t// Main resource store\n\tstores.$data = shared(name, atom<T[]>([]));\n\n\t// Relation stores\n\tfor (const relation of relations) {\n\t\tconst storeKey = `$${relation}`;\n\t\tconst sharedKey = `${name}${capitalize(relation)}`;\n\t\tstores[storeKey] = shared(sharedKey, atom<any[]>([]));\n\t}\n\n\treturn stores as ResourceStores<T, TRelations>;\n}\n\n/**\n * Creates a simple single store (non-factory version for simpler use cases)\n *\n * @example\n * ```typescript\n * import { shared } from '@it-astro:request-nanostores';\n * import { createStore } from '@htlkg/data/stores';\n *\n * export const $currentUser = createStore<User>(shared, 'currentUser');\n * ```\n */\nexport function createStore<T>(\n\tshared: SharedFn,\n\tname: string,\n\tdefaultValue: T[] = [],\n): WritableAtom<readonly T[]> {\n\treturn shared(name, atom<T[]>(defaultValue));\n}\n\n/**\n * Creates a single-value store (for non-array values like current user)\n *\n * @example\n * ```typescript\n * import { shared } from '@it-astro:request-nanostores';\n * import { createSingleStore } from '@htlkg/data/stores';\n *\n * export const $currentUser = createSingleStore<User | null>(shared, 'currentUser', null);\n * ```\n */\nexport function createSingleStore<T>(\n\tshared: SharedFn,\n\tname: string,\n\tdefaultValue: T,\n): WritableAtom<T> {\n\treturn shared(name, atom<T>(defaultValue));\n}\n\n/**\n * Helper to create typed store with proper inference\n */\nexport type InferStoreType<TStore extends WritableAtom<any>> = TStore extends WritableAtom<infer T>\n\t? T\n\t: never;\n"],"mappings":";AAEO,IAAI,QAAQ,uBAAO,OAAO;;;ACAjC,IAAI,gBAAgB,CAAC;AACrB,IAAI,UAAU;AACd,IAAM,2BAA2B;AAC1B,IAAI,QAAQ;AAGZ,IAAM,kCAAO,kBAAgB;AAClC,MAAI,YAAY,CAAC;AACjB,MAAI,QAAQ;AAAA,IACV,MAAM;AACJ,UAAI,CAAC,MAAM,IAAI;AACb,cAAM,OAAO,MAAM;AAAA,QAAC,CAAC,EAAE;AAAA,MACzB;AACA,aAAO,MAAM;AAAA,IACf;AAAA,IACA,IAAI;AAAA,IACJ,OAAO,UAAU;AACf,YAAM,KAAK,UAAU,KAAK,QAAQ;AAElC,aAAO,MAAM;AACX,iBACM,IAAI,UAAU,0BAClB,IAAI,cAAc,UAElB;AACA,cAAI,cAAc,CAAC,MAAM,UAAU;AACjC,0BAAc,OAAO,GAAG,wBAAwB;AAAA,UAClD,OAAO;AACL,iBAAK;AAAA,UACP;AAAA,QACF;AAEA,YAAI,QAAQ,UAAU,QAAQ,QAAQ;AACtC,YAAI,CAAC,OAAO;AACV,oBAAU,OAAO,OAAO,CAAC;AACzB,cAAI,CAAC,EAAE,MAAM,GAAI,OAAM,IAAI;AAAA,QAC7B;AAAA,MACF;AAAA,IACF;AAAA,IACA,OAAO,UAAU,YAAY;AAC3B;AACA,UAAI,mBAAmB,CAAC,cAAc;AACtC,eAAS,YAAY,WAAW;AAC9B,sBAAc,KAAK,UAAU,MAAM,OAAO,UAAU,UAAU;AAAA,MAChE;AAEA,UAAI,kBAAkB;AACpB,aACE,UAAU,GACV,UAAU,cAAc,QACxB,WAAW,0BACX;AACA,wBAAc,OAAO;AAAA,YACnB,cAAc,UAAU,CAAC;AAAA,YACzB,cAAc,UAAU,CAAC;AAAA,YACzB,cAAc,UAAU,CAAC;AAAA,UAC3B;AAAA,QACF;AACA,sBAAc,SAAS;AAAA,MACzB;AAAA,IACF;AAAA;AAAA;AAAA,IAGA,MAAM;AAAA,IAAC;AAAA,IACP,IAAI,UAAU;AACZ,UAAI,WAAW,MAAM;AACrB,UAAI,aAAa,UAAU;AACzB,cAAM,QAAQ;AACd,cAAM,OAAO,QAAQ;AAAA,MACvB;AAAA,IACF;AAAA,IACA,UAAU,UAAU;AAClB,UAAI,SAAS,MAAM,OAAO,QAAQ;AAClC,eAAS,MAAM,KAAK;AACpB,aAAO;AAAA,IACT;AAAA,IACA,OAAO;AAAA,EACT;AAEA,MAAI,QAAQ,IAAI,aAAa,cAAc;AACzC,UAAM,KAAK,IAAI,MAAM;AACnB,kBAAY,CAAC;AACb,YAAM,KAAK;AACX,YAAM,IAAI;AAAA,IACZ;AAAA,EACF;AAEA,SAAO;AACT;;;ACrDA,SAAS,WAAW,KAAqB;AACxC,SAAO,IAAI,OAAO,CAAC,EAAE,YAAY,IAAI,IAAI,MAAM,CAAC;AACjD;AA4CO,SAAS,qBACf,QACA,QACgC;AAChC,QAAM,EAAE,MAAM,YAAY,CAAC,EAAE,IAAI;AAEjC,QAAM,SAA8B,CAAC;AAGrC,SAAO,QAAQ,OAAO,MAAM,KAAU,CAAC,CAAC,CAAC;AAGzC,aAAW,YAAY,WAAW;AACjC,UAAM,WAAW,IAAI,QAAQ;AAC7B,UAAM,YAAY,GAAG,IAAI,GAAG,WAAW,QAAQ,CAAC;AAChD,WAAO,QAAQ,IAAI,OAAO,WAAW,KAAY,CAAC,CAAC,CAAC;AAAA,EACrD;AAEA,SAAO;AACR;AAaO,SAAS,YACf,QACA,MACA,eAAoB,CAAC,GACQ;AAC7B,SAAO,OAAO,MAAM,KAAU,YAAY,CAAC;AAC5C;AAaO,SAAS,kBACf,QACA,MACA,cACkB;AAClB,SAAO,OAAO,MAAM,KAAQ,YAAY,CAAC;AAC1C;","names":[]}
package/package.json CHANGED
@@ -1,38 +1,65 @@
1
1
  {
2
- "name": "@htlkg/data",
3
- "version": "0.0.2",
4
- "type": "module",
5
- "exports": {
6
- ".": "./dist/index.js",
7
- "./client": "./dist/client/index.js",
8
- "./queries": "./dist/queries/index.js",
9
- "./mutations": "./dist/mutations/index.js",
10
- "./hooks": "./dist/hooks/index.js",
11
- "./content-collections": "./dist/content-collections/index.js"
12
- },
13
- "files": [
14
- "dist"
15
- ],
16
- "dependencies": {
17
- "@aws-amplify/api": "^6.0.0",
18
- "vue": "^3.5.22",
19
- "zod": "^3.22.0",
20
- "@htlkg/core": "0.0.2"
21
- },
22
- "publishConfig": {
23
- "access": "restricted"
24
- },
25
- "devDependencies": {
26
- "astro": "^5.14.7",
27
- "aws-amplify": "^6.15.7",
28
- "tsup": "^8.0.0",
29
- "typescript": "^5.9.2",
30
- "vitest": "^3.2.4"
31
- },
32
- "scripts": {
33
- "build": "tsup",
34
- "dev": "tsup --watch",
35
- "test": "vitest run",
36
- "test:watch": "vitest"
37
- }
38
- }
2
+ "name": "@htlkg/data",
3
+ "version": "0.0.9",
4
+ "type": "module",
5
+ "exports": {
6
+ ".": {
7
+ "import": "./dist/index.js",
8
+ "types": "./dist/index.d.ts"
9
+ },
10
+ "./client": {
11
+ "import": "./dist/client/index.js",
12
+ "types": "./dist/client/index.d.ts"
13
+ },
14
+ "./queries": {
15
+ "import": "./dist/queries/index.js",
16
+ "types": "./dist/queries/index.d.ts"
17
+ },
18
+ "./mutations": {
19
+ "import": "./dist/mutations/index.js",
20
+ "types": "./dist/mutations/index.d.ts"
21
+ },
22
+ "./hooks": {
23
+ "import": "./dist/hooks/index.js",
24
+ "types": "./dist/hooks/index.d.ts"
25
+ },
26
+ "./stores": {
27
+ "import": "./dist/stores/index.js",
28
+ "types": "./dist/stores/index.d.ts"
29
+ },
30
+ "./content-collections": {
31
+ "import": "./dist/content-collections/index.js",
32
+ "types": "./dist/content-collections/index.d.ts"
33
+ }
34
+ },
35
+ "files": [
36
+ "src",
37
+ "dist"
38
+ ],
39
+ "scripts": {
40
+ "build": "tsup",
41
+ "dev": "tsup --watch",
42
+ "test": "vitest run",
43
+ "test:watch": "vitest",
44
+ "prepublishOnly": "pnpm build",
45
+ "version:patch": "pnpm version patch --no-git-tag-version",
46
+ "version:minor": "pnpm version minor --no-git-tag-version",
47
+ "version:major": "pnpm version major --no-git-tag-version"
48
+ },
49
+ "dependencies": {
50
+ "@aws-amplify/api": "^6.0.0",
51
+ "@htlkg/core": "^0.0.9",
52
+ "vue": "^3.5.22",
53
+ "zod": "^3.22.0"
54
+ },
55
+ "publishConfig": {
56
+ "access": "public"
57
+ },
58
+ "devDependencies": {
59
+ "astro": "^5.14.7",
60
+ "aws-amplify": "^6.15.7",
61
+ "tsup": "^8.0.0",
62
+ "typescript": "^5.9.2",
63
+ "vitest": "^3.2.4"
64
+ }
65
+ }
@@ -0,0 +1,106 @@
1
+ import { describe, it, expect, vi, beforeEach } from 'vitest';
2
+ import { generateServerClientUsingCookies } from '../server';
3
+ import type { ResourcesConfig } from 'aws-amplify';
4
+
5
+ // Mock dependencies
6
+ vi.mock('@htlkg/core/amplify-astro-adapter', () => ({
7
+ createRunWithAmplifyServerContext: vi.fn(() => vi.fn()),
8
+ createLogger: vi.fn(() => ({
9
+ debug: vi.fn(),
10
+ info: vi.fn(),
11
+ warn: vi.fn(),
12
+ error: vi.fn(),
13
+ })),
14
+ }));
15
+
16
+ vi.mock('aws-amplify/adapter-core/internals', () => ({
17
+ getAmplifyServerContext: vi.fn(),
18
+ }));
19
+
20
+ vi.mock('aws-amplify/api/internals', () => ({
21
+ generateClientWithAmplifyInstance: vi.fn(() => ({
22
+ graphql: vi.fn(),
23
+ models: {},
24
+ })),
25
+ }));
26
+
27
+ const mockAmplifyConfig: ResourcesConfig = {
28
+ Auth: {
29
+ Cognito: {
30
+ identityPoolId: '123',
31
+ userPoolId: 'abc',
32
+ userPoolClientId: 'def',
33
+ },
34
+ },
35
+ API: {
36
+ GraphQL: {
37
+ defaultAuthMode: 'apiKey',
38
+ apiKey: 'FAKE-KEY',
39
+ endpoint: 'https://localhost/graphql',
40
+ region: 'local-host',
41
+ },
42
+ },
43
+ };
44
+
45
+ describe('generateServerClientUsingCookies', () => {
46
+ const mockCookies = {
47
+ get: vi.fn(),
48
+ set: vi.fn(),
49
+ delete: vi.fn(),
50
+ };
51
+
52
+ const mockRequest = new Request('https://example.com');
53
+
54
+ beforeEach(() => {
55
+ vi.clearAllMocks();
56
+ });
57
+
58
+ it('should create a client with the provided config', () => {
59
+ const client = generateServerClientUsingCookies({
60
+ config: mockAmplifyConfig,
61
+ cookies: mockCookies as any,
62
+ request: mockRequest,
63
+ });
64
+
65
+ expect(client).toBeDefined();
66
+ expect(client.graphql).toBeDefined();
67
+ });
68
+
69
+ it('should accept authMode option', () => {
70
+ const client = generateServerClientUsingCookies({
71
+ config: mockAmplifyConfig,
72
+ cookies: mockCookies as any,
73
+ request: mockRequest,
74
+ authMode: 'userPool',
75
+ });
76
+
77
+ expect(client).toBeDefined();
78
+ });
79
+
80
+ it('should accept apiKey option', () => {
81
+ const client = generateServerClientUsingCookies({
82
+ config: mockAmplifyConfig,
83
+ cookies: mockCookies as any,
84
+ request: mockRequest,
85
+ authMode: 'apiKey',
86
+ apiKey: 'custom-api-key',
87
+ });
88
+
89
+ expect(client).toBeDefined();
90
+ });
91
+
92
+ it('should accept headers option', () => {
93
+ const customHeaders = {
94
+ 'X-Custom-Header': 'value',
95
+ };
96
+
97
+ const client = generateServerClientUsingCookies({
98
+ config: mockAmplifyConfig,
99
+ cookies: mockCookies as any,
100
+ request: mockRequest,
101
+ headers: customHeaders,
102
+ });
103
+
104
+ expect(client).toBeDefined();
105
+ });
106
+ });
@@ -0,0 +1,91 @@
1
+ # Client Module
2
+
3
+ GraphQL client generation for client-side and server-side data fetching.
4
+
5
+ ## Installation
6
+
7
+ ```typescript
8
+ import {
9
+ generateClient,
10
+ generateServerClient,
11
+ getSharedClient,
12
+ resetSharedClient,
13
+ } from '@htlkg/data/client';
14
+ ```
15
+
16
+ ## Client-Side
17
+
18
+ ### getSharedClient
19
+
20
+ Singleton client for client-side fetching. Avoids creating multiple instances.
21
+
22
+ ```typescript
23
+ import { getSharedClient } from '@htlkg/data/client';
24
+ import type { Schema } from '@backend/data/resource';
25
+
26
+ const client = getSharedClient<Schema>();
27
+ const { data } = await client.models.Account.list();
28
+ ```
29
+
30
+ ### generateClient
31
+
32
+ Create a new client instance. Use in component setup or after hydration.
33
+
34
+ ```typescript
35
+ import { generateClient } from '@htlkg/data/client';
36
+
37
+ const client = generateClient<Schema>();
38
+ const { data: brands } = await client.models.Brand.list();
39
+ ```
40
+
41
+ ### resetSharedClient
42
+
43
+ Reset the singleton (useful for auth state changes or testing).
44
+
45
+ ```typescript
46
+ import { resetSharedClient } from '@htlkg/data/client';
47
+
48
+ // After logout
49
+ resetSharedClient();
50
+ ```
51
+
52
+ ## Server-Side
53
+
54
+ ### generateServerClient
55
+
56
+ Create a server-side client for SSR. Must be called inside `runWithAmplifyServerContext`.
57
+
58
+ ```typescript
59
+ import { generateServerClient } from '@htlkg/data/client';
60
+ import { createRunWithAmplifyServerContext } from '@htlkg/core/amplify-astro-adapter';
61
+
62
+ const runWithAmplifyServerContext = createRunWithAmplifyServerContext({
63
+ config: amplifyConfig
64
+ });
65
+
66
+ const result = await runWithAmplifyServerContext({
67
+ astroServerContext: {
68
+ cookies: Astro.cookies,
69
+ request: Astro.request,
70
+ },
71
+ operation: async (contextSpec) => {
72
+ const client = generateServerClient<Schema>();
73
+ return await client.models.User.list();
74
+ },
75
+ });
76
+ ```
77
+
78
+ ## Auth Modes
79
+
80
+ | Mode | Description |
81
+ |------|-------------|
82
+ | `userPool` | Uses JWT from cookies (default) |
83
+ | `apiKey` | Uses API key for public requests |
84
+
85
+ ```typescript
86
+ // Authenticated request
87
+ const client = generateServerClient<Schema>({ authMode: 'userPool' });
88
+
89
+ // Public request
90
+ const client = generateServerClient<Schema>({ authMode: 'apiKey' });
91
+ ```