@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.
- package/README.md +53 -0
- package/dist/client/index.d.ts +16 -1
- package/dist/client/index.js +13 -1
- package/dist/client/index.js.map +1 -1
- package/dist/hooks/index.d.ts +113 -5
- package/dist/hooks/index.js +155 -164
- package/dist/hooks/index.js.map +1 -1
- package/dist/index.d.ts +4 -2
- package/dist/index.js +263 -161
- package/dist/index.js.map +1 -1
- package/dist/queries/index.js.map +1 -1
- package/dist/stores/index.d.ts +106 -0
- package/dist/stores/index.js +108 -0
- package/dist/stores/index.js.map +1 -0
- package/package.json +64 -37
- package/src/client/__tests__/server.test.ts +106 -0
- package/src/client/client.md +91 -0
- package/src/client/index.test.ts +198 -0
- package/src/client/index.ts +145 -0
- package/src/client/server.ts +118 -0
- package/src/content-collections/content-collections.md +87 -0
- package/src/content-collections/generator.ts +314 -0
- package/src/content-collections/index.ts +32 -0
- package/src/content-collections/schemas.ts +75 -0
- package/src/content-collections/sync.ts +139 -0
- package/src/hooks/README.md +293 -0
- package/src/hooks/createDataHook.ts +208 -0
- package/src/hooks/data-hook-errors.property.test.ts +270 -0
- package/src/hooks/data-hook-filters.property.test.ts +263 -0
- package/src/hooks/data-hooks.property.test.ts +190 -0
- package/src/hooks/hooks.test.ts +76 -0
- package/src/hooks/index.ts +21 -0
- package/src/hooks/useAccounts.ts +66 -0
- package/src/hooks/useBrands.ts +95 -0
- package/src/hooks/useProducts.ts +88 -0
- package/src/hooks/useUsers.ts +89 -0
- package/src/index.ts +32 -0
- package/src/mutations/accounts.ts +127 -0
- package/src/mutations/brands.ts +133 -0
- package/src/mutations/index.ts +32 -0
- package/src/mutations/mutations.md +96 -0
- package/src/mutations/users.ts +136 -0
- package/src/queries/accounts.ts +121 -0
- package/src/queries/brands.ts +176 -0
- package/src/queries/index.ts +45 -0
- package/src/queries/products.ts +282 -0
- package/src/queries/queries.md +88 -0
- package/src/queries/server-helpers.ts +114 -0
- package/src/queries/users.ts +199 -0
- package/src/stores/createStores.ts +148 -0
- package/src/stores/index.ts +15 -0
- 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
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
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
|
+
```
|