@jumoo/usync 0.0.0-stage → 17.5.1
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/LICENCE +373 -0
- package/README.md +9 -2
- package/dist/api/client/client.gen.d.ts +2 -0
- package/dist/api/client/client.gen.js +216 -0
- package/dist/api/client/index.d.ts +10 -0
- package/dist/api/client/index.js +6 -0
- package/dist/api/client/types.gen.d.ts +120 -0
- package/dist/api/client/types.gen.js +2 -0
- package/dist/api/client/utils.gen.d.ts +37 -0
- package/dist/api/client/utils.gen.js +230 -0
- package/dist/api/client.gen.d.ts +12 -0
- package/dist/api/client.gen.js +4 -0
- package/dist/api/core/auth.gen.d.ts +25 -0
- package/dist/api/core/auth.gen.js +14 -0
- package/dist/api/core/bodySerializer.gen.d.ts +25 -0
- package/dist/api/core/bodySerializer.gen.js +57 -0
- package/dist/api/core/params.gen.d.ts +43 -0
- package/dist/api/core/params.gen.js +109 -0
- package/dist/api/core/pathSerializer.gen.d.ts +33 -0
- package/dist/api/core/pathSerializer.gen.js +106 -0
- package/dist/api/core/queryKeySerializer.gen.d.ts +18 -0
- package/dist/api/core/queryKeySerializer.gen.js +92 -0
- package/dist/api/core/serverSentEvents.gen.d.ts +71 -0
- package/dist/api/core/serverSentEvents.gen.js +132 -0
- package/dist/api/core/types.gen.d.ts +83 -0
- package/dist/api/core/types.gen.js +2 -0
- package/dist/api/core/utils.gen.d.ts +19 -0
- package/dist/api/core/utils.gen.js +87 -0
- package/dist/api/index.d.ts +3 -0
- package/dist/api/index.js +4 -0
- package/dist/api/sdk.gen.d.ts +44 -0
- package/dist/api/sdk.gen.js +147 -0
- package/dist/api/types.gen.d.ts +567 -0
- package/dist/api/types.gen.js +47 -0
- package/dist/components/events.d.ts +11 -0
- package/dist/components/events.js +11 -0
- package/dist/components/index.d.ts +8 -0
- package/dist/components/index.js +9 -0
- package/dist/components/usync-action-box.d.ts +22 -0
- package/dist/components/usync-action-box.js +119 -0
- package/dist/components/usync-change-detail.d.ts +31 -0
- package/dist/components/usync-change-detail.js +709 -0
- package/dist/components/usync-change-view.d.ts +22 -0
- package/dist/components/usync-change-view.js +125 -0
- package/dist/components/usync-progress-box.d.ts +20 -0
- package/dist/components/usync-progress-box.js +223 -0
- package/dist/components/usync-result-row.d.ts +15 -0
- package/dist/components/usync-result-row.js +133 -0
- package/dist/components/usync-results-group-view.d.ts +19 -0
- package/dist/components/usync-results-group-view.js +110 -0
- package/dist/components/usync-results-view.d.ts +17 -0
- package/dist/components/usync-results-view.js +176 -0
- package/dist/components/usync-sync-file-info-view.d.ts +15 -0
- package/dist/components/usync-sync-file-info-view.js +62 -0
- package/dist/conditions/constants.d.ts +1 -0
- package/dist/conditions/constants.js +1 -0
- package/dist/conditions/index.d.ts +2 -0
- package/dist/conditions/index.js +2 -0
- package/dist/conditions/legacy-files.condition.d.ts +10 -0
- package/dist/conditions/legacy-files.condition.js +16 -0
- package/dist/conditions/manifest.d.ts +1 -0
- package/dist/conditions/manifest.js +18 -0
- package/dist/conditions/new-section.condition.d.ts +8 -0
- package/dist/conditions/new-section.condition.js +48 -0
- package/dist/constants.d.ts +30 -0
- package/dist/constants.js +38 -0
- package/dist/dialogs/details-modal-element.d.ts +18 -0
- package/dist/dialogs/details-modal-element.js +98 -0
- package/dist/dialogs/details-modal-token.d.ts +10 -0
- package/dist/dialogs/details-modal-token.js +7 -0
- package/dist/dialogs/error-modal-element.d.ts +7 -0
- package/dist/dialogs/error-modal-element.js +54 -0
- package/dist/dialogs/error-modal-token.d.ts +9 -0
- package/dist/dialogs/error-modal-token.js +6 -0
- package/dist/dialogs/index.d.ts +6 -0
- package/dist/dialogs/index.js +6 -0
- package/dist/dialogs/legacy-modal-element.d.ts +13 -0
- package/dist/dialogs/legacy-modal-element.js +78 -0
- package/dist/dialogs/manifest.d.ts +1 -0
- package/dist/dialogs/manifest.js +25 -0
- package/dist/dialogs/single-import-modal.element.d.ts +13 -0
- package/dist/dialogs/single-import-modal.element.js +105 -0
- package/dist/dialogs/single-import-modal.token.d.ts +9 -0
- package/dist/dialogs/single-import-modal.token.js +6 -0
- package/dist/exports.d.ts +10 -0
- package/dist/exports.js +10 -0
- package/dist/external/signalr/index.d.ts +1 -0
- package/dist/external/signalr/index.js +1 -0
- package/dist/hey-api.d.ts +11 -0
- package/dist/hey-api.js +14 -0
- package/dist/icons/icons.d.ts +5 -0
- package/dist/icons/icons.js +6 -0
- package/dist/icons/manifest.d.ts +1 -0
- package/dist/icons/manifest.js +7 -0
- package/dist/icons/svg/logo.d.ts +2 -0
- package/dist/icons/svg/logo.js +21 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +11 -0
- package/dist/lang/files/da-dk.d.ts +130 -0
- package/dist/lang/files/da-dk.js +141 -0
- package/dist/lang/files/de-de.d.ts +130 -0
- package/dist/lang/files/de-de.js +141 -0
- package/dist/lang/files/en-us.d.ts +161 -0
- package/dist/lang/files/en-us.js +172 -0
- package/dist/lang/files/es-es.d.ts +130 -0
- package/dist/lang/files/es-es.js +141 -0
- package/dist/lang/files/fr-fr.d.ts +130 -0
- package/dist/lang/files/fr-fr.js +141 -0
- package/dist/lang/files/nl-nl.d.ts +130 -0
- package/dist/lang/files/nl-nl.js +141 -0
- package/dist/lang/manifest.d.ts +1 -0
- package/dist/lang/manifest.js +63 -0
- package/dist/manifests.d.ts +1 -0
- package/dist/manifests.js +14 -0
- package/dist/repository/SyncAction.respositoy.d.ts +101 -0
- package/dist/repository/SyncAction.respositoy.js +125 -0
- package/dist/repository/index.d.ts +4 -0
- package/dist/repository/index.js +4 -0
- package/dist/repository/sources/SyncAction.source.d.ts +37 -0
- package/dist/repository/sources/SyncAction.source.js +57 -0
- package/dist/repository/sources/SyncMigration.source.d.ts +13 -0
- package/dist/repository/sources/SyncMigration.source.js +30 -0
- package/dist/repository/sources/SyncSettings.source.d.ts +23 -0
- package/dist/repository/sources/SyncSettings.source.js +33 -0
- package/dist/signalr/index.d.ts +3 -0
- package/dist/signalr/index.js +3 -0
- package/dist/signalr/signalr-reconnect-policy.d.ts +11 -0
- package/dist/signalr/signalr-reconnect-policy.js +14 -0
- package/dist/signalr/signalr.context.d.ts +34 -0
- package/dist/signalr/signalr.context.js +110 -0
- package/dist/signalr/signalr.context.token.d.ts +3 -0
- package/dist/signalr/signalr.context.token.js +2 -0
- package/dist/signalr/types.d.ts +21 -0
- package/dist/signalr/types.js +1 -0
- package/dist/tree/constants.d.ts +1 -0
- package/dist/tree/constants.js +1 -0
- package/dist/tree/index.d.ts +2 -0
- package/dist/tree/index.js +2 -0
- package/dist/tree/manifest.d.ts +1 -0
- package/dist/tree/manifest.js +92 -0
- package/dist/tree/types.d.ts +14 -0
- package/dist/tree/types.js +1 -0
- package/dist/tree/usync.menu-element.d.ts +13 -0
- package/dist/tree/usync.menu-element.js +74 -0
- package/dist/utils/diff/change-name.d.ts +13 -0
- package/dist/utils/diff/change-name.js +30 -0
- package/dist/utils/diff/classify-change.d.ts +21 -0
- package/dist/utils/diff/classify-change.js +111 -0
- package/dist/utils/diff/deep-equal.d.ts +13 -0
- package/dist/utils/diff/deep-equal.js +57 -0
- package/dist/utils/diff/format-value.d.ts +36 -0
- package/dist/utils/diff/format-value.js +88 -0
- package/dist/utils/diff/index.d.ts +7 -0
- package/dist/utils/diff/index.js +7 -0
- package/dist/utils/diff/json-diff.d.ts +34 -0
- package/dist/utils/diff/json-diff.js +300 -0
- package/dist/utils/diff/line-diff.d.ts +16 -0
- package/dist/utils/diff/line-diff.js +199 -0
- package/dist/utils/diff/types.d.ts +62 -0
- package/dist/utils/diff/types.js +12 -0
- package/dist/workspace/components/events.d.ts +31 -0
- package/dist/workspace/components/events.js +54 -0
- package/dist/workspace/components/index.d.ts +3 -0
- package/dist/workspace/components/index.js +3 -0
- package/dist/workspace/components/usync-action-button.d.ts +19 -0
- package/dist/workspace/components/usync-action-button.js +113 -0
- package/dist/workspace/components/usync-file-picker.element.d.ts +23 -0
- package/dist/workspace/components/usync-file-picker.element.js +106 -0
- package/dist/workspace/components/usync-file-upload.element.d.ts +21 -0
- package/dist/workspace/components/usync-file-upload.element.js +121 -0
- package/dist/workspace/dialogs/import-modal.element.d.ts +11 -0
- package/dist/workspace/dialogs/import-modal.element.js +75 -0
- package/dist/workspace/dialogs/import-modal.token.d.ts +6 -0
- package/dist/workspace/dialogs/import-modal.token.js +6 -0
- package/dist/workspace/dialogs/index.d.ts +2 -0
- package/dist/workspace/dialogs/index.js +2 -0
- package/dist/workspace/dialogs/manifest.d.ts +1 -0
- package/dist/workspace/dialogs/manifest.js +7 -0
- package/dist/workspace/index.d.ts +3 -0
- package/dist/workspace/index.js +3 -0
- package/dist/workspace/manifest.d.ts +3 -0
- package/dist/workspace/manifest.js +112 -0
- package/dist/workspace/types.d.ts +12 -0
- package/dist/workspace/types.js +1 -0
- package/dist/workspace/views/addons/addons.element.d.ts +7 -0
- package/dist/workspace/views/addons/addons.element.js +117 -0
- package/dist/workspace/views/default/default.element.d.ts +31 -0
- package/dist/workspace/views/default/default.element.js +466 -0
- package/dist/workspace/views/legacy/legacy.element.d.ts +14 -0
- package/dist/workspace/views/legacy/legacy.element.js +187 -0
- package/dist/workspace/views/settings/components/usyncSettingItem.element.d.ts +15 -0
- package/dist/workspace/views/settings/components/usyncSettingItem.element.js +96 -0
- package/dist/workspace/views/settings/settings.element.d.ts +17 -0
- package/dist/workspace/views/settings/settings.element.js +174 -0
- package/dist/workspace/workspace.context.d.ts +74 -0
- package/dist/workspace/workspace.context.js +448 -0
- package/dist/workspace/workspace.element.d.ts +17 -0
- package/dist/workspace/workspace.element.js +69 -0
- package/package.json +69 -5
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { uSyncConstants, uSyncMenuElement } from '@jumoo/uSync';
|
|
2
|
+
import { USYNC_CONDITION_NEW_SECTION } from '../conditions/constants';
|
|
3
|
+
import { USYNC_SECTION_ALIAS } from './constants';
|
|
4
|
+
const sectionAlias = 'Umb.Section.Settings';
|
|
5
|
+
/*
|
|
6
|
+
* config slight of hand: This manifest is added in c#
|
|
7
|
+
* via a manaifestreader, that only adds it if a setting
|
|
8
|
+
* is true in the appsettings.json file.
|
|
9
|
+
*
|
|
10
|
+
* we can then use conditional code that will only allow
|
|
11
|
+
* the tree to show in settings when this section doesn't
|
|
12
|
+
* exist globally.
|
|
13
|
+
*/
|
|
14
|
+
/*
|
|
15
|
+
const uSyncSection: UmbExtensionManifest = {
|
|
16
|
+
type: 'section',
|
|
17
|
+
alias: USYNC_SECTION_ALIAS,
|
|
18
|
+
name: 'uSync',
|
|
19
|
+
weight: 350,
|
|
20
|
+
meta: {
|
|
21
|
+
label: '#uSync_section',
|
|
22
|
+
pathname: 'sync',
|
|
23
|
+
},
|
|
24
|
+
};
|
|
25
|
+
*/
|
|
26
|
+
const menuConstants = {
|
|
27
|
+
alias: 'usync.menu',
|
|
28
|
+
name: 'uSync',
|
|
29
|
+
icon: 'icon-infinity',
|
|
30
|
+
rootElement: uSyncConstants.workspace.rootElement,
|
|
31
|
+
};
|
|
32
|
+
const menu = {
|
|
33
|
+
type: 'menu',
|
|
34
|
+
alias: menuConstants.alias,
|
|
35
|
+
name: menuConstants.name,
|
|
36
|
+
meta: {
|
|
37
|
+
label: menuConstants.name,
|
|
38
|
+
icon: menuConstants.icon,
|
|
39
|
+
entityType: menuConstants.rootElement,
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
const menuItem = {
|
|
43
|
+
type: 'menuItem',
|
|
44
|
+
alias: 'usync.menu.item',
|
|
45
|
+
name: 'uSync menu item',
|
|
46
|
+
element: uSyncMenuElement,
|
|
47
|
+
meta: {
|
|
48
|
+
label: 'uSync',
|
|
49
|
+
icon: 'usync-logo',
|
|
50
|
+
entityType: uSyncConstants.workspace.rootElement,
|
|
51
|
+
menus: [menuConstants.alias],
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
const menuSidebarApp = {
|
|
55
|
+
type: 'sectionSidebarApp',
|
|
56
|
+
kind: 'menu',
|
|
57
|
+
alias: 'usync.sidebarapp',
|
|
58
|
+
name: 'uSync section sidebar menu',
|
|
59
|
+
weight: 150,
|
|
60
|
+
meta: {
|
|
61
|
+
label: '#uSync_section',
|
|
62
|
+
menu: menu.alias,
|
|
63
|
+
},
|
|
64
|
+
conditions: [
|
|
65
|
+
{
|
|
66
|
+
alias: 'Umb.Condition.SectionAlias',
|
|
67
|
+
oneOf: [sectionAlias, USYNC_SECTION_ALIAS],
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
alias: USYNC_CONDITION_NEW_SECTION,
|
|
71
|
+
},
|
|
72
|
+
],
|
|
73
|
+
};
|
|
74
|
+
/// example of how to extend uSync menus.
|
|
75
|
+
// const subMenuItem : ManifestuSyncMenuItem = {
|
|
76
|
+
// type: 'usync-menuItem',
|
|
77
|
+
// alias: 'usync.menu.sub.item',
|
|
78
|
+
// name: 'uSync core menu item',
|
|
79
|
+
// meta: {
|
|
80
|
+
// label: 'uSync Extension',
|
|
81
|
+
// icon: 'icon-brick',
|
|
82
|
+
// entityType: 'usync-root',
|
|
83
|
+
// menus: [uSyncConstants.menuAlias],
|
|
84
|
+
// }
|
|
85
|
+
// }
|
|
86
|
+
export const manifests = [
|
|
87
|
+
// uSyncSection,
|
|
88
|
+
menu,
|
|
89
|
+
menuSidebarApp,
|
|
90
|
+
menuItem,
|
|
91
|
+
// subMenuItem
|
|
92
|
+
];
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { ManifestElement } from '@umbraco-cms/backoffice/extension-api';
|
|
2
|
+
import { MetaMenuItem, UmbMenuItemElement } from '@umbraco-cms/backoffice/menu';
|
|
3
|
+
/**
|
|
4
|
+
* Defines a sub menu item extension for the uSync menu
|
|
5
|
+
*/
|
|
6
|
+
export interface ManifestuSyncMenuItem extends ManifestElement<UmbMenuItemElement> {
|
|
7
|
+
type: 'usync-menuItem';
|
|
8
|
+
meta: MetaMenuItem;
|
|
9
|
+
}
|
|
10
|
+
declare global {
|
|
11
|
+
interface UmbExtensionManifestMap {
|
|
12
|
+
ManifestuSyncMenuItem: ManifestuSyncMenuItem;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { LitElement } from '@umbraco-cms/backoffice/external/lit';
|
|
2
|
+
import { ManifestMenuItem } from '@umbraco-cms/backoffice/menu';
|
|
3
|
+
declare const uSyncMenuElement_base: import("@umbraco-cms/backoffice/extension-api").HTMLElementConstructor<import("@umbraco-cms/backoffice/element-api").UmbElement> & typeof LitElement;
|
|
4
|
+
export declare class uSyncMenuElement extends uSyncMenuElement_base {
|
|
5
|
+
#private;
|
|
6
|
+
manifest: ManifestMenuItem;
|
|
7
|
+
hasChildren: boolean;
|
|
8
|
+
itemPath?: string;
|
|
9
|
+
constructor();
|
|
10
|
+
render(): import("lit-html").TemplateResult<1>;
|
|
11
|
+
renderChildren(): import("lit-html").TemplateResult<1>;
|
|
12
|
+
}
|
|
13
|
+
export default uSyncMenuElement;
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
|
|
8
|
+
if (kind === "m") throw new TypeError("Private method is not writable");
|
|
9
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
|
10
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
|
11
|
+
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
12
|
+
};
|
|
13
|
+
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
14
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
15
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
16
|
+
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
17
|
+
};
|
|
18
|
+
var _uSyncMenuElement_instances, _uSyncMenuElement_pathName, _uSyncMenuElement_constructHref;
|
|
19
|
+
import { UmbElementMixin } from '@umbraco-cms/backoffice/element-api';
|
|
20
|
+
import { umbExtensionsRegistry } from '@umbraco-cms/backoffice/extension-registry';
|
|
21
|
+
import { LitElement, customElement, html, property, state, } from '@umbraco-cms/backoffice/external/lit';
|
|
22
|
+
import { UMB_SECTION_CONTEXT } from '@umbraco-cms/backoffice/section';
|
|
23
|
+
let uSyncMenuElement = class uSyncMenuElement extends UmbElementMixin(LitElement) {
|
|
24
|
+
constructor() {
|
|
25
|
+
super();
|
|
26
|
+
_uSyncMenuElement_instances.add(this);
|
|
27
|
+
_uSyncMenuElement_pathName.set(this, void 0);
|
|
28
|
+
this.hasChildren = false;
|
|
29
|
+
umbExtensionsRegistry.byType('usync-menuItem').subscribe((_items) => {
|
|
30
|
+
this.hasChildren = _items.length > 0;
|
|
31
|
+
});
|
|
32
|
+
this.consumeContext(UMB_SECTION_CONTEXT, (sectionContext) => {
|
|
33
|
+
this.observe(sectionContext?.pathname, (pathName) => {
|
|
34
|
+
__classPrivateFieldSet(this, _uSyncMenuElement_pathName, pathName, "f");
|
|
35
|
+
__classPrivateFieldGet(this, _uSyncMenuElement_instances, "m", _uSyncMenuElement_constructHref).call(this);
|
|
36
|
+
}, 'observePathname');
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
render() {
|
|
40
|
+
return html `<umb-menu-item-layout
|
|
41
|
+
label=${this.manifest.meta.label ?? this.manifest.name}
|
|
42
|
+
icon-name=${this.manifest.meta.icon ?? 'icon-bug'}
|
|
43
|
+
.href=${this.itemPath}
|
|
44
|
+
?has-Children=${this.hasChildren}
|
|
45
|
+
>${this.renderChildren()}
|
|
46
|
+
</umb-menu-item-layout>`;
|
|
47
|
+
}
|
|
48
|
+
renderChildren() {
|
|
49
|
+
return html `<umb-extension-slot
|
|
50
|
+
type="usync-menuItem"
|
|
51
|
+
default-element="umb-menu-item-default"></umb-extension-slot>`;
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
_uSyncMenuElement_pathName = new WeakMap();
|
|
55
|
+
_uSyncMenuElement_instances = new WeakSet();
|
|
56
|
+
_uSyncMenuElement_constructHref = function _uSyncMenuElement_constructHref() {
|
|
57
|
+
if (!__classPrivateFieldGet(this, _uSyncMenuElement_pathName, "f"))
|
|
58
|
+
return;
|
|
59
|
+
this.itemPath = `section/${__classPrivateFieldGet(this, _uSyncMenuElement_pathName, "f")}/workspace/${this.manifest.meta.entityType}`;
|
|
60
|
+
};
|
|
61
|
+
__decorate([
|
|
62
|
+
property({ type: Object, attribute: false })
|
|
63
|
+
], uSyncMenuElement.prototype, "manifest", void 0);
|
|
64
|
+
__decorate([
|
|
65
|
+
state()
|
|
66
|
+
], uSyncMenuElement.prototype, "hasChildren", void 0);
|
|
67
|
+
__decorate([
|
|
68
|
+
state()
|
|
69
|
+
], uSyncMenuElement.prototype, "itemPath", void 0);
|
|
70
|
+
uSyncMenuElement = __decorate([
|
|
71
|
+
customElement('usync-menu')
|
|
72
|
+
], uSyncMenuElement);
|
|
73
|
+
export { uSyncMenuElement };
|
|
74
|
+
export default uSyncMenuElement;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export interface USyncChangeLabel {
|
|
2
|
+
label: string;
|
|
3
|
+
prefix?: string;
|
|
4
|
+
culture?: string;
|
|
5
|
+
raw: string;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Splits a `USyncChange.name` into a display label, an optional `Property -`
|
|
9
|
+
* prefix, and an optional trailing culture. When nothing matches, the name is
|
|
10
|
+
* left completely alone - other trackers produce arbitrary names and we must
|
|
11
|
+
* not mangle them.
|
|
12
|
+
*/
|
|
13
|
+
export declare function parseChangeName(name: string): USyncChangeLabel;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
// Tight language-tag pattern so a plain "(2)" (e.g. "Sort Order (2)") is never
|
|
2
|
+
// mistaken for a culture - a culture must start with 2-3 letters.
|
|
3
|
+
const CULTURE_SUFFIX = /^(.*?)\s*\(([A-Za-z]{2,3}(?:-[A-Za-z0-9]{2,8})*)\)\s*$/;
|
|
4
|
+
const PROPERTY_PREFIX = /^Property\s*-\s*/;
|
|
5
|
+
/**
|
|
6
|
+
* Splits a `USyncChange.name` into a display label, an optional `Property -`
|
|
7
|
+
* prefix, and an optional trailing culture. When nothing matches, the name is
|
|
8
|
+
* left completely alone - other trackers produce arbitrary names and we must
|
|
9
|
+
* not mangle them.
|
|
10
|
+
*/
|
|
11
|
+
export function parseChangeName(name) {
|
|
12
|
+
const cultureMatch = CULTURE_SUFFIX.exec(name);
|
|
13
|
+
let remaining = name;
|
|
14
|
+
let culture;
|
|
15
|
+
if (cultureMatch) {
|
|
16
|
+
remaining = cultureMatch[1];
|
|
17
|
+
culture = cultureMatch[2];
|
|
18
|
+
}
|
|
19
|
+
const prefixMatch = PROPERTY_PREFIX.exec(remaining);
|
|
20
|
+
let prefix;
|
|
21
|
+
let label = remaining;
|
|
22
|
+
if (prefixMatch) {
|
|
23
|
+
prefix = 'Property';
|
|
24
|
+
label = remaining.slice(prefixMatch[0].length);
|
|
25
|
+
}
|
|
26
|
+
if (!cultureMatch && !prefixMatch) {
|
|
27
|
+
return { label: name, raw: name };
|
|
28
|
+
}
|
|
29
|
+
return { label, prefix, culture, raw: name };
|
|
30
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { USyncChange } from '../../api/types.gen.js';
|
|
2
|
+
import { type USyncClassifiedChange } from './types.js';
|
|
3
|
+
export declare const USYNC_MASKED_VALUE = "*****";
|
|
4
|
+
export type USyncJsonContainerResult = {
|
|
5
|
+
ok: true;
|
|
6
|
+
value: unknown;
|
|
7
|
+
} | {
|
|
8
|
+
ok: false;
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* Parses `text` as a JSON *container* (object or array) only. Bare scalars
|
|
12
|
+
* such as `123` or `"hi"` parse successfully with `JSON.parse` but are never
|
|
13
|
+
* what we mean by "this is a JSON value to structurally diff" - a
|
|
14
|
+
* `textstring` of `123` must not be treated as JSON.
|
|
15
|
+
*/
|
|
16
|
+
export declare function tryParseJsonContainer(text: string): USyncJsonContainerResult;
|
|
17
|
+
/**
|
|
18
|
+
* Classifies a `USyncChange` detail into a diff strategy, in priority order.
|
|
19
|
+
* See the plan for the full rationale behind the ordering.
|
|
20
|
+
*/
|
|
21
|
+
export declare function classifyChange(detail: USyncChange): USyncClassifiedChange;
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { USYNC_DIFF_LIMITS } from './types.js';
|
|
2
|
+
// `ChangeDetailType` is an enum, and importing it as a value is not permitted
|
|
3
|
+
// here (only type-only imports from the generated API types are allowed) -
|
|
4
|
+
// so its members are matched against their known string values instead.
|
|
5
|
+
const CHANGE_NO_CHANGE = 'NoChange';
|
|
6
|
+
const CHANGE_ERROR = 'Error';
|
|
7
|
+
const CHANGE_WARNING = 'Warning';
|
|
8
|
+
export const USYNC_MASKED_VALUE = '*****';
|
|
9
|
+
const RAW_XML_DETAIL_NAME = 'Raw XML';
|
|
10
|
+
/**
|
|
11
|
+
* Parses `text` as a JSON *container* (object or array) only. Bare scalars
|
|
12
|
+
* such as `123` or `"hi"` parse successfully with `JSON.parse` but are never
|
|
13
|
+
* what we mean by "this is a JSON value to structurally diff" - a
|
|
14
|
+
* `textstring` of `123` must not be treated as JSON.
|
|
15
|
+
*/
|
|
16
|
+
export function tryParseJsonContainer(text) {
|
|
17
|
+
const trimmed = text.trim();
|
|
18
|
+
if (!(trimmed.startsWith('{') || trimmed.startsWith('[')))
|
|
19
|
+
return { ok: false };
|
|
20
|
+
try {
|
|
21
|
+
const value = JSON.parse(trimmed);
|
|
22
|
+
if (value === null || typeof value !== 'object')
|
|
23
|
+
return { ok: false };
|
|
24
|
+
return { ok: true, value };
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
return { ok: false };
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
function byteLength(text) {
|
|
31
|
+
return new TextEncoder().encode(text).length;
|
|
32
|
+
}
|
|
33
|
+
const XML_START = /^\s*</;
|
|
34
|
+
/**
|
|
35
|
+
* Classifies a `USyncChange` detail into a diff strategy, in priority order.
|
|
36
|
+
* See the plan for the full rationale behind the ordering.
|
|
37
|
+
*/
|
|
38
|
+
export function classifyChange(detail) {
|
|
39
|
+
const { oldValue, newValue, name } = detail;
|
|
40
|
+
const change = detail.change;
|
|
41
|
+
if (change === CHANGE_NO_CHANGE) {
|
|
42
|
+
return skip(oldValue, newValue);
|
|
43
|
+
}
|
|
44
|
+
if (change === CHANGE_ERROR) {
|
|
45
|
+
return notice(oldValue);
|
|
46
|
+
}
|
|
47
|
+
if (change === CHANGE_WARNING) {
|
|
48
|
+
return notice(newValue);
|
|
49
|
+
}
|
|
50
|
+
if (oldValue === USYNC_MASKED_VALUE || newValue === USYNC_MASKED_VALUE) {
|
|
51
|
+
return build('masked', oldValue, newValue, 'text');
|
|
52
|
+
}
|
|
53
|
+
const bytes = Math.max(byteLength(oldValue), byteLength(newValue));
|
|
54
|
+
const forceXml = name === RAW_XML_DETAIL_NAME;
|
|
55
|
+
if (bytes > USYNC_DIFF_LIMITS.maxDiffBytes) {
|
|
56
|
+
return build('oversized', oldValue, newValue, forceXml ? 'xml' : 'text', bytes);
|
|
57
|
+
}
|
|
58
|
+
if (oldValue === newValue) {
|
|
59
|
+
return skip(oldValue, newValue);
|
|
60
|
+
}
|
|
61
|
+
if (oldValue === '' || newValue === '') {
|
|
62
|
+
return build('single', oldValue, newValue, forceXml ? 'xml' : 'text', bytes);
|
|
63
|
+
}
|
|
64
|
+
const oldJson = tryParseJsonContainer(oldValue);
|
|
65
|
+
const newJson = tryParseJsonContainer(newValue);
|
|
66
|
+
if (oldJson.ok && newJson.ok) {
|
|
67
|
+
return {
|
|
68
|
+
strategy: 'json',
|
|
69
|
+
oldText: oldValue,
|
|
70
|
+
newText: newValue,
|
|
71
|
+
oldJson: oldJson.value,
|
|
72
|
+
newJson: newJson.value,
|
|
73
|
+
language: 'json',
|
|
74
|
+
bytes,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
if (forceXml || XML_START.test(oldValue) || XML_START.test(newValue)) {
|
|
78
|
+
return build('lines', oldValue, newValue, 'xml', bytes);
|
|
79
|
+
}
|
|
80
|
+
if (oldValue.includes('\n') || newValue.includes('\n')) {
|
|
81
|
+
return build('lines', oldValue, newValue, 'text', bytes);
|
|
82
|
+
}
|
|
83
|
+
if (bytes <= USYNC_DIFF_LIMITS.inlineScalarChars) {
|
|
84
|
+
return build('scalar', oldValue, newValue, 'text', bytes);
|
|
85
|
+
}
|
|
86
|
+
if (bytes <= USYNC_DIFF_LIMITS.maxWordDiffBytes) {
|
|
87
|
+
return build('words', oldValue, newValue, 'text', bytes);
|
|
88
|
+
}
|
|
89
|
+
return build('lines', oldValue, newValue, 'text', bytes);
|
|
90
|
+
}
|
|
91
|
+
function skip(oldValue, newValue) {
|
|
92
|
+
return build('skip', oldValue, newValue, 'text');
|
|
93
|
+
}
|
|
94
|
+
function notice(message) {
|
|
95
|
+
return {
|
|
96
|
+
strategy: 'notice',
|
|
97
|
+
oldText: message,
|
|
98
|
+
newText: message,
|
|
99
|
+
language: 'text',
|
|
100
|
+
bytes: byteLength(message),
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
function build(strategy, oldValue, newValue, language, bytes) {
|
|
104
|
+
return {
|
|
105
|
+
strategy,
|
|
106
|
+
oldText: oldValue,
|
|
107
|
+
newText: newValue,
|
|
108
|
+
language,
|
|
109
|
+
bytes: bytes ?? Math.max(byteLength(oldValue), byteLength(newValue)),
|
|
110
|
+
};
|
|
111
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A short tag describing the "shape" of a value, used to decide whether two
|
|
3
|
+
* values are even comparable as the same kind of thing (object vs array vs
|
|
4
|
+
* primitive) before doing a structural walk.
|
|
5
|
+
*/
|
|
6
|
+
export declare function typeTag(value: unknown): string;
|
|
7
|
+
export declare function isPlainObject(value: unknown): value is Record<string, unknown>;
|
|
8
|
+
/**
|
|
9
|
+
* Structural equality for JSON-shaped values (objects, arrays, strings,
|
|
10
|
+
* numbers, booleans, null). Array order is significant. Object key order is
|
|
11
|
+
* not.
|
|
12
|
+
*/
|
|
13
|
+
export declare function deepEqual(a: unknown, b: unknown): boolean;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A short tag describing the "shape" of a value, used to decide whether two
|
|
3
|
+
* values are even comparable as the same kind of thing (object vs array vs
|
|
4
|
+
* primitive) before doing a structural walk.
|
|
5
|
+
*/
|
|
6
|
+
export function typeTag(value) {
|
|
7
|
+
if (value === null)
|
|
8
|
+
return 'null';
|
|
9
|
+
if (Array.isArray(value))
|
|
10
|
+
return 'array';
|
|
11
|
+
const t = typeof value;
|
|
12
|
+
if (t === 'object')
|
|
13
|
+
return 'object';
|
|
14
|
+
return t;
|
|
15
|
+
}
|
|
16
|
+
export function isPlainObject(value) {
|
|
17
|
+
if (value === null || typeof value !== 'object')
|
|
18
|
+
return false;
|
|
19
|
+
if (Array.isArray(value))
|
|
20
|
+
return false;
|
|
21
|
+
const proto = Object.getPrototypeOf(value);
|
|
22
|
+
return proto === Object.prototype || proto === null;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Structural equality for JSON-shaped values (objects, arrays, strings,
|
|
26
|
+
* numbers, booleans, null). Array order is significant. Object key order is
|
|
27
|
+
* not.
|
|
28
|
+
*/
|
|
29
|
+
export function deepEqual(a, b) {
|
|
30
|
+
if (a === b)
|
|
31
|
+
return true;
|
|
32
|
+
if (Array.isArray(a) && Array.isArray(b)) {
|
|
33
|
+
if (a.length !== b.length)
|
|
34
|
+
return false;
|
|
35
|
+
for (let i = 0; i < a.length; i++) {
|
|
36
|
+
if (!deepEqual(a[i], b[i]))
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
return true;
|
|
40
|
+
}
|
|
41
|
+
if (isPlainObject(a) && isPlainObject(b)) {
|
|
42
|
+
const aKeys = Object.keys(a);
|
|
43
|
+
const bKeys = Object.keys(b);
|
|
44
|
+
if (aKeys.length !== bKeys.length)
|
|
45
|
+
return false;
|
|
46
|
+
for (const key of aKeys) {
|
|
47
|
+
if (!Object.prototype.hasOwnProperty.call(b, key))
|
|
48
|
+
return false;
|
|
49
|
+
if (!deepEqual(a[key], b[key]))
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
return true;
|
|
53
|
+
}
|
|
54
|
+
// Different shapes (one array one object, one object one primitive, etc.)
|
|
55
|
+
// or unequal primitives - already excluded by the `a === b` check above.
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Joins a path segment onto a base path, using `.key` for identifier-safe
|
|
3
|
+
* keys and `["odd key"]` otherwise.
|
|
4
|
+
*/
|
|
5
|
+
export declare function joinKey(base: string, key: string): string;
|
|
6
|
+
export interface USyncElementPathOptions {
|
|
7
|
+
/** Positional index, used when no identity label is available. */
|
|
8
|
+
index?: number;
|
|
9
|
+
/**
|
|
10
|
+
* A pre-built identity label (e.g. `headline (en-US)` for the
|
|
11
|
+
* alias+culture composite identity) - when set this *is* the element's
|
|
12
|
+
* identity, so no index/derived-label lookup happens.
|
|
13
|
+
*/
|
|
14
|
+
identityLabel?: string;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Builds a readable path segment for an array element, e.g. `[0: Hero]` for
|
|
18
|
+
* an index-matched element, or `[headline (en-US)]` for an element matched by
|
|
19
|
+
* a composite alias+culture identity.
|
|
20
|
+
*/
|
|
21
|
+
export declare function elementPath(base: string, element: unknown, options?: USyncElementPathOptions): string;
|
|
22
|
+
/**
|
|
23
|
+
* Truncates a long path in the middle, keeping the trailing leaf segment
|
|
24
|
+
* (the part the reader actually cares about) fully intact.
|
|
25
|
+
*/
|
|
26
|
+
export declare function formatDiffPath(path: string, maxLen?: number): string;
|
|
27
|
+
/**
|
|
28
|
+
* Short, display-agnostic stringification of a leaf value for inline
|
|
29
|
+
* rendering (e.g. `old → new`). Callers decide layout/markup; this only
|
|
30
|
+
* turns a value into text.
|
|
31
|
+
*/
|
|
32
|
+
export declare function formatLeafValue(value: unknown): string;
|
|
33
|
+
/** Pretty-prints a parsed JSON value the same way the server does. */
|
|
34
|
+
export declare function prettyJson(value: unknown): string;
|
|
35
|
+
/** Truncates a string in the middle to roughly `maxLen` characters. */
|
|
36
|
+
export declare function truncateMiddle(text: string, maxLen: number): string;
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { isPlainObject } from './deep-equal.js';
|
|
2
|
+
const IDENTIFIER_KEY = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
|
|
3
|
+
/**
|
|
4
|
+
* Joins a path segment onto a base path, using `.key` for identifier-safe
|
|
5
|
+
* keys and `["odd key"]` otherwise.
|
|
6
|
+
*/
|
|
7
|
+
export function joinKey(base, key) {
|
|
8
|
+
if (IDENTIFIER_KEY.test(key)) {
|
|
9
|
+
return base.length > 0 ? `${base}.${key}` : key;
|
|
10
|
+
}
|
|
11
|
+
return `${base}[${JSON.stringify(key)}]`;
|
|
12
|
+
}
|
|
13
|
+
const ELEMENT_LABEL_FIELDS = [
|
|
14
|
+
'name',
|
|
15
|
+
'alias',
|
|
16
|
+
'editorAlias',
|
|
17
|
+
'propertyAlias',
|
|
18
|
+
'contentTypeAlias',
|
|
19
|
+
'$type',
|
|
20
|
+
'culture',
|
|
21
|
+
];
|
|
22
|
+
function deriveElementLabel(element) {
|
|
23
|
+
if (!isPlainObject(element))
|
|
24
|
+
return undefined;
|
|
25
|
+
for (const field of ELEMENT_LABEL_FIELDS) {
|
|
26
|
+
const value = element[field];
|
|
27
|
+
if (typeof value === 'string' && value.length > 0)
|
|
28
|
+
return value;
|
|
29
|
+
}
|
|
30
|
+
return undefined;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Builds a readable path segment for an array element, e.g. `[0: Hero]` for
|
|
34
|
+
* an index-matched element, or `[headline (en-US)]` for an element matched by
|
|
35
|
+
* a composite alias+culture identity.
|
|
36
|
+
*/
|
|
37
|
+
export function elementPath(base, element, options = {}) {
|
|
38
|
+
if (options.identityLabel !== undefined) {
|
|
39
|
+
return `${base}[${options.identityLabel}]`;
|
|
40
|
+
}
|
|
41
|
+
const index = options.index ?? 0;
|
|
42
|
+
const label = deriveElementLabel(element);
|
|
43
|
+
return label !== undefined ? `${base}[${index}: ${label}]` : `${base}[${index}]`;
|
|
44
|
+
}
|
|
45
|
+
const LEAF_SEGMENT = /(\.[^.[\]]+|\[[^\]]*\])$/;
|
|
46
|
+
/**
|
|
47
|
+
* Truncates a long path in the middle, keeping the trailing leaf segment
|
|
48
|
+
* (the part the reader actually cares about) fully intact.
|
|
49
|
+
*/
|
|
50
|
+
export function formatDiffPath(path, maxLen = 120) {
|
|
51
|
+
if (path.length <= maxLen)
|
|
52
|
+
return path;
|
|
53
|
+
const leafMatch = LEAF_SEGMENT.exec(path);
|
|
54
|
+
const leaf = leafMatch ? leafMatch[0] : '';
|
|
55
|
+
const headLen = Math.max(maxLen - leaf.length - 1, 0);
|
|
56
|
+
const head = path.slice(0, headLen);
|
|
57
|
+
return `${head}…${leaf}`;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Short, display-agnostic stringification of a leaf value for inline
|
|
61
|
+
* rendering (e.g. `old → new`). Callers decide layout/markup; this only
|
|
62
|
+
* turns a value into text.
|
|
63
|
+
*/
|
|
64
|
+
export function formatLeafValue(value) {
|
|
65
|
+
if (value === undefined)
|
|
66
|
+
return 'undefined';
|
|
67
|
+
if (value === null)
|
|
68
|
+
return 'null';
|
|
69
|
+
if (typeof value === 'string')
|
|
70
|
+
return value;
|
|
71
|
+
if (typeof value === 'number' || typeof value === 'boolean')
|
|
72
|
+
return String(value);
|
|
73
|
+
return JSON.stringify(value);
|
|
74
|
+
}
|
|
75
|
+
/** Pretty-prints a parsed JSON value the same way the server does. */
|
|
76
|
+
export function prettyJson(value) {
|
|
77
|
+
return JSON.stringify(value, null, 1);
|
|
78
|
+
}
|
|
79
|
+
/** Truncates a string in the middle to roughly `maxLen` characters. */
|
|
80
|
+
export function truncateMiddle(text, maxLen) {
|
|
81
|
+
if (text.length <= maxLen)
|
|
82
|
+
return text;
|
|
83
|
+
const ellipsis = '…';
|
|
84
|
+
const keep = Math.max(maxLen - ellipsis.length, 0);
|
|
85
|
+
const headLen = Math.ceil(keep / 2);
|
|
86
|
+
const tailLen = Math.floor(keep / 2);
|
|
87
|
+
return `${text.slice(0, headLen)}${ellipsis}${text.slice(text.length - tailLen)}`;
|
|
88
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { type USyncJsonDiffResult } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Composite identities are tried before single fields, and in the order
|
|
4
|
+
* listed - a block's `values` entries and property values key on
|
|
5
|
+
* alias(+culture/segment) almost always, and that composite survives
|
|
6
|
+
* serialisation round-trips even when raw GUIDs don't.
|
|
7
|
+
*/
|
|
8
|
+
export declare const USYNC_COMPOSITE_IDENTITIES: string[][];
|
|
9
|
+
export declare const USYNC_IDENTITY_FIELDS: string[];
|
|
10
|
+
interface IdentityStrategy {
|
|
11
|
+
isComposite: boolean;
|
|
12
|
+
keyOf(element: Record<string, unknown>): string;
|
|
13
|
+
labelOf(element: Record<string, unknown>): string;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Picks an identity strategy for matching elements of two arrays by identity
|
|
17
|
+
* rather than position, or `null` when neither array offers one (the caller
|
|
18
|
+
* should fall back to index matching).
|
|
19
|
+
*/
|
|
20
|
+
export declare function pickIdentity(a: unknown[], b: unknown[]): IdentityStrategy | null;
|
|
21
|
+
export interface USyncJsonDiffOptions {
|
|
22
|
+
maxChanges?: number;
|
|
23
|
+
maxDepth?: number;
|
|
24
|
+
expandJsonStrings?: boolean;
|
|
25
|
+
maxStringExpansions?: number;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Structurally diffs two parsed JSON values, producing a flat list of leaf
|
|
29
|
+
* changes with readable paths instead of a wholesale before/after dump.
|
|
30
|
+
* Arrays are matched by identity where possible (see `pickIdentity`) so that
|
|
31
|
+
* inserting one block does not make every other block look changed.
|
|
32
|
+
*/
|
|
33
|
+
export declare function diffJsonValues(oldValue: unknown, newValue: unknown, options?: USyncJsonDiffOptions): USyncJsonDiffResult;
|
|
34
|
+
export {};
|