@iebh/tera-fy 2.0.20 → 2.0.21
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/CHANGELOG.md +39 -1
- package/dist/plugin.vue2.es2019.js +1 -1
- package/dist/terafy.es2019.js +1 -1
- package/dist/terafy.js +1 -1
- package/eslint.config.js +0 -5
- package/lib/syncro/entities.js +1 -1
- package/lib/syncro/syncro.js +80 -9
- package/lib/terafy.client.js +6 -2
- package/lib/terafy.server.js +71 -50
- package/package.json +5 -4
- package/utils/pathTools.js +3 -5
package/lib/terafy.server.js
CHANGED
|
@@ -69,9 +69,9 @@ export default class TeraFyServer {
|
|
|
69
69
|
...cloneDeep(message), // Need to clone to resolve promise nasties
|
|
70
70
|
};
|
|
71
71
|
e.source.postMessage(payload, this.settings.restrictOrigin);
|
|
72
|
-
} catch (
|
|
73
|
-
this.debug('ERROR', 1, 'Attempted to dispatch payload server(via reply)->client', {payload,
|
|
74
|
-
throw
|
|
72
|
+
} catch (err) {
|
|
73
|
+
this.debug('ERROR', 1, 'Attempted to dispatch payload server(via reply)->client', {payload, err});
|
|
74
|
+
throw err;
|
|
75
75
|
}
|
|
76
76
|
},
|
|
77
77
|
});
|
|
@@ -127,14 +127,16 @@ export default class TeraFyServer {
|
|
|
127
127
|
...cloneDeep(message), // Need to clone to resolve promise nasties
|
|
128
128
|
};
|
|
129
129
|
iFrame.contentWindow.postMessage(payload, this.settings.restrictOrigin);
|
|
130
|
-
} catch (
|
|
131
|
-
this.debug('ERROR', 1, 'Attempted to dispatch payload server(top level window)->cient(iframe)', {payload,
|
|
132
|
-
throw
|
|
130
|
+
} catch (err) {
|
|
131
|
+
this.debug('ERROR', 1, 'Attempted to dispatch payload server(top level window)->cient(iframe)', {payload, err});
|
|
132
|
+
throw err;
|
|
133
133
|
}
|
|
134
134
|
},
|
|
135
135
|
});
|
|
136
136
|
}
|
|
137
137
|
case TeraFyServer.SERVERMODE_POPUP:
|
|
138
|
+
// FIXME: Need implementation for POPUP mode?
|
|
139
|
+
throw new Error('SERVERMODE_POPUP getClientContext not implemented');
|
|
138
140
|
}
|
|
139
141
|
}
|
|
140
142
|
|
|
@@ -190,7 +192,7 @@ export default class TeraFyServer {
|
|
|
190
192
|
* @returns {Promise<*>} A promise which resolves when the operation has completed with the remote reply
|
|
191
193
|
*/
|
|
192
194
|
send(message) {
|
|
193
|
-
if (!this.messageEvent) throw new Error('send()
|
|
195
|
+
if (!this.messageEvent?.source) throw new Error('send() requires a messageEvent with a source');
|
|
194
196
|
|
|
195
197
|
let id = nanoid();
|
|
196
198
|
|
|
@@ -199,10 +201,12 @@ export default class TeraFyServer {
|
|
|
199
201
|
Object.assign(this.acceptPostboxes[id], {
|
|
200
202
|
resolve, reject,
|
|
201
203
|
});
|
|
204
|
+
|
|
205
|
+
// Use sendRaw with the specific source from the stored messageEvent
|
|
202
206
|
this.sendRaw({
|
|
203
207
|
id,
|
|
204
208
|
...message,
|
|
205
|
-
});
|
|
209
|
+
}, this.messageEvent?.source); // Pass the source explicitly
|
|
206
210
|
});
|
|
207
211
|
|
|
208
212
|
return this.acceptPostboxes[id].promise;
|
|
@@ -224,7 +228,10 @@ export default class TeraFyServer {
|
|
|
224
228
|
...cloneDeep(message), // Need to clone to resolve promise nasties
|
|
225
229
|
};
|
|
226
230
|
this.debug('INFO', 3, 'Dispatch response', message, '<=>', payload);
|
|
227
|
-
|
|
231
|
+
|
|
232
|
+
let target = sendVia || globalThis.parent;
|
|
233
|
+
if (!target) this.debug('WARN', 1, 'Cannot sendRaw, no target window (sendVia or parent) found.');
|
|
234
|
+
target.postMessage(payload, this.settings.restrictOrigin);
|
|
228
235
|
} catch (e) {
|
|
229
236
|
this.debug('ERROR', 2, 'Attempted to dispatch response server->client', payload);
|
|
230
237
|
this.debug('ERROR', 2, 'Message compose server->client:', e);
|
|
@@ -274,6 +281,7 @@ export default class TeraFyServer {
|
|
|
274
281
|
} else {
|
|
275
282
|
this.acceptPostboxes[message.id].resolve(message.response);
|
|
276
283
|
}
|
|
284
|
+
delete this.acceptPostboxes[message.id]; // Clean up postbox
|
|
277
285
|
} else if (message.action == 'rpc') { // Relay RPC calls
|
|
278
286
|
if (!this[message.method]) throw new Error(`Unknown RPC method "${message.method}"`);
|
|
279
287
|
return this[message.method].apply(this.createContext(rawMessage), message.args);
|
|
@@ -289,12 +297,16 @@ export default class TeraFyServer {
|
|
|
289
297
|
}, rawMessage.source))
|
|
290
298
|
.catch(e => {
|
|
291
299
|
console.warn(`TERA-FY server threw on RPC:${message.method}:`, e);
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
300
|
+
if (message.action === 'rpc' && message.id && rawMessage.source) {
|
|
301
|
+
this.sendRaw({
|
|
302
|
+
id: message.id,
|
|
303
|
+
action: 'response',
|
|
304
|
+
isError: true,
|
|
305
|
+
response: e instanceof Error ? e.message : String(e), // Return error message to requester
|
|
306
|
+
}, rawMessage.source);
|
|
307
|
+
} else {
|
|
308
|
+
console.warn(`Unable to respond with errored RPC:${message.method} as reply postbox is invalid`);
|
|
309
|
+
}
|
|
298
310
|
})
|
|
299
311
|
}
|
|
300
312
|
|
|
@@ -315,6 +327,13 @@ export default class TeraFyServer {
|
|
|
315
327
|
* @returns {Promise<*>} A promise which resolves with the resulting inner callback payload
|
|
316
328
|
*/
|
|
317
329
|
requestFocus(cb) {
|
|
330
|
+
// Ensure messageEvent is set before calling senderRpc
|
|
331
|
+
if (!this.messageEvent && this.settings.serverMode != TeraFyServer.SERVERMODE_TERA) {
|
|
332
|
+
console.warn("requestFocus called without a messageEvent context. Cannot toggle focus.");
|
|
333
|
+
// Proceed without toggling focus if no context is available
|
|
334
|
+
return Promise.resolve().then(() => cb.call(this));
|
|
335
|
+
}
|
|
336
|
+
|
|
318
337
|
return Promise.resolve()
|
|
319
338
|
.then(()=> this.settings.serverMode != TeraFyServer.SERVERMODE_TERA && this.senderRpc('toggleFocus', true))
|
|
320
339
|
.then(()=> cb.call(this))
|
|
@@ -403,7 +422,10 @@ export default class TeraFyServer {
|
|
|
403
422
|
}
|
|
404
423
|
: null
|
|
405
424
|
)
|
|
406
|
-
.catch(e =>
|
|
425
|
+
.catch(e => {
|
|
426
|
+
console.warn('getUser() catch', e);
|
|
427
|
+
return null; // Return null on error
|
|
428
|
+
})
|
|
407
429
|
}
|
|
408
430
|
|
|
409
431
|
|
|
@@ -517,8 +539,8 @@ export default class TeraFyServer {
|
|
|
517
539
|
// Force refresh projects against the new user
|
|
518
540
|
await app.service('$projects').refresh();
|
|
519
541
|
return;
|
|
520
|
-
} catch (
|
|
521
|
-
throw new Error(`Failed to decode local dev state - ${
|
|
542
|
+
} catch (err) {
|
|
543
|
+
throw new Error(`Failed to decode local dev state - ${err.toString()}`);
|
|
522
544
|
}
|
|
523
545
|
}
|
|
524
546
|
|
|
@@ -530,7 +552,7 @@ export default class TeraFyServer {
|
|
|
530
552
|
|
|
531
553
|
// Attach click listner to internal button to re-popup the auth window (in case popups are blocked)
|
|
532
554
|
focusContent.querySelector('a.btn').addEventListener('click', ()=>
|
|
533
|
-
this.uiWindow(new URL(this.settings.sitePathLogin, this.settings.siteUrl))
|
|
555
|
+
this.uiWindow(new URL(this.settings.sitePathLogin, this.settings.siteUrl).toString())
|
|
534
556
|
);
|
|
535
557
|
|
|
536
558
|
// Create a deferred promise which will (eventually) resolve when the downstream window signals its ready
|
|
@@ -565,7 +587,7 @@ export default class TeraFyServer {
|
|
|
565
587
|
// Go fullscreen, try to open the auth window + prompt the user to retry (if popups are blocked) and wait for resolution
|
|
566
588
|
await this.requestFocus(()=> {
|
|
567
589
|
// Try opening the popup automatically - this will likely fail if the user has popup blocking enabled
|
|
568
|
-
this.uiWindow(new URL(this.settings.sitePathLogin, this.settings.siteUrl));
|
|
590
|
+
this.uiWindow(new URL(this.settings.sitePathLogin, this.settings.siteUrl).toString());
|
|
569
591
|
|
|
570
592
|
// Display a message to the user, offering the ability to re-open the popup if it was originally denied
|
|
571
593
|
this.uiSplat(focusContent, {logo: true});
|
|
@@ -685,7 +707,7 @@ export default class TeraFyServer {
|
|
|
685
707
|
}))
|
|
686
708
|
.then(project => resolve(project))
|
|
687
709
|
.catch(e => {
|
|
688
|
-
if (e == 'cancel') {
|
|
710
|
+
if (e.toLowerCase() == 'cancel') {
|
|
689
711
|
return this.requestFocus(()=>
|
|
690
712
|
app.service('$prompt').dialog({
|
|
691
713
|
title: settings.noSelectTitle,
|
|
@@ -732,7 +754,7 @@ export default class TeraFyServer {
|
|
|
732
754
|
app.service('$prompt').dialog({
|
|
733
755
|
title: settings.title,
|
|
734
756
|
component: 'projectsSelect',
|
|
735
|
-
buttons: settings.allowCancel
|
|
757
|
+
buttons: settings.allowCancel ? ['cancel'] : false,
|
|
736
758
|
})
|
|
737
759
|
))
|
|
738
760
|
.then(project => settings.setActive
|
|
@@ -773,11 +795,11 @@ export default class TeraFyServer {
|
|
|
773
795
|
* @returns {Promise<Object>} A promise which resolves to the namespace POJO state
|
|
774
796
|
*/
|
|
775
797
|
setNamespace(name, state, options) {
|
|
776
|
-
if (!/^[\w
|
|
798
|
+
if (!/^[\w-]+$/.test(name)) throw new Error('Namespaces must be alphanumeric + hyphens + underscores');
|
|
777
799
|
if (typeof state != 'object') throw new Error('State must be an object');
|
|
778
800
|
|
|
779
801
|
return app.service('$sync').setSnapshot(`project_namespaces::${app.service('$projects').active.id}}::${name}`, state, {
|
|
780
|
-
method: options.method,
|
|
802
|
+
method: options.method ?? 'merge',
|
|
781
803
|
});
|
|
782
804
|
}
|
|
783
805
|
|
|
@@ -839,7 +861,6 @@ export default class TeraFyServer {
|
|
|
839
861
|
*/
|
|
840
862
|
setProjectState(path, value, options) {
|
|
841
863
|
let settings = {
|
|
842
|
-
save: true,
|
|
843
864
|
strategy: 'set',
|
|
844
865
|
...options,
|
|
845
866
|
};
|
|
@@ -863,7 +884,8 @@ export default class TeraFyServer {
|
|
|
863
884
|
},
|
|
864
885
|
);
|
|
865
886
|
|
|
866
|
-
|
|
887
|
+
// Sync functionality for the moment but could be async in the future
|
|
888
|
+
return Promise.resolve(value);
|
|
867
889
|
}
|
|
868
890
|
|
|
869
891
|
|
|
@@ -902,7 +924,7 @@ export default class TeraFyServer {
|
|
|
902
924
|
newState: cloneDeep(target),
|
|
903
925
|
});
|
|
904
926
|
|
|
905
|
-
return value;
|
|
927
|
+
return Promise.resolve(value);
|
|
906
928
|
}
|
|
907
929
|
}
|
|
908
930
|
|
|
@@ -1011,7 +1033,7 @@ export default class TeraFyServer {
|
|
|
1011
1033
|
},
|
|
1012
1034
|
},
|
|
1013
1035
|
modalDialogClass: 'modal-dialog-lg',
|
|
1014
|
-
buttons: settings.allowCancel
|
|
1036
|
+
buttons: settings.allowCancel ? ['cancel'] : false,
|
|
1015
1037
|
})
|
|
1016
1038
|
))
|
|
1017
1039
|
}
|
|
@@ -1188,11 +1210,7 @@ export default class TeraFyServer {
|
|
|
1188
1210
|
};
|
|
1189
1211
|
|
|
1190
1212
|
return Promise.resolve()
|
|
1191
|
-
.then(()=>
|
|
1192
|
-
console.log('Checking for project, is required:', settings.autoRequire);
|
|
1193
|
-
settings.autoRequire && this.requireProject()
|
|
1194
|
-
console.log('Project set!')
|
|
1195
|
-
})
|
|
1213
|
+
.then(()=> settings.autoRequire && this.requireProject())
|
|
1196
1214
|
.then(()=> {
|
|
1197
1215
|
if (settings.id) return; // We already have a file ID specified - skip
|
|
1198
1216
|
|
|
@@ -1206,6 +1224,9 @@ export default class TeraFyServer {
|
|
|
1206
1224
|
})
|
|
1207
1225
|
.then(file => settings.id = file.id)
|
|
1208
1226
|
})
|
|
1227
|
+
.then(()=> { // Final checks
|
|
1228
|
+
if (!settings.id) throw new Error("Could not determine file ID to save to.");
|
|
1229
|
+
})
|
|
1209
1230
|
.then(()=> app.service('$supabase').fileSet(app.service('$projects').decodeFilePath(settings.id), contents, {
|
|
1210
1231
|
overwrite: true,
|
|
1211
1232
|
toast: false,
|
|
@@ -1242,7 +1263,7 @@ export default class TeraFyServer {
|
|
|
1242
1263
|
autoRequire: true,
|
|
1243
1264
|
filters: {
|
|
1244
1265
|
library: true,
|
|
1245
|
-
...options?.
|
|
1266
|
+
...(options?.filters ?? {}), // Use filters from options if provided
|
|
1246
1267
|
},
|
|
1247
1268
|
...options,
|
|
1248
1269
|
};
|
|
@@ -1283,6 +1304,8 @@ export default class TeraFyServer {
|
|
|
1283
1304
|
toast: false,
|
|
1284
1305
|
}))
|
|
1285
1306
|
.then(blob => {
|
|
1307
|
+
if (!blob) throw new Error(`File not found or empty: ${filePath}`);
|
|
1308
|
+
|
|
1286
1309
|
switch (settings.format) {
|
|
1287
1310
|
// NOTE: Any updates to the format list should also extend setProjectLibrary()
|
|
1288
1311
|
case 'pojo':
|
|
@@ -1365,7 +1388,7 @@ export default class TeraFyServer {
|
|
|
1365
1388
|
filePath = app.service('$projects').decodeFilePath(settings.id);
|
|
1366
1389
|
})
|
|
1367
1390
|
.then(()=> {
|
|
1368
|
-
// Mutate settings.
|
|
1391
|
+
// Mutate settings.refs -> Blob or File format needed by Supabase
|
|
1369
1392
|
if (settings.format == 'auto') {
|
|
1370
1393
|
settings.format =
|
|
1371
1394
|
Array.isArray(settings.refs) ? 'pojo'
|
|
@@ -1388,7 +1411,7 @@ export default class TeraFyServer {
|
|
|
1388
1411
|
if (!(settings.refs instanceof Blob)) throw new Error("setProjectLibrary({format: 'blob'} but non-Blob provided as `refs`");
|
|
1389
1412
|
return new File([settings.refs], app.service('$supabase')._parsePath(filePath).basename);
|
|
1390
1413
|
case 'file':
|
|
1391
|
-
if (!(settings.
|
|
1414
|
+
if (!(settings.refs instanceof File)) throw new Error("setProjectLibrary({format: 'file'} but non-File provided as `refs`");
|
|
1392
1415
|
return settings.refs;
|
|
1393
1416
|
default:
|
|
1394
1417
|
throw new Error(`Unsupported library format "${settings.format}"`);
|
|
@@ -1396,7 +1419,7 @@ export default class TeraFyServer {
|
|
|
1396
1419
|
})
|
|
1397
1420
|
.then(fileBlob => app.service('$supabase').fileUpload(filePath, {
|
|
1398
1421
|
file: fileBlob,
|
|
1399
|
-
overwrite:
|
|
1422
|
+
overwrite: settings.overwrite,
|
|
1400
1423
|
mode: 'encoded',
|
|
1401
1424
|
}))
|
|
1402
1425
|
.then(()=> null)
|
|
@@ -1484,11 +1507,9 @@ export default class TeraFyServer {
|
|
|
1484
1507
|
app.service('$prompt').dialog({
|
|
1485
1508
|
title: settings.title,
|
|
1486
1509
|
body: settings.body,
|
|
1487
|
-
buttons:
|
|
1488
|
-
settings.buttons == 'ok' ? ['ok']
|
|
1489
|
-
: false,
|
|
1510
|
+
buttons: settings.buttons == 'ok' ? ['ok'] : false,
|
|
1490
1511
|
isHtml: settings.isHtml,
|
|
1491
|
-
dialogClose: 'resolve',
|
|
1512
|
+
dialogClose: 'resolve', // Resolve promise when closed
|
|
1492
1513
|
})
|
|
1493
1514
|
);
|
|
1494
1515
|
}
|
|
@@ -1527,7 +1548,7 @@ export default class TeraFyServer {
|
|
|
1527
1548
|
{
|
|
1528
1549
|
title: 'OK',
|
|
1529
1550
|
class: 'btn btn-success',
|
|
1530
|
-
click: 'resolve',
|
|
1551
|
+
click: 'resolve', // Resolve promise with default value (usually true or button index)
|
|
1531
1552
|
},
|
|
1532
1553
|
{
|
|
1533
1554
|
title: 'Cancel',
|
|
@@ -1536,8 +1557,8 @@ export default class TeraFyServer {
|
|
|
1536
1557
|
},
|
|
1537
1558
|
],
|
|
1538
1559
|
})
|
|
1539
|
-
.then(()=> 'OK')
|
|
1540
|
-
.catch(()=> Promise.reject('CANCEL'))
|
|
1560
|
+
.then(()=> 'OK') // Resolve with 'OK' if OK button clicked
|
|
1561
|
+
.catch(()=> Promise.reject('CANCEL')) // Reject with 'CANCEL' if Cancel button clicked or closed
|
|
1541
1562
|
);
|
|
1542
1563
|
}
|
|
1543
1564
|
|
|
@@ -1557,7 +1578,7 @@ export default class TeraFyServer {
|
|
|
1557
1578
|
* Display, update or dispose of windows for long running tasks
|
|
1558
1579
|
* All options are cumulative - i.e. they are merged with other options previously provided
|
|
1559
1580
|
*
|
|
1560
|
-
* @param {Object|Boolean} [options] Additional options to mutate behaviour, if boolean false `
|
|
1581
|
+
* @param {Object|Boolean} [options] Additional options to mutate behaviour, if boolean false `close: true` is assumed
|
|
1561
1582
|
* @param {String} [options.body=''] Window body text
|
|
1562
1583
|
* @param {Boolean} [options.bodyHtml=false] If truthy, treat the body as HTML
|
|
1563
1584
|
* @param {String} [options.title='TERA'] Window title, can only be set on the initial call
|
|
@@ -1648,7 +1669,7 @@ export default class TeraFyServer {
|
|
|
1648
1669
|
return this.requestFocus(()=>
|
|
1649
1670
|
app.service('$prompt').dialog({
|
|
1650
1671
|
title: settings.title,
|
|
1651
|
-
closable: true,
|
|
1672
|
+
closable: true, // Allow closing via backdrop click (will reject)
|
|
1652
1673
|
component: 'UiPrompt',
|
|
1653
1674
|
componentProps: {
|
|
1654
1675
|
body: settings.body,
|
|
@@ -1662,7 +1683,7 @@ export default class TeraFyServer {
|
|
|
1662
1683
|
icon: 'fas fa-check',
|
|
1663
1684
|
title: 'Ok',
|
|
1664
1685
|
click() {
|
|
1665
|
-
return
|
|
1686
|
+
return app.service('$prompt').close(true, this.newValue);
|
|
1666
1687
|
},
|
|
1667
1688
|
},
|
|
1668
1689
|
'cancel',
|
|
@@ -1672,9 +1693,9 @@ export default class TeraFyServer {
|
|
|
1672
1693
|
.then(answer => {
|
|
1673
1694
|
if (answer) {
|
|
1674
1695
|
return answer;
|
|
1675
|
-
} else if (settings.required) {
|
|
1696
|
+
} else if (settings.required) { // Required answer but returned response is falsy
|
|
1676
1697
|
return Promise.reject('CANCEL');
|
|
1677
|
-
} else {
|
|
1698
|
+
} else { // Everything else - relay the raw value
|
|
1678
1699
|
return answer;
|
|
1679
1700
|
}
|
|
1680
1701
|
})
|
|
@@ -1719,7 +1740,7 @@ export default class TeraFyServer {
|
|
|
1719
1740
|
location: false,
|
|
1720
1741
|
menubar: false,
|
|
1721
1742
|
status: false,
|
|
1722
|
-
|
|
1743
|
+
scrollbars: false,
|
|
1723
1744
|
},
|
|
1724
1745
|
...options,
|
|
1725
1746
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@iebh/tera-fy",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.21",
|
|
4
4
|
"description": "TERA website worker",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"dev": "esbuild --platform=browser --format=esm --bundle lib/terafy.client.js --outfile=dist/terafy.js --minify --serve --servedir=.",
|
|
@@ -86,7 +86,7 @@
|
|
|
86
86
|
"uuid": "^11.1.0"
|
|
87
87
|
},
|
|
88
88
|
"devDependencies": {
|
|
89
|
-
"@momsfriendlydevco/eslint-config": "^2.1.
|
|
89
|
+
"@momsfriendlydevco/eslint-config": "^2.1.3",
|
|
90
90
|
"@release-it/conventional-changelog": "^10.0.0",
|
|
91
91
|
"concurrently": "^9.1.2",
|
|
92
92
|
"documentation": "^14.0.3",
|
|
@@ -117,8 +117,9 @@
|
|
|
117
117
|
"before:init": "git reset HEAD -- api.md docs/ dist/",
|
|
118
118
|
"before:bump": [
|
|
119
119
|
"npm run build",
|
|
120
|
-
"git add api.md docs/ dist/"
|
|
121
|
-
]
|
|
120
|
+
"git add -f api.md docs/ dist/"
|
|
121
|
+
],
|
|
122
|
+
"after:release": "git rm --cached -r dist && echo 'Untracked dist/ for subsequent commits.'"
|
|
122
123
|
},
|
|
123
124
|
"plugins": {
|
|
124
125
|
"@release-it/conventional-changelog": {
|
package/utils/pathTools.js
CHANGED
|
@@ -121,11 +121,9 @@ export function merge(target, path, value) {
|
|
|
121
121
|
*/
|
|
122
122
|
export function defaults(target, path, value) {
|
|
123
123
|
if (typeof path == 'string' || Array.isArray(path)) { // Called as (target, path, value)
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
return _defaults(get(target, path), value);
|
|
128
|
-
}
|
|
124
|
+
return !has(target, path) // Target path doesn't exist at all
|
|
125
|
+
? set(target, path, value)
|
|
126
|
+
: _defaults(get(target, path), value);
|
|
129
127
|
} else { // Called as (target, value)
|
|
130
128
|
return _defaults(target, path);
|
|
131
129
|
}
|