@storecraft/sdk 0.1.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.
@@ -0,0 +1,281 @@
1
+
2
+ import {
3
+ api_query_to_searchparams
4
+ } from '@storecraft/core/v-api/utils.query.js';
5
+ import { assert } from './utils.functional.js';
6
+
7
+
8
+ /**
9
+ *
10
+ * @param {import("../index.js").StorecraftSDKConfig} config
11
+ * @param {string} path
12
+ */
13
+ export const url = (config, path) => {
14
+ let base = config?.endpoint?.trim();
15
+
16
+ base = base?.endsWith('/') ? base.slice(0, -1) : base;
17
+ path = path?.startsWith('/') ? path.slice(1) : path;
18
+
19
+ return base ? `${base}/api/${path}` : `/api/${path}`;
20
+ }
21
+
22
+
23
+ /**
24
+ * - Prepends `backend` endpoint.
25
+ * - Fetches with `authentication` middleware.
26
+ * - Refreshed `auth` if needed.
27
+ *
28
+ * @param {import('../index.js').StorecraftSDK} sdk
29
+ * @param {string} path relative path in api
30
+ * @param {RequestInit} [init] request `init` type
31
+ *
32
+ * @returns {Promise<Response>}
33
+ */
34
+ export const fetchOnlyApiResponseWithAuth = async (sdk, path, init={}) => {
35
+
36
+ const auth_token = await sdk.auth.working_auth_token();
37
+ const auth_header_value = (
38
+ sdk.auth.authStrategy==='apikey' ? 'Basic' : 'Bearer'
39
+ ) + ` ${auth_token}`;
40
+
41
+
42
+ const response = await fetch(
43
+ url(sdk.config, path),
44
+ {
45
+ ...init,
46
+ headers: {
47
+ ...(init?.headers ?? {}),
48
+ 'Authorization': auth_header_value
49
+ }
50
+ }
51
+ );
52
+
53
+ return response;
54
+
55
+ // const auth_problem = response.status >= 400 && response.status < 500;
56
+
57
+ // if(auth_problem) {
58
+
59
+ // }
60
+ }
61
+
62
+
63
+ /**
64
+ * - Prepends `backend` endpoint.
65
+ * - Fetches with `authentication` middleware.
66
+ * - Refreshed `auth` if needed.
67
+ * - Throws a `json` representation of the `error`,
68
+ * if the request is `bad`
69
+ *
70
+ * @template {any} R
71
+ *
72
+ * @param {import('../index.js').StorecraftSDK} sdk
73
+ * @param {string} path relative path in api
74
+ * @param {RequestInit} [init] request `init` type
75
+ *
76
+ *
77
+ * @throws {import('@storecraft/core/v-api').error}
78
+ *
79
+ * @returns {Promise<R>}
80
+ */
81
+ export const fetchApiWithAuth = async (sdk, path, init={}) => {
82
+
83
+ const response = await fetchOnlyApiResponseWithAuth(
84
+ sdk, path, init
85
+ );
86
+
87
+ // console.log('fetchApiWithAuth::response', response)
88
+
89
+ const isok = response.ok;
90
+ let payload = undefined;
91
+
92
+ try {
93
+ payload = await response.json();
94
+ } catch(e) {
95
+ console.log('fetchApiWithAuth.json()', e)
96
+ }
97
+
98
+ if(!isok)
99
+ throw payload;
100
+
101
+ return payload;
102
+ }
103
+
104
+
105
+ /**
106
+ * @template {any} G Get type
107
+ *
108
+ *
109
+ * @param {import('../index.js').StorecraftSDK} sdk
110
+ * @param {string} handle_or_id `handle` or `id`
111
+ * @param {string} resource base path of resource
112
+ *
113
+ *
114
+ * @returns {Promise<G>}
115
+ */
116
+ export async function get(sdk, resource, handle_or_id) {
117
+ return fetchApiWithAuth(
118
+ sdk,
119
+ `${resource}/${handle_or_id}`,
120
+ {
121
+ method: 'get'
122
+ }
123
+ );
124
+ }
125
+
126
+
127
+ /**
128
+ *
129
+ * @template {any} U the upsert type
130
+ *
131
+ *
132
+ * @param {import('../index.js').StorecraftSDK} sdk
133
+ * @param {string} resource base path of resource
134
+ * @param {U} item Item to upsert
135
+ *
136
+ *
137
+ * @returns {Promise<string>} id
138
+ */
139
+ export async function upsert(sdk, resource, item) {
140
+ return fetchApiWithAuth(
141
+ sdk,
142
+ `${resource}`,
143
+ {
144
+ method: 'post',
145
+ body: JSON.stringify(item),
146
+ headers: {
147
+ 'Content-Type': 'application/json'
148
+ }
149
+ }
150
+ );
151
+ }
152
+
153
+
154
+ /**
155
+ *
156
+ * @param {import('../index.js').StorecraftSDK} sdk
157
+ * @param {string} resource base path of resource
158
+ * @param {string} handle_or_id `handle` or `id`
159
+ *
160
+ *
161
+ * @returns {Promise<boolean>}
162
+ */
163
+ export async function remove(sdk, resource, handle_or_id) {
164
+ return fetchApiWithAuth(
165
+ sdk,
166
+ `${resource}/${handle_or_id}`,
167
+ {
168
+ method: 'delete'
169
+ }
170
+ );
171
+ }
172
+
173
+
174
+ /**
175
+ * @description Invoke `api` endpoint that requires use of `query`
176
+ * object. I named it `list` because it usually entails list op.
177
+ *
178
+ *
179
+ * @template {any} G Get type
180
+ *
181
+ *
182
+ * @param {import('../index.js').StorecraftSDK} sdk
183
+ * @param {string} resource base path of resource
184
+ * @param {import('@storecraft/core/v-api').ApiQuery} [query]
185
+ *
186
+ *
187
+ * @returns {Promise<G[]>}
188
+ */
189
+ export async function list(sdk, resource, query={}) {
190
+ const sq = api_query_to_searchparams(query);
191
+
192
+ // console.log('sq', sq.toString())
193
+ return fetchApiWithAuth(
194
+ sdk,
195
+ `${resource}?${sq.toString()}`,
196
+ {
197
+ method: 'get'
198
+ }
199
+ );
200
+ }
201
+
202
+
203
+ /**
204
+ * A simple resource base `class` with `CRUD` helpers
205
+ *
206
+ *
207
+ * @template {any} U upsert type
208
+ * @template {any} G get type
209
+ */
210
+ export class collection_base {
211
+
212
+ /** @type {import('../index.js').StorecraftSDK} */
213
+ #sdk = undefined;
214
+
215
+ /** @type {string} */
216
+ #base_name = undefined;
217
+
218
+ /**
219
+ *
220
+ * @param {import('../index.js').StorecraftSDK} sdk storecraft sdk
221
+ * @param {string} base_name base name of resource type
222
+ */
223
+ constructor(sdk, base_name) {
224
+ this.#sdk = sdk;
225
+ this.#base_name = base_name;
226
+ }
227
+
228
+
229
+ /**
230
+ *
231
+ * @param {string} handle_or_id `handle` or `id`
232
+ *
233
+ *
234
+ * @returns {Promise<G>}
235
+ */
236
+ async get(handle_or_id) {
237
+ return get(this.sdk, this.base_name, handle_or_id);
238
+ }
239
+
240
+ /**
241
+ *
242
+ * @param {U} item Item to upsert
243
+ *
244
+ *
245
+ * @returns {Promise<string>} id
246
+ */
247
+ async upsert(item) {
248
+ return upsert(this.sdk, this.base_name, item);
249
+ }
250
+
251
+ /**
252
+ *
253
+ * @param {string} handle_or_id `handle` or `id`
254
+ *
255
+ *
256
+ * @returns {Promise<boolean>}
257
+ */
258
+ async remove(handle_or_id) {
259
+ return remove(this.sdk, this.base_name, handle_or_id);
260
+ }
261
+
262
+ /**
263
+ *
264
+ * @param {import('@storecraft/core/v-api').ApiQuery} query Query object
265
+ *
266
+ *
267
+ * @returns {Promise<G[]>}
268
+ */
269
+ async list(query) {
270
+ return list(this.sdk, this.base_name, query);
271
+ }
272
+
273
+ get base_name() {
274
+ return this.#base_name;
275
+ }
276
+
277
+ get sdk() {
278
+ return this.#sdk;
279
+ }
280
+
281
+ }
@@ -0,0 +1,141 @@
1
+ /**
2
+ * @param {...any} fields
3
+ */
4
+ export const select_fields = (...fields) => {
5
+ /**
6
+ * @param {object} o
7
+ */
8
+ return o => fields.reduce((p, c) => ({ ...p, [c] : o[c] }), {});
9
+ }
10
+
11
+ /**
12
+ *
13
+ * @param {...any} fields
14
+ */
15
+ export const filter_fields = (...fields) => {
16
+ /**
17
+ * @param {any[]} items
18
+ */
19
+ return items => items.map(item => select_fields(...fields)(item))
20
+ }
21
+
22
+ /**
23
+ *
24
+ * @param {object} o
25
+ */
26
+ export const select_unused_fields = o => Object.keys(o).reduce((p, c) => {
27
+ if(Array.isArray(o[c])) {
28
+ if(o[c].length) p[c]=o[c]
29
+ }
30
+ else if(typeof o[c]!=='undefined')
31
+ p[c]=o[c]
32
+ return p
33
+ }, {});
34
+
35
+ /**
36
+ *
37
+ * @param {any[]} items
38
+ */
39
+ export const filter_unused = items =>
40
+ items.map(item => select_unused_fields(item));
41
+
42
+ /**
43
+ * @param {...string} keys
44
+ */
45
+ export const delete_keys = (...keys) => {
46
+ /**
47
+ * @param {any} o
48
+ */
49
+ return o => {
50
+ o = Array.isArray(o) ? o : [o]
51
+ o.forEach(it => keys.forEach(k => delete it[k] ))
52
+ return o
53
+ }
54
+ }
55
+
56
+
57
+ /**
58
+ *
59
+ * @param {string} text
60
+ */
61
+ export const text2tokens_unsafe = (text) => {
62
+ return text?.toString().toLowerCase().match(/\S+/g)
63
+ }
64
+
65
+ export const STOP_WORDS = [
66
+ 'i','me','my','myself','we','our','ours','ourselves','you',
67
+ 'your','yours','yourself','yourselves','he','him','his','himself',
68
+ 'she','her','hers','herself','it','its','itself','they','them',
69
+ 'their','theirs','themselves','what','which','who','whom','this',
70
+ 'that','these','those','am','is','are','was','were','be','been',
71
+ 'being','have','has','had','having','do','does','did','doing','a',
72
+ 'an','the','and','but','if','or','because','as','until','while',
73
+ 'of','at','by','for','with','about','against','between','into',
74
+ 'through','during','before','after','above','below','to','from','up',
75
+ 'down','in','out','on','off','over','under','again','further','then','once',
76
+ 'here','there','when','where','why','how','all','any','both','each','few',
77
+ 'more','most','other','some','such','no','nor','not','only','own','same',
78
+ 'so','than','too','very','s','t','can','will','just','don','should','now'
79
+ ]
80
+
81
+ /**
82
+ *
83
+ * @param {string} text
84
+ * @returns {string[] | undefined}
85
+ */
86
+ export const text2tokens = (text) => {
87
+ // match can return undefined
88
+ // let tokens = text?.toString().toLowerCase().match(/\S+/g)
89
+ // let tokens = text?.toString().toLowerCase().match(/[^\W_]+/g)
90
+ let tokens = text?.toString().toLowerCase().match(/[\p{L}\d]+/gu)
91
+
92
+ tokens = tokens ?? []
93
+ tokens = tokens.filter(
94
+ t => !STOP_WORDS.includes(t)
95
+ )
96
+
97
+ return tokens
98
+ }
99
+
100
+
101
+ /**
102
+ *
103
+ * @param {any[]} arrA
104
+ * @param {any[]} arrB
105
+ * @returns
106
+ */
107
+ export const union_array = (arrA=[], arrB=[]) => [
108
+ ...new Set([...arrA, ...arrB])
109
+ ];
110
+
111
+ /**
112
+ * @param {string} str
113
+ */
114
+ export const isEmpty = (str) => (!str?.trim().length)
115
+
116
+ /**
117
+ * Transforms a string into a handle
118
+ * @param {string} title
119
+ */
120
+ export const to_handle = (title) => {
121
+ if(typeof title !== 'string')
122
+ return undefined
123
+ let trimmed = title.trim()
124
+ if(trimmed === "")
125
+ return undefined
126
+
127
+ trimmed = trimmed.toLowerCase().match(/[\p{L}\d]+/gu).join('-')
128
+ if(trimmed.length==0)
129
+ return undefined
130
+
131
+ return trimmed
132
+ }
133
+
134
+ /**
135
+ * @param {any} condition
136
+ * @param {string} msg
137
+ */
138
+ export const assert = (condition, msg) => {
139
+ if(!Boolean(condition))
140
+ throw msg
141
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,29 @@
1
+ {
2
+ "compileOnSave": false,
3
+ "compilerOptions": {
4
+ "noEmit": true,
5
+ "allowJs": true,
6
+ "checkJs": true,
7
+ "resolveJsonModule": true,
8
+ "target": "ESNext",
9
+ "module": "NodeNext",
10
+ "moduleResolution": "NodeNext",
11
+ "esModuleInterop": true,
12
+ "lib": [
13
+ "dom",
14
+ "dom.iterable",
15
+ "esnext"
16
+ ],
17
+ "skipLibCheck": true,
18
+ "strict": false,
19
+ "incremental": true,
20
+ "isolatedModules": true
21
+ },
22
+ "include": [
23
+ "*",
24
+ "**/*"
25
+ ],
26
+ "exclude": [
27
+ "**/*.json"
28
+ ]
29
+ }
package/types.d.ts ADDED
@@ -0,0 +1 @@
1
+ export * from './index.js'