@fjell/express-router 4.4.3 → 4.4.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.
- package/commit.sh +8 -0
- package/dist/CItemRouter.cjs +10 -15
- package/dist/CItemRouter.cjs.map +1 -1
- package/dist/CItemRouter.d.ts +1 -1
- package/dist/CItemRouter.js +10 -15
- package/dist/CItemRouter.js.map +1 -1
- package/dist/ItemRouter.cjs +139 -48
- package/dist/ItemRouter.cjs.map +1 -1
- package/dist/ItemRouter.d.ts +8 -6
- package/dist/ItemRouter.js +139 -48
- package/dist/ItemRouter.js.map +1 -1
- package/dist/PItemRouter.cjs +10 -15
- package/dist/PItemRouter.cjs.map +1 -1
- package/dist/PItemRouter.d.ts +1 -1
- package/dist/PItemRouter.js +10 -15
- package/dist/PItemRouter.js.map +1 -1
- package/dist/index.cjs +159 -76
- package/dist/index.cjs.map +1 -1
- package/dist/util/general.d.ts +4 -0
- package/package.json +11 -11
- package/pnpm-workspace.yaml.bak +4 -0
- package/release.sh +89 -0
- package/src/CItemRouter.ts +12 -14
- package/src/ItemRouter.ts +111 -46
- package/src/PItemRouter.ts +11 -12
- package/src/util/general.ts +65 -0
package/src/ItemRouter.ts
CHANGED
|
@@ -3,13 +3,12 @@ import {
|
|
|
3
3
|
cPK,
|
|
4
4
|
Item,
|
|
5
5
|
ItemEvent,
|
|
6
|
-
ItemProperties,
|
|
7
6
|
LocKey,
|
|
8
7
|
LocKeyArray,
|
|
9
8
|
PriKey,
|
|
10
9
|
validatePK
|
|
11
10
|
} from "@fjell/core";
|
|
12
|
-
import {
|
|
11
|
+
import { Instance, NotFoundError } from "@fjell/lib";
|
|
13
12
|
import deepmerge from "deepmerge";
|
|
14
13
|
import { Request, Response, Router } from "express";
|
|
15
14
|
import LibLogger from "./logger";
|
|
@@ -25,14 +24,14 @@ export class ItemRouter<
|
|
|
25
24
|
L5 extends string = never
|
|
26
25
|
> {
|
|
27
26
|
|
|
28
|
-
protected lib:
|
|
27
|
+
protected lib: Instance<Item<S, L1, L2, L3, L4, L5>, S, L1, L2, L3, L4, L5>;
|
|
29
28
|
private keyType: S;
|
|
30
29
|
protected options: ItemRouterOptions;
|
|
31
30
|
private childRouters: Record<string, Router> = {};
|
|
32
|
-
|
|
31
|
+
protected logger;
|
|
33
32
|
|
|
34
33
|
constructor(
|
|
35
|
-
lib:
|
|
34
|
+
lib: Instance<Item<S, L1, L2, L3, L4, L5>, S, L1, L2, L3, L4, L5>,
|
|
36
35
|
keyType: S,
|
|
37
36
|
options: ItemRouterOptions = {}
|
|
38
37
|
) {
|
|
@@ -75,23 +74,74 @@ export class ItemRouter<
|
|
|
75
74
|
throw new Error('Method not implemented in an abstract router');
|
|
76
75
|
}
|
|
77
76
|
|
|
77
|
+
protected postAllAction = async (req: Request, res: Response) => {
|
|
78
|
+
const libOptions = this.lib.definition.options;
|
|
79
|
+
const libOperations = this.lib.operations;
|
|
80
|
+
this.logger.debug('Posting All Action', { query: req?.query, params: req?.params, locals: res?.locals });
|
|
81
|
+
const allActionKey = req.path.substring(req.path.lastIndexOf('/') + 1);
|
|
82
|
+
if (!libOptions.allActions) {
|
|
83
|
+
this.logger.error('Item Actions are not configured');
|
|
84
|
+
res.status(500).json({ error: 'Item Actions are not configured' });
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
const allAction = libOptions.allActions[allActionKey];
|
|
88
|
+
if (!allAction) {
|
|
89
|
+
this.logger.error('All Action is not configured', { allActionKey });
|
|
90
|
+
res.status(500).json({ error: 'Item Action is not configured' });
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
try {
|
|
94
|
+
res.json(await libOperations.allAction(allActionKey, req.body));
|
|
95
|
+
} catch (err: any) {
|
|
96
|
+
this.logger.error('Error in All Action', { message: err?.message, stack: err?.stack });
|
|
97
|
+
res.status(500).json(err);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
protected getAllFacet = async (req: Request, res: Response) => {
|
|
102
|
+
const libOptions = this.lib.definition.options;
|
|
103
|
+
const libOperations = this.lib.operations;
|
|
104
|
+
this.logger.debug('Getting All Facet', { query: req?.query, params: req?.params, locals: res?.locals });
|
|
105
|
+
const facetKey = req.path.substring(req.path.lastIndexOf('/') + 1);
|
|
106
|
+
if (!libOptions.allFacets) {
|
|
107
|
+
this.logger.error('Item Facets are not configured');
|
|
108
|
+
res.status(500).json({ error: 'Item Facets are not configured' });
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
const facet = libOptions.allFacets[facetKey];
|
|
112
|
+
if (!facet) {
|
|
113
|
+
this.logger.error('Item Facet is not configured', { facetKey });
|
|
114
|
+
res.status(500).json({ error: 'Item Facet is not configured' });
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
try {
|
|
118
|
+
const combinedQueryParams = { ...req.query, ...req.params } as Record<string, string | number | boolean | Date | Array<string | number | boolean | Date>>;
|
|
119
|
+
res.json(await libOperations.allFacet(facetKey, combinedQueryParams));
|
|
120
|
+
} catch (err: any) {
|
|
121
|
+
this.logger.error('Error in All Facet', { message: err?.message, stack: err?.stack });
|
|
122
|
+
res.status(500).json(err);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
78
126
|
protected postItemAction = async (req: Request, res: Response) => {
|
|
79
|
-
this.
|
|
127
|
+
const libOptions = this.lib.definition.options;
|
|
128
|
+
const libOperations = this.lib.operations;
|
|
129
|
+
this.logger.debug('Getting Item', { query: req?.query, params: req?.params, locals: res?.locals });
|
|
80
130
|
const ik = this.getIk(res);
|
|
81
131
|
const actionKey = req.path.substring(req.path.lastIndexOf('/') + 1);
|
|
82
|
-
if (!
|
|
132
|
+
if (!libOptions.actions) {
|
|
83
133
|
this.logger.error('Item Actions are not configured');
|
|
84
134
|
res.status(500).json({ error: 'Item Actions are not configured' });
|
|
85
135
|
return;
|
|
86
136
|
}
|
|
87
|
-
const action =
|
|
137
|
+
const action = libOptions.actions[actionKey];
|
|
88
138
|
if (!action) {
|
|
89
139
|
this.logger.error('Item Action is not configured', { actionKey });
|
|
90
140
|
res.status(500).json({ error: 'Item Action is not configured' });
|
|
91
141
|
return;
|
|
92
142
|
}
|
|
93
143
|
try {
|
|
94
|
-
res.json(await
|
|
144
|
+
res.json(await libOperations.action(ik, actionKey, req.body));
|
|
95
145
|
} catch (err: any) {
|
|
96
146
|
this.logger.error('Error in Item Action', { message: err?.message, stack: err?.stack });
|
|
97
147
|
res.status(500).json(err);
|
|
@@ -99,22 +149,25 @@ export class ItemRouter<
|
|
|
99
149
|
}
|
|
100
150
|
|
|
101
151
|
protected getItemFacet = async (req: Request, res: Response) => {
|
|
102
|
-
this.
|
|
152
|
+
const libOptions = this.lib.definition.options;
|
|
153
|
+
const libOperations = this.lib.operations;
|
|
154
|
+
this.logger.debug('Getting Item', { query: req?.query, params: req?.params, locals: res?.locals });
|
|
103
155
|
const ik = this.getIk(res);
|
|
104
156
|
const facetKey = req.path.substring(req.path.lastIndexOf('/') + 1);
|
|
105
|
-
if (!
|
|
157
|
+
if (!libOptions.facets) {
|
|
106
158
|
this.logger.error('Item Facets are not configured');
|
|
107
159
|
res.status(500).json({ error: 'Item Facets are not configured' });
|
|
108
160
|
return;
|
|
109
161
|
}
|
|
110
|
-
const facet =
|
|
162
|
+
const facet = libOptions.facets[facetKey];
|
|
111
163
|
if (!facet) {
|
|
112
164
|
this.logger.error('Item Facet is not configured', { facetKey });
|
|
113
165
|
res.status(500).json({ error: 'Item Facet is not configured' });
|
|
114
166
|
return;
|
|
115
167
|
}
|
|
116
168
|
try {
|
|
117
|
-
|
|
169
|
+
const combinedQueryParams = { ...req.query, ...req.params } as Record<string, string | number | boolean | Date | Array<string | number | boolean | Date>>;
|
|
170
|
+
res.json(await libOperations.facet(ik, facetKey, combinedQueryParams));
|
|
118
171
|
} catch (err: any) {
|
|
119
172
|
this.logger.error('Error in Item Facet', { message: err?.message, stack: err?.stack });
|
|
120
173
|
res.status(500).json(err);
|
|
@@ -122,44 +175,53 @@ export class ItemRouter<
|
|
|
122
175
|
}
|
|
123
176
|
|
|
124
177
|
private configure = (router: Router) => {
|
|
125
|
-
|
|
178
|
+
const libOptions = this.lib.definition.options;
|
|
179
|
+
this.logger.debug('Configuring Router', { pkType: this.getPkType() });
|
|
126
180
|
router.get('/', this.findItems);
|
|
127
181
|
router.post('/', this.createItem);
|
|
128
182
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
183
|
+
this.logger.default('All Actions supplied to Router', { allActions: libOptions.allActions });
|
|
184
|
+
if (libOptions.allActions) {
|
|
185
|
+
Object.keys(libOptions.allActions).forEach((actionKey) => {
|
|
186
|
+
this.logger.debug('Configuring All Action %s', actionKey);
|
|
187
|
+
// TODO: Ok, this is a bit of a hack, but we need to customize the types of the request handlers
|
|
188
|
+
router.post(`/${actionKey}`, this.postAllAction);
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
this.logger.default('All Facets supplied to Router', { allFacets: libOptions.allFacets });
|
|
193
|
+
if (libOptions.allFacets) {
|
|
194
|
+
Object.keys(libOptions.allFacets).forEach((facetKey) => {
|
|
195
|
+
this.logger.debug('Configuring All Facet %s', facetKey);
|
|
196
|
+
// TODO: Ok, this is a bit of a hack, but we need to customize the types of the request handlers
|
|
197
|
+
router.get(`/${facetKey}`, this.getAllFacet);
|
|
198
|
+
});
|
|
199
|
+
}
|
|
138
200
|
|
|
139
201
|
const itemRouter = Router();
|
|
140
202
|
itemRouter.get('/', this.getItem);
|
|
141
203
|
itemRouter.put('/', this.updateItem);
|
|
142
204
|
itemRouter.delete('/', this.deleteItem);
|
|
143
205
|
|
|
144
|
-
this.logger.
|
|
145
|
-
if (
|
|
146
|
-
Object.keys(
|
|
147
|
-
this.logger.
|
|
206
|
+
this.logger.default('Item Actions supplied to Router', { itemActions: libOptions.actions });
|
|
207
|
+
if (libOptions.actions) {
|
|
208
|
+
Object.keys(libOptions.actions).forEach((actionKey) => {
|
|
209
|
+
this.logger.debug('Configuring Item Action %s', actionKey);
|
|
148
210
|
// TODO: Ok, this is a bit of a hack, but we need to customize the types of the request handlers
|
|
149
211
|
itemRouter.post(`/${actionKey}`, this.postItemAction)
|
|
150
212
|
});
|
|
151
213
|
}
|
|
152
214
|
|
|
153
|
-
this.logger.
|
|
154
|
-
if (
|
|
155
|
-
Object.keys(
|
|
156
|
-
this.logger.
|
|
215
|
+
this.logger.default('Item Facets supplied to Router', { itemFacets: libOptions.facets });
|
|
216
|
+
if (libOptions.facets) {
|
|
217
|
+
Object.keys(libOptions.facets).forEach((facetKey) => {
|
|
218
|
+
this.logger.debug('Configuring Item Facet %s', facetKey);
|
|
157
219
|
// TODO: Ok, this is a bit of a hack, but we need to customize the types of the request handlers
|
|
158
220
|
itemRouter.get(`/${facetKey}`, this.getItemFacet)
|
|
159
221
|
});
|
|
160
222
|
}
|
|
161
223
|
|
|
162
|
-
this.logger.
|
|
224
|
+
this.logger.debug('Configuring Item Operations under PK Param %s', this.getPkParam());
|
|
163
225
|
router.use(`/:${this.getPkParam()}`, this.validatePrimaryKeyValue, itemRouter);
|
|
164
226
|
|
|
165
227
|
if (this.childRouters) {
|
|
@@ -174,14 +236,14 @@ export class ItemRouter<
|
|
|
174
236
|
res.locals[this.getPkParam()] = pkParamValue;
|
|
175
237
|
next();
|
|
176
238
|
} else {
|
|
177
|
-
this.logger.error('Invalid Primary Key', { pkParamValue, path: req?.
|
|
178
|
-
res.status(500).json({ error: 'Invalid Primary Key', path: req?.
|
|
239
|
+
this.logger.error('Invalid Primary Key', { pkParamValue, path: req?.originalUrl });
|
|
240
|
+
res.status(500).json({ error: 'Invalid Primary Key', path: req?.originalUrl });
|
|
179
241
|
}
|
|
180
242
|
}
|
|
181
243
|
|
|
182
244
|
private configureChildRouters = (router: Router, childRouters: Record<string, Router>) => {
|
|
183
245
|
for (const path in childRouters) {
|
|
184
|
-
this.logger.
|
|
246
|
+
this.logger.debug('Configuring Child Router at Path %s', path);
|
|
185
247
|
|
|
186
248
|
router.use(`/${path}`, childRouters[path]);
|
|
187
249
|
}
|
|
@@ -208,14 +270,16 @@ export class ItemRouter<
|
|
|
208
270
|
// TODO: Probably a better way to do this, but this postCreate hook only needs the item.
|
|
209
271
|
/* istanbul ignore next */
|
|
210
272
|
public postCreateItem = async (item: Item<S, L1, L2, L3, L4, L5>): Promise<Item<S, L1, L2, L3, L4, L5>> => {
|
|
211
|
-
this.logger.
|
|
273
|
+
this.logger.debug('Post Create Item', { item });
|
|
212
274
|
return item;
|
|
213
275
|
};
|
|
214
276
|
|
|
215
277
|
protected deleteItem = async (req: Request, res: Response): Promise<void> => {
|
|
216
|
-
|
|
278
|
+
const libOperations = this.lib.operations;
|
|
279
|
+
|
|
280
|
+
this.logger.debug('Deleting Item', { query: req.query, params: req.params, locals: res.locals });
|
|
217
281
|
const ik = this.getIk(res);
|
|
218
|
-
const removedItem = await
|
|
282
|
+
const removedItem = await libOperations.remove(ik);
|
|
219
283
|
const item = validatePK(removedItem, this.getPkType());
|
|
220
284
|
res.json(item);
|
|
221
285
|
};
|
|
@@ -228,11 +292,12 @@ export class ItemRouter<
|
|
|
228
292
|
/* eslint-enable */
|
|
229
293
|
|
|
230
294
|
protected getItem = async (req: Request, res: Response) => {
|
|
231
|
-
|
|
295
|
+
const libOperations = this.lib.operations;
|
|
296
|
+
this.logger.debug('Getting Item', { query: req.query, params: req.params, locals: res.locals });
|
|
232
297
|
const ik = this.getIk(res);
|
|
233
298
|
try {
|
|
234
299
|
// TODO: What error does validate PK throw, when can that fail?
|
|
235
|
-
const item = validatePK(await
|
|
300
|
+
const item = validatePK(await libOperations.get(ik), this.getPkType());
|
|
236
301
|
res.json(item);
|
|
237
302
|
} catch (err: any) {
|
|
238
303
|
if (err instanceof NotFoundError) {
|
|
@@ -252,18 +317,18 @@ export class ItemRouter<
|
|
|
252
317
|
}
|
|
253
318
|
|
|
254
319
|
protected updateItem = async (req: Request, res: Response) => {
|
|
255
|
-
this.
|
|
320
|
+
const libOperations = this.lib.operations;
|
|
321
|
+
this.logger.debug('Updating Item',
|
|
256
322
|
{ body: req?.body, query: req?.query, params: req?.params, locals: res?.locals });
|
|
257
323
|
const ik = this.getIk(res);
|
|
258
|
-
const itemToUpdate = this.convertDates(req.body as
|
|
259
|
-
const retItem = validatePK(await
|
|
324
|
+
const itemToUpdate = this.convertDates(req.body as Partial<Item<S, L1, L2, L3, L4, L5>>);
|
|
325
|
+
const retItem = validatePK(await libOperations.update(ik, itemToUpdate), this.getPkType());
|
|
260
326
|
res.json(retItem);
|
|
261
327
|
};
|
|
262
328
|
|
|
263
|
-
public convertDates = (item: Item<S, L1, L2, L3, L4, L5
|
|
264
|
-
Item<S, L1, L2, L3, L4, L5> | ItemProperties<S, L1, L2, L3, L4, L5> => {
|
|
329
|
+
public convertDates = (item: Partial<Item<S, L1, L2, L3, L4, L5>>): Partial<Item<S, L1, L2, L3, L4, L5>> => {
|
|
265
330
|
const events = item.events as Record<string, ItemEvent>;
|
|
266
|
-
this.logger.
|
|
331
|
+
this.logger.debug('Converting Dates', { item });
|
|
267
332
|
if (events) {
|
|
268
333
|
Object.keys(events).forEach((key: string) => {
|
|
269
334
|
Object.assign(events, {
|
package/src/PItemRouter.ts
CHANGED
|
@@ -2,9 +2,6 @@ import { Item, ItemQuery, paramsToQuery, PriKey, QueryParams, validatePK } from
|
|
|
2
2
|
import { Primary } from "@fjell/lib";
|
|
3
3
|
import { ItemRouter, ItemRouterOptions } from "@/ItemRouter";
|
|
4
4
|
import { Request, Response } from "express";
|
|
5
|
-
import LibLogger from "@/logger";
|
|
6
|
-
|
|
7
|
-
const logger = LibLogger.get('PItemRouter');
|
|
8
5
|
|
|
9
6
|
interface ParsedQuery {
|
|
10
7
|
[key: string]: undefined | string | string[] | ParsedQuery | ParsedQuery[];
|
|
@@ -12,7 +9,7 @@ interface ParsedQuery {
|
|
|
12
9
|
|
|
13
10
|
export class PItemRouter<T extends Item<S>, S extends string> extends ItemRouter<S> {
|
|
14
11
|
|
|
15
|
-
constructor(lib: Primary.
|
|
12
|
+
constructor(lib: Primary.Instance<T, S>, keyType: S, options: ItemRouterOptions = {}) {
|
|
16
13
|
super(lib as any, keyType, options);
|
|
17
14
|
}
|
|
18
15
|
|
|
@@ -22,16 +19,18 @@ export class PItemRouter<T extends Item<S>, S extends string> extends ItemRouter
|
|
|
22
19
|
}
|
|
23
20
|
|
|
24
21
|
public createItem = async (req: Request, res: Response) => {
|
|
25
|
-
|
|
22
|
+
const libOperations = this.lib.operations;
|
|
23
|
+
this.logger.default('Creating Item', { body: req.body, query: req.query, params: req.params, locals: res.locals });
|
|
26
24
|
const itemToCreate = this.convertDates(req.body as Item<S>);
|
|
27
|
-
let item =
|
|
28
|
-
validatePK(await this.lib.create(itemToCreate), this.getPkType()) as Item<S>;
|
|
25
|
+
let item = validatePK(await libOperations.create(itemToCreate), this.getPkType()) as Item<S>;
|
|
29
26
|
item = await this.postCreateItem(item);
|
|
27
|
+
this.logger.default('Created Item %j', item);
|
|
30
28
|
res.json(item);
|
|
31
29
|
};
|
|
32
30
|
|
|
33
31
|
protected findItems = async (req: Request, res: Response) => {
|
|
34
|
-
|
|
32
|
+
const libOperations = this.lib.operations;
|
|
33
|
+
this.logger.default('Finding Items', { query: req.query, params: req.params, locals: res.locals });
|
|
35
34
|
|
|
36
35
|
let items: Item<S>[] = [];
|
|
37
36
|
|
|
@@ -42,19 +41,19 @@ export class PItemRouter<T extends Item<S>, S extends string> extends ItemRouter
|
|
|
42
41
|
|
|
43
42
|
if (finder) {
|
|
44
43
|
// If finder is defined? Call a finder.
|
|
45
|
-
logger.default('Finding Items with
|
|
44
|
+
this.logger.default('Finding Items with Finder %s %j one:%s', finder, finderParams, one);
|
|
46
45
|
|
|
47
46
|
if (one === 'true') {
|
|
48
47
|
const item = await (this.lib as any).findOne(finder, JSON.parse(finderParams));
|
|
49
48
|
items = item ? [item] : [];
|
|
50
49
|
} else {
|
|
51
|
-
items = await
|
|
50
|
+
items = await libOperations.find(finder, JSON.parse(finderParams));
|
|
52
51
|
}
|
|
53
52
|
} else {
|
|
54
|
-
logger.default('Finding Items with a query', { query: req.query });
|
|
55
53
|
// TODO: This is once of the more important places to perform some validaation and feedback
|
|
56
54
|
const itemQuery: ItemQuery = paramsToQuery(req.query as QueryParams);
|
|
57
|
-
|
|
55
|
+
this.logger.default('Finding Items with a query %j', itemQuery);
|
|
56
|
+
items = await libOperations.all(itemQuery);
|
|
58
57
|
}
|
|
59
58
|
|
|
60
59
|
res.json(items.map((item: Item<S>) => validatePK(item, this.getPkType())));
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
2
|
+
/* eslint-disable no-undefined */
|
|
3
|
+
export const clean = (obj: any) => {
|
|
4
|
+
return Object.fromEntries(
|
|
5
|
+
Object.entries(obj).filter(([_, v]) => v !== undefined)
|
|
6
|
+
);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
//Recursive implementation of jSON.stringify;
|
|
10
|
+
export const stringifyJSON = function (obj: any, visited: Set<any> = new Set()): string {
|
|
11
|
+
const arrOfKeyVals: string[] = [];
|
|
12
|
+
const arrVals: string[] = [];
|
|
13
|
+
let objKeys: string[] = [];
|
|
14
|
+
|
|
15
|
+
/*********CHECK FOR PRIMITIVE TYPES**********/
|
|
16
|
+
if (typeof obj === 'number' || typeof obj === 'boolean' || obj === null)
|
|
17
|
+
return '' + obj;
|
|
18
|
+
else if (typeof obj === 'string')
|
|
19
|
+
return '"' + obj + '"';
|
|
20
|
+
|
|
21
|
+
/*********DETECT CIRCULAR REFERENCES**********/
|
|
22
|
+
if (obj instanceof Object && visited.has(obj)) {
|
|
23
|
+
return '"(circular)"';
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/*********CHECK FOR ARRAY**********/
|
|
27
|
+
else if (Array.isArray(obj)) {
|
|
28
|
+
//check for empty array
|
|
29
|
+
if (obj[0] === undefined)
|
|
30
|
+
return '[]';
|
|
31
|
+
else {
|
|
32
|
+
// Add array to visited before processing its elements
|
|
33
|
+
visited.add(obj);
|
|
34
|
+
obj.forEach(function (el) {
|
|
35
|
+
arrVals.push(stringifyJSON(el, visited));
|
|
36
|
+
});
|
|
37
|
+
return '[' + arrVals + ']';
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
/*********CHECK FOR OBJECT**********/
|
|
41
|
+
else if (obj instanceof Object) {
|
|
42
|
+
// Add object to visited before processing its properties
|
|
43
|
+
visited.add(obj);
|
|
44
|
+
//get object keys
|
|
45
|
+
objKeys = Object.keys(obj);
|
|
46
|
+
//set key output;
|
|
47
|
+
objKeys.forEach(function (key) {
|
|
48
|
+
const keyOut = '"' + key + '":';
|
|
49
|
+
const keyValOut = obj[key];
|
|
50
|
+
//skip functions and undefined properties
|
|
51
|
+
if (keyValOut instanceof Function || keyValOut === undefined)
|
|
52
|
+
return; // Skip this entry entirely instead of pushing an empty string
|
|
53
|
+
else if (typeof keyValOut === 'string')
|
|
54
|
+
arrOfKeyVals.push(keyOut + '"' + keyValOut + '"');
|
|
55
|
+
else if (typeof keyValOut === 'boolean' || typeof keyValOut === 'number' || keyValOut === null)
|
|
56
|
+
arrOfKeyVals.push(keyOut + keyValOut);
|
|
57
|
+
//check for nested objects, call recursively until no more objects
|
|
58
|
+
else if (keyValOut instanceof Object) {
|
|
59
|
+
arrOfKeyVals.push(keyOut + stringifyJSON(keyValOut, visited));
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
return '{' + arrOfKeyVals + '}';
|
|
63
|
+
}
|
|
64
|
+
return '';
|
|
65
|
+
};
|