@micro-lc/preview 0.5.0-rc4 → 0.5.0-rc6
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 +5 -0
- package/dist/index.d.ts +23 -42
- package/dist/index.js +10 -97
- package/dist/service-worker.d.ts +4 -16
- package/dist/service-worker.js +146 -1
- package/dist/src/lib/handlebars.d.ts +2 -0
- package/dist/src/lib/handlebars.js +130 -0
- package/dist/src/lib/index.d.ts +110 -0
- package/dist/src/lib/index.js +38 -0
- package/package.json +11 -8
- package/website/assets/index-d6db66eb.js +13 -0
- package/website/assets/index-d846d55b.js +15 -0
- package/website/development/{index.js → assets/index-3ca91186.js} +613 -559
- package/website/development/assets/index-62118151.js +6685 -0
- package/website/development/index.html +1 -1
- package/website/development/manifest.json +1 -6
- package/website/development/service-worker.js +2108 -104
- package/website/index.html +1 -1
- package/website/manifest.json +1 -6
- package/website/service-worker.js +3 -1
- package/website/index.js +0 -13
package/CHANGELOG.md
CHANGED
package/dist/index.d.ts
CHANGED
@@ -1,5 +1,12 @@
|
|
1
1
|
import type { Manifest } from '@micro-lc/back-kit-engine/schemas';
|
2
2
|
import type { Component, PluginConfiguration } from '@micro-lc/interfaces/schemas/v2';
|
3
|
+
interface UrlMatchPair {
|
4
|
+
headers: Record<string, string>;
|
5
|
+
originalFrom: `${string}/`;
|
6
|
+
query: Record<string, string>;
|
7
|
+
to: `${string}/`;
|
8
|
+
}
|
9
|
+
type SourceMapItems = Record<`${string}/`, UrlMatchPair>;
|
3
10
|
interface TagInfo {
|
4
11
|
definitions?: Record<string, unknown>;
|
5
12
|
description?: string;
|
@@ -24,6 +31,7 @@ interface OptionMessage {
|
|
24
31
|
content: {
|
25
32
|
disableOverlay?: boolean;
|
26
33
|
redirectTo?: string;
|
34
|
+
run?: boolean;
|
27
35
|
timeout?: number;
|
28
36
|
};
|
29
37
|
type: 'options';
|
@@ -39,19 +47,21 @@ type RegisteredElementMessages = {
|
|
39
47
|
content: StyledElementContent | undefined;
|
40
48
|
type: 'focus-element';
|
41
49
|
};
|
50
|
+
interface NewConfiguration {
|
51
|
+
configuration: PluginConfiguration;
|
52
|
+
contexts: Map<string, Component[]>;
|
53
|
+
tags: string[];
|
54
|
+
}
|
42
55
|
interface NewConfigurationMessage {
|
43
|
-
content:
|
44
|
-
configuration: PluginConfiguration;
|
45
|
-
contexts: Map<string, Component[]>;
|
46
|
-
tags: string[];
|
47
|
-
};
|
56
|
+
content: NewConfiguration;
|
48
57
|
type: 'new-configuration';
|
49
58
|
}
|
59
|
+
interface UpdateConfiguration {
|
60
|
+
configuration: PluginConfiguration;
|
61
|
+
context: Component;
|
62
|
+
}
|
50
63
|
interface UpdateConfigurationMessage {
|
51
|
-
content:
|
52
|
-
configuration: PluginConfiguration;
|
53
|
-
context: Component;
|
54
|
-
};
|
64
|
+
content: UpdateConfiguration;
|
55
65
|
type: 'update';
|
56
66
|
}
|
57
67
|
interface UpdatedMessage {
|
@@ -60,24 +70,10 @@ interface UpdatedMessage {
|
|
60
70
|
}
|
61
71
|
type RegisteredConfigMessages = NewConfigurationMessage | UpdateConfigurationMessage;
|
62
72
|
type ReducedMouseEvent = Pick<MouseEvent, 'bubbles' | 'cancelable' | 'clientX' | 'clientY'>;
|
63
|
-
interface ServiceWorkerConfig {
|
64
|
-
dictionary: Record<`${string}/`, {
|
65
|
-
headers: Record<string, string>;
|
66
|
-
query: Record<string, string>;
|
67
|
-
to: `${string}/`;
|
68
|
-
}>;
|
69
|
-
scope?: string;
|
70
|
-
}
|
71
73
|
interface SourceMapMessage {
|
72
|
-
content:
|
74
|
+
content: SourceMapItems;
|
73
75
|
type: 'set-source-map';
|
74
76
|
}
|
75
|
-
interface ServiceWorkerMessage {
|
76
|
-
content: {
|
77
|
-
status: 'ready';
|
78
|
-
};
|
79
|
-
type: 'service-worker';
|
80
|
-
}
|
81
77
|
type RegisteredMessages = {
|
82
78
|
content: ReducedMouseEvent;
|
83
79
|
type: 'mousedown' | 'mousemove';
|
@@ -92,7 +88,7 @@ type RegisteredMessages = {
|
|
92
88
|
} | {
|
93
89
|
content: InfoEntry[];
|
94
90
|
type: 'tag-info';
|
95
|
-
} | RegisteredConfigMessages | UpdatedMessage | RegisteredElementMessages | OptionMessage | RequestResource | SourceMapMessage
|
91
|
+
} | RegisteredConfigMessages | UpdatedMessage | RegisteredElementMessages | OptionMessage | RequestResource | SourceMapMessage;
|
96
92
|
interface NotifyProps extends Record<string, unknown> {
|
97
93
|
data?: Record<string, string>;
|
98
94
|
description: string;
|
@@ -115,21 +111,6 @@ declare const MIA_PREVIEW_ID = "__mia_preview_id";
|
|
115
111
|
declare const OVERLAY_Z_INDEX = 1000000;
|
116
112
|
declare const DEFAULT_MODE = "select";
|
117
113
|
declare function isRegisteredMessage(input: unknown): input is RegisteredMessages;
|
118
|
-
|
119
|
-
|
120
|
-
private __instance;
|
121
|
-
private __handler;
|
122
|
-
private __window;
|
123
|
-
private __randomColor;
|
124
|
-
private postMessage;
|
125
|
-
constructor(handler: (this: PostChannel, message: RegisteredMessages) => void);
|
126
|
-
set instance(str: string);
|
127
|
-
get window(): Window;
|
128
|
-
set window(win: Window);
|
129
|
-
send(to: Window, message: RegisteredMessages, origin?: string): void;
|
130
|
-
recv(window: Window, from?: Window | null): () => void;
|
131
|
-
}
|
132
|
-
export type { OverlayMode, TagInfo, InfoEntry, ElementContent, StyledElementContent, NewConfigurationMessage, UpdateConfigurationMessage, RegisteredConfigMessages, RegisteredMessages, OptionMessage, NotifyProps, Reset, Update, RequestResource, SourceMapMessage, ServiceWorkerConfig, };
|
133
|
-
export type * from './service-worker';
|
134
|
-
export { MIA_PREVIEW_ID, OVERLAY_Z_INDEX, DEFAULT_MODE, generateDarkColorHex, isRegisteredMessage, PostChannel, };
|
114
|
+
export type { NewConfiguration, UpdateConfiguration, OverlayMode, TagInfo, InfoEntry, ElementContent, StyledElementContent, NewConfigurationMessage, UpdateConfigurationMessage, RegisteredConfigMessages, RegisteredMessages, OptionMessage, NotifyProps, Reset, Update, RequestResource, SourceMapItems, UrlMatchPair, };
|
115
|
+
export { MIA_PREVIEW_ID, OVERLAY_Z_INDEX, DEFAULT_MODE, isRegisteredMessage, };
|
135
116
|
export * from './handlebars';
|
package/dist/index.js
CHANGED
@@ -7,20 +7,19 @@ const MIA_PREVIEW_ID = '__mia_preview_id';
|
|
7
7
|
const OVERLAY_Z_INDEX = 1_000_000;
|
8
8
|
const DEFAULT_MODE = 'select';
|
9
9
|
const keys = {
|
10
|
-
'click-element': DebugMessage.
|
11
|
-
'ctrl-space': DebugMessage.
|
10
|
+
'click-element': DebugMessage.Default,
|
11
|
+
'ctrl-space': DebugMessage.Default,
|
12
12
|
'focus-element': DebugMessage.Default,
|
13
|
-
mousedown: DebugMessage.
|
13
|
+
mousedown: DebugMessage.Default,
|
14
14
|
mousemove: DebugMessage.Skip,
|
15
15
|
'new-configuration': DebugMessage.Default,
|
16
|
-
notification: DebugMessage.
|
17
|
-
options: DebugMessage.
|
18
|
-
'request-resource': DebugMessage.
|
19
|
-
'service-worker': DebugMessage.Skip,
|
16
|
+
notification: DebugMessage.Default,
|
17
|
+
options: DebugMessage.Default,
|
18
|
+
'request-resource': DebugMessage.Default,
|
20
19
|
'set-source-map': DebugMessage.Default,
|
21
|
-
'tag-info': DebugMessage.
|
22
|
-
update: DebugMessage.
|
23
|
-
updated: DebugMessage.
|
20
|
+
'tag-info': DebugMessage.Default,
|
21
|
+
update: DebugMessage.Default,
|
22
|
+
updated: DebugMessage.Default,
|
24
23
|
};
|
25
24
|
const isValidKey = (type) => Object.keys(keys).includes(type);
|
26
25
|
function isRegisteredMessage(input) {
|
@@ -35,91 +34,5 @@ function isRegisteredMessage(input) {
|
|
35
34
|
}
|
36
35
|
return true;
|
37
36
|
}
|
38
|
-
|
39
|
-
if (input === null) {
|
40
|
-
return false;
|
41
|
-
}
|
42
|
-
if (typeof input !== 'object') {
|
43
|
-
return false;
|
44
|
-
}
|
45
|
-
if (!('type' in input) || typeof input.type !== 'string') {
|
46
|
-
return false;
|
47
|
-
}
|
48
|
-
const { type: signedType } = input;
|
49
|
-
if (!signedType.startsWith(signature)) {
|
50
|
-
return false;
|
51
|
-
}
|
52
|
-
const type = signedType.substring(signature.length);
|
53
|
-
if (!isValidKey(type)) {
|
54
|
-
return false;
|
55
|
-
}
|
56
|
-
return true;
|
57
|
-
}
|
58
|
-
const sign = (signature, message) => ({ ...message, type: `${signature}${message.type}` });
|
59
|
-
const unsign = (signature, message) => {
|
60
|
-
let { type } = message;
|
61
|
-
if (type.startsWith(signature)) {
|
62
|
-
type = type.substring(signature.length);
|
63
|
-
}
|
64
|
-
return { ...message, type };
|
65
|
-
};
|
66
|
-
const generateDarkColorHex = () => {
|
67
|
-
let color = '#';
|
68
|
-
for (let i = 0; i < 3; i++) {
|
69
|
-
color += (`0${Math.floor(Math.random() * Math.pow(16, 2) / 2).toString(16)}`).slice(-2);
|
70
|
-
}
|
71
|
-
return color;
|
72
|
-
};
|
73
|
-
class PostChannel {
|
74
|
-
__instance;
|
75
|
-
__handler;
|
76
|
-
__window;
|
77
|
-
__randomColor;
|
78
|
-
postMessage(to, message, origin) {
|
79
|
-
if (import.meta.env.MODE !== 'production' || this.__window.__BACKOFFICE_CONFIGURATOR_LOG_LEVEL__ === 'debug') {
|
80
|
-
console.assert(to !== this.__window);
|
81
|
-
}
|
82
|
-
if ( /* (import.meta.env.MODE !== 'production' || this.__window.__BACKOFFICE_CONFIGURATOR_LOG_LEVEL__ === 'debug') && */keys[message.type] !== DebugMessage.Skip) {
|
83
|
-
const color = this.__randomColor;
|
84
|
-
const background = `${color}22`;
|
85
|
-
const style = { background, color };
|
86
|
-
const hasTop = this.__window.top !== this.__window;
|
87
|
-
console.groupCollapsed(`%c Msg from ${this.__window.origin} ${hasTop ? '(inner)' : '(top)'} `, Object.entries(style).map(([key, val]) => `${key}: ${val}`).join('; '));
|
88
|
-
console.info(`window '${this.__window.origin}' is sending a message of type %c ${message.type} `, 'background: lightgreen; color: darkgreen');
|
89
|
-
console.log('to', to);
|
90
|
-
console.log(message.content);
|
91
|
-
console.groupEnd();
|
92
|
-
}
|
93
|
-
to.postMessage(sign(this.__instance, message), origin);
|
94
|
-
}
|
95
|
-
constructor(handler) {
|
96
|
-
this.__instance = '';
|
97
|
-
this.__handler = handler;
|
98
|
-
this.__window = window;
|
99
|
-
this.__randomColor = generateDarkColorHex();
|
100
|
-
}
|
101
|
-
set instance(str) {
|
102
|
-
this.__instance = str;
|
103
|
-
}
|
104
|
-
get window() {
|
105
|
-
return this.__window;
|
106
|
-
}
|
107
|
-
set window(win) {
|
108
|
-
this.__window = win;
|
109
|
-
}
|
110
|
-
send(to, message, origin = '*') {
|
111
|
-
this.__window !== to && this.postMessage(to, message, origin);
|
112
|
-
}
|
113
|
-
recv(window, from = null) {
|
114
|
-
const listener = ({ data, source }) => {
|
115
|
-
if ((from === null || source === from) && isInstanceMessage(data, this.__instance)) {
|
116
|
-
const message = unsign(this.__instance, data);
|
117
|
-
this.__handler(message);
|
118
|
-
}
|
119
|
-
};
|
120
|
-
window.addEventListener('message', listener);
|
121
|
-
return () => window.removeEventListener('message', listener);
|
122
|
-
}
|
123
|
-
}
|
124
|
-
export { MIA_PREVIEW_ID, OVERLAY_Z_INDEX, DEFAULT_MODE, generateDarkColorHex, isRegisteredMessage, PostChannel, };
|
37
|
+
export { MIA_PREVIEW_ID, OVERLAY_Z_INDEX, DEFAULT_MODE, isRegisteredMessage, };
|
125
38
|
export * from './handlebars';
|
package/dist/service-worker.d.ts
CHANGED
@@ -1,28 +1,16 @@
|
|
1
|
+
/// <reference lib="webworker" />
|
1
2
|
interface UrlMatchPair {
|
2
3
|
headers: Record<string, string>;
|
3
4
|
originalFrom: `${string}/`;
|
4
5
|
query: Record<string, string>;
|
5
6
|
to: `${string}/`;
|
6
7
|
}
|
7
|
-
|
8
|
-
|
9
|
-
scope?: string;
|
10
|
-
}
|
11
|
-
type WorkerMessage = {
|
8
|
+
type SourceMapItems = Record<`${string}/`, UrlMatchPair>;
|
9
|
+
type IncomingWorkerMessage = {
|
12
10
|
content: SourceMapItems;
|
13
11
|
type: 'set-source-map';
|
14
|
-
} | {
|
15
|
-
content: {
|
16
|
-
clientId: string;
|
17
|
-
};
|
18
|
-
type: 'source-map-ack';
|
19
|
-
} | {
|
20
|
-
content: {
|
21
|
-
status: 'ready';
|
22
|
-
};
|
23
|
-
type: 'service-worker';
|
24
12
|
} | {
|
25
13
|
content: Record<string, never>;
|
26
14
|
type: 'unload-client';
|
27
15
|
};
|
28
|
-
export type { SourceMapItems, UrlMatchPair,
|
16
|
+
export type { IncomingWorkerMessage, SourceMapItems, UrlMatchPair, };
|
package/dist/service-worker.js
CHANGED
@@ -1 +1,146 @@
|
|
1
|
-
|
1
|
+
import PostChannel, { fromServiceWorkerToReceiver } from '@micro-lc/post-channel';
|
2
|
+
import { ReplaySubject } from 'rxjs';
|
3
|
+
// every sw client opens a dedicated channel
|
4
|
+
// this map collects, per client, a buffer and the communication channel itself
|
5
|
+
const openChannels = new Map();
|
6
|
+
// source maps can be stored by unique
|
7
|
+
// key which is either the scope or the clientId
|
8
|
+
const sourceMap = new Map();
|
9
|
+
// reverse map
|
10
|
+
const tosMap = new Map();
|
11
|
+
const isAvailable = (key, map) => {
|
12
|
+
const iter = map.keys();
|
13
|
+
let matchingKey;
|
14
|
+
let { done = false, value } = iter.next();
|
15
|
+
while (!done && matchingKey === undefined) {
|
16
|
+
if (value !== undefined && key.startsWith(value)) {
|
17
|
+
matchingKey = value;
|
18
|
+
done = true;
|
19
|
+
}
|
20
|
+
const { value: nextValue, done: nextDone = false } = iter.next();
|
21
|
+
done = nextDone;
|
22
|
+
value = nextValue;
|
23
|
+
}
|
24
|
+
return matchingKey;
|
25
|
+
};
|
26
|
+
const isWindowClient = (source) => source !== null && 'id' in source;
|
27
|
+
const errorCatcher = (clientId) => (error) => console.error(`[SW]: error on ${clientId} - ${String(error)}`);
|
28
|
+
const getReceiver = fromServiceWorkerToReceiver(self, { errorCatcher });
|
29
|
+
const makeListener = (clientId) => {
|
30
|
+
return function listener({ data }) {
|
31
|
+
// todo: missing validation
|
32
|
+
const workerMessageData = data;
|
33
|
+
switch (workerMessageData.type) {
|
34
|
+
case 'set-source-map': {
|
35
|
+
const { content } = workerMessageData;
|
36
|
+
const clientMaps = Object.entries(content).reduce((maps, [key, value]) => {
|
37
|
+
const { clientSourceMap, clientTosMap } = maps;
|
38
|
+
if (Object.keys(value.headers).length > 0) {
|
39
|
+
clientTosMap.set(encodeURI(value.to), value.headers);
|
40
|
+
}
|
41
|
+
clientSourceMap.set(encodeURI(key), value);
|
42
|
+
return maps;
|
43
|
+
}, { clientSourceMap: new Map(), clientTosMap: new Map() });
|
44
|
+
sourceMap.set(clientId, clientMaps.clientSourceMap);
|
45
|
+
tosMap.set(clientId, clientMaps.clientTosMap);
|
46
|
+
break;
|
47
|
+
}
|
48
|
+
case 'unload-client': {
|
49
|
+
openChannels.delete(clientId);
|
50
|
+
this.disconnect();
|
51
|
+
break;
|
52
|
+
}
|
53
|
+
default:
|
54
|
+
break;
|
55
|
+
}
|
56
|
+
};
|
57
|
+
};
|
58
|
+
const createChannel = (clientId) => {
|
59
|
+
const listenerToSubscription = new Map();
|
60
|
+
const buffer = new ReplaySubject();
|
61
|
+
const sender = {
|
62
|
+
addEventListener(_, listener) {
|
63
|
+
listenerToSubscription.set(listener, buffer.subscribe((msg) => listener(msg)));
|
64
|
+
},
|
65
|
+
removeEventListener(_, listener) {
|
66
|
+
listenerToSubscription.get(listener)?.unsubscribe();
|
67
|
+
listenerToSubscription.delete(listener);
|
68
|
+
},
|
69
|
+
};
|
70
|
+
return {
|
71
|
+
buffer,
|
72
|
+
channel: new PostChannel(makeListener(clientId), sender, getReceiver(clientId)),
|
73
|
+
};
|
74
|
+
};
|
75
|
+
const getCachedOpenChannel = (clientId) => {
|
76
|
+
let ch = openChannels.get(clientId);
|
77
|
+
if (!ch) {
|
78
|
+
ch = createChannel(clientId);
|
79
|
+
openChannels.set(clientId, ch);
|
80
|
+
}
|
81
|
+
return ch;
|
82
|
+
};
|
83
|
+
self.addEventListener('install', () => {
|
84
|
+
// on service-worker.js update => immediate kill switch and update
|
85
|
+
// https://whatwebcando.today/articles/handling-service-worker-updates/
|
86
|
+
// https://stackoverflow.com/questions/33986976/how-can-i-remove-a-buggy-service-worker-or-implement-a-kill-switch/38980776#38980776
|
87
|
+
self.skipWaiting()
|
88
|
+
.catch((err) => console.error(`[SW] error while skipWaiting - ${String(err)}`));
|
89
|
+
});
|
90
|
+
self.addEventListener('activate', (event) => {
|
91
|
+
// all fetch events should be handled by the
|
92
|
+
// service worker as soon as active
|
93
|
+
event.waitUntil(self.clients.claim());
|
94
|
+
});
|
95
|
+
self.addEventListener('message', (message) => {
|
96
|
+
// messages allowed are `set-source-map` which replies with an
|
97
|
+
// acknowledgment `source-map-ack` that informs the client
|
98
|
+
// all is set to reverse proxy their fetch calls
|
99
|
+
const { source } = message;
|
100
|
+
if (!isWindowClient(source)) {
|
101
|
+
return;
|
102
|
+
}
|
103
|
+
const { id: clientId } = source;
|
104
|
+
getCachedOpenChannel(clientId).buffer.next(message);
|
105
|
+
});
|
106
|
+
self.addEventListener('fetch', (event) => {
|
107
|
+
const clientId = event.clientId !== '' ? event.clientId : event.resultingClientId;
|
108
|
+
const clientSourceMap = sourceMap.get(clientId);
|
109
|
+
if (!clientSourceMap) {
|
110
|
+
return event.respondWith(self.fetch(event.request));
|
111
|
+
}
|
112
|
+
// there must be a tosMap is clientSourceMap ain't undefined
|
113
|
+
const clientTosMap = tosMap.get(clientId);
|
114
|
+
let { request: { url } } = event;
|
115
|
+
const key = isAvailable(url, clientSourceMap);
|
116
|
+
const to = isAvailable(url, clientTosMap);
|
117
|
+
const headers = new Headers(event.request.headers);
|
118
|
+
let extraQuery = {};
|
119
|
+
if (key !== undefined) {
|
120
|
+
const value = clientSourceMap.get(key)?.to;
|
121
|
+
const extraHeaders = clientSourceMap.get(key)?.headers;
|
122
|
+
const rest = url.substring(key.length);
|
123
|
+
extraQuery = clientSourceMap.get(key)?.query;
|
124
|
+
url = `${value}${rest}`;
|
125
|
+
Object.entries(extraHeaders).forEach(([headerKey, headerValue]) => headers.set(headerKey, headerValue));
|
126
|
+
}
|
127
|
+
if (to !== undefined) {
|
128
|
+
const extraHeaders = clientTosMap.get(to);
|
129
|
+
Object.entries(extraHeaders).forEach(([headerKey, headerValue]) => headers.set(headerKey, headerValue));
|
130
|
+
}
|
131
|
+
let finalUrl;
|
132
|
+
try {
|
133
|
+
const urlInstance = new URL(url);
|
134
|
+
Object.entries(extraQuery).forEach(([qk, qv]) => urlInstance.searchParams.set(qk, qv));
|
135
|
+
finalUrl = urlInstance.href;
|
136
|
+
}
|
137
|
+
catch {
|
138
|
+
finalUrl = url;
|
139
|
+
}
|
140
|
+
console.log({ clientId, clientSourceMap, finalUrl });
|
141
|
+
event.respondWith(self.fetch(finalUrl, { ...event.request, headers })
|
142
|
+
.catch((error) => {
|
143
|
+
console.error(error);
|
144
|
+
return Promise.reject(error);
|
145
|
+
}));
|
146
|
+
});
|
@@ -0,0 +1,130 @@
|
|
1
|
+
var LexerState;
|
2
|
+
(function (LexerState) {
|
3
|
+
LexerState[LexerState["Literal"] = 0] = "Literal";
|
4
|
+
LexerState[LexerState["Normal"] = 1] = "Normal";
|
5
|
+
})(LexerState || (LexerState = {}));
|
6
|
+
class Lexer {
|
7
|
+
_input;
|
8
|
+
_length;
|
9
|
+
_idx = 0;
|
10
|
+
_mode = LexerState.Literal;
|
11
|
+
_literals = [];
|
12
|
+
_variables = [];
|
13
|
+
_braketCount = 0;
|
14
|
+
_done = false;
|
15
|
+
constructor(input) {
|
16
|
+
this._input = input;
|
17
|
+
this._length = input.length;
|
18
|
+
}
|
19
|
+
concatToLastLiteral(concat) {
|
20
|
+
// SAFETY: _literals here is always at least long 1
|
21
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
22
|
+
this._literals.push(this._literals.pop().concat(concat));
|
23
|
+
}
|
24
|
+
concatToLastVariable(concat) {
|
25
|
+
// SAFETY: _variables here is always at least long 1
|
26
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
27
|
+
this._variables.push(this._variables.pop().concat(concat));
|
28
|
+
}
|
29
|
+
get() {
|
30
|
+
const rawLiterals = [...this._literals];
|
31
|
+
const literals = Object.assign([], rawLiterals);
|
32
|
+
Object.defineProperty(literals, 'raw', { value: rawLiterals, writable: false });
|
33
|
+
return {
|
34
|
+
literals: literals,
|
35
|
+
variables: this._variables,
|
36
|
+
};
|
37
|
+
}
|
38
|
+
run() {
|
39
|
+
if (this._length === 0) {
|
40
|
+
this._literals.push('');
|
41
|
+
}
|
42
|
+
while (this._idx < this._length) {
|
43
|
+
const char = this._input.charAt(this._idx);
|
44
|
+
switch (this._mode) {
|
45
|
+
case LexerState.Literal: {
|
46
|
+
if (this._literals.length === 0) {
|
47
|
+
this._literals.push('');
|
48
|
+
}
|
49
|
+
if (char === '{'
|
50
|
+
&& this._idx + 1 !== this._length
|
51
|
+
&& this._input.charAt(this._idx + 1) === '{') {
|
52
|
+
this._literals.push('');
|
53
|
+
this._variables.push('');
|
54
|
+
this._mode = LexerState.Normal;
|
55
|
+
this._idx += 1;
|
56
|
+
}
|
57
|
+
else {
|
58
|
+
this.concatToLastLiteral(char);
|
59
|
+
}
|
60
|
+
break;
|
61
|
+
}
|
62
|
+
case LexerState.Normal:
|
63
|
+
if (char === '}' && this._input.charAt(this._idx + 1) === '}') {
|
64
|
+
this._mode = LexerState.Literal;
|
65
|
+
this._idx += 1;
|
66
|
+
}
|
67
|
+
else {
|
68
|
+
this.concatToLastVariable(char);
|
69
|
+
}
|
70
|
+
break;
|
71
|
+
default:
|
72
|
+
break;
|
73
|
+
}
|
74
|
+
this._idx += 1;
|
75
|
+
}
|
76
|
+
if (this._mode === LexerState.Normal) {
|
77
|
+
this._literals.pop();
|
78
|
+
// SAFETY: _variables here is always at least long 1
|
79
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
80
|
+
this.concatToLastLiteral(`{{${this._variables.pop()}`);
|
81
|
+
}
|
82
|
+
this._done = true;
|
83
|
+
}
|
84
|
+
}
|
85
|
+
const isSquareBrakets = (field) => {
|
86
|
+
const trimmed = field.trim();
|
87
|
+
if (!(trimmed.startsWith('[') && trimmed.endsWith(']'))) {
|
88
|
+
return false;
|
89
|
+
}
|
90
|
+
const inner = trimmed.slice(1).slice(0, -1).trim();
|
91
|
+
if (Number.parseInt(inner).toString(10) !== inner) {
|
92
|
+
return false;
|
93
|
+
}
|
94
|
+
return true;
|
95
|
+
};
|
96
|
+
const getNumber = (field) => {
|
97
|
+
return Number.parseInt(field.trim().slice(1).slice(0, -1)
|
98
|
+
.trim());
|
99
|
+
};
|
100
|
+
function interpolate(context, path, preserveUnknown) {
|
101
|
+
if (!path.trim()) {
|
102
|
+
return preserveUnknown ? `{{${path}}}` : '';
|
103
|
+
}
|
104
|
+
const result = path.trim().split('.').reduce((superctx, field) => {
|
105
|
+
if (Array.isArray(superctx) && isSquareBrakets(field)) {
|
106
|
+
return superctx[getNumber(field)];
|
107
|
+
}
|
108
|
+
if (typeof superctx === 'object' && superctx !== null && field in superctx) {
|
109
|
+
return superctx[field];
|
110
|
+
}
|
111
|
+
return undefined;
|
112
|
+
}, { ...context });
|
113
|
+
if (typeof result === 'string') {
|
114
|
+
return result;
|
115
|
+
}
|
116
|
+
return preserveUnknown ? `{{${path}}}` : '';
|
117
|
+
}
|
118
|
+
export function parse(input, context, preserveUnknown = false) {
|
119
|
+
const lexer = new Lexer(input);
|
120
|
+
lexer.run();
|
121
|
+
const template = lexer.get();
|
122
|
+
let [acc] = template.literals;
|
123
|
+
for (let i = 0; i < template.variables.length; i++) {
|
124
|
+
acc = acc
|
125
|
+
.concat(interpolate(context, template.variables[i], preserveUnknown))
|
126
|
+
.concat(template.literals[i + 1]);
|
127
|
+
}
|
128
|
+
return acc;
|
129
|
+
}
|
130
|
+
export const compileObject = (obj, context) => Object.fromEntries(Object.entries(obj).map(([key, value]) => [key, parse(value, context)]));
|
@@ -0,0 +1,110 @@
|
|
1
|
+
import type { Manifest } from '@micro-lc/back-kit-engine/schemas';
|
2
|
+
import type { Component, PluginConfiguration } from '@micro-lc/interfaces/schemas/v2';
|
3
|
+
import type { SourceMapItems } from '../../service-worker';
|
4
|
+
interface TagInfo {
|
5
|
+
definitions?: Record<string, unknown>;
|
6
|
+
description?: string;
|
7
|
+
docLink?: string;
|
8
|
+
label?: string;
|
9
|
+
properties?: Manifest['properties'];
|
10
|
+
type?: Manifest['type'];
|
11
|
+
}
|
12
|
+
type OverlayMode = 0 | 'interact' | 'select';
|
13
|
+
interface InfoEntry {
|
14
|
+
info: TagInfo;
|
15
|
+
tag: string;
|
16
|
+
}
|
17
|
+
interface ElementContent {
|
18
|
+
ids: string[];
|
19
|
+
selectors: string[];
|
20
|
+
}
|
21
|
+
interface StyledElementContent extends ElementContent {
|
22
|
+
style: Partial<CSSStyleDeclaration>;
|
23
|
+
}
|
24
|
+
interface OptionMessage {
|
25
|
+
content: {
|
26
|
+
disableOverlay?: boolean;
|
27
|
+
redirectTo?: string;
|
28
|
+
run?: boolean;
|
29
|
+
timeout?: number;
|
30
|
+
};
|
31
|
+
type: 'options';
|
32
|
+
}
|
33
|
+
interface RequestResource {
|
34
|
+
content: Record<string, unknown>;
|
35
|
+
type: 'request-resource';
|
36
|
+
}
|
37
|
+
type RegisteredElementMessages = {
|
38
|
+
content: ElementContent | undefined;
|
39
|
+
type: 'click-element';
|
40
|
+
} | {
|
41
|
+
content: StyledElementContent | undefined;
|
42
|
+
type: 'focus-element';
|
43
|
+
};
|
44
|
+
interface NewConfiguration {
|
45
|
+
configuration: PluginConfiguration;
|
46
|
+
contexts: Map<string, Component[]>;
|
47
|
+
tags: string[];
|
48
|
+
}
|
49
|
+
interface NewConfigurationMessage {
|
50
|
+
content: NewConfiguration;
|
51
|
+
type: 'new-configuration';
|
52
|
+
}
|
53
|
+
interface UpdateConfiguration {
|
54
|
+
configuration: PluginConfiguration;
|
55
|
+
context: Component;
|
56
|
+
}
|
57
|
+
interface UpdateConfigurationMessage {
|
58
|
+
content: UpdateConfiguration;
|
59
|
+
type: 'update';
|
60
|
+
}
|
61
|
+
interface UpdatedMessage {
|
62
|
+
content: Record<string, never>;
|
63
|
+
type: 'updated';
|
64
|
+
}
|
65
|
+
type RegisteredConfigMessages = NewConfigurationMessage | UpdateConfigurationMessage;
|
66
|
+
type ReducedMouseEvent = Pick<MouseEvent, 'bubbles' | 'cancelable' | 'clientX' | 'clientY'>;
|
67
|
+
interface SourceMapMessage {
|
68
|
+
content: SourceMapItems;
|
69
|
+
type: 'set-source-map';
|
70
|
+
}
|
71
|
+
type RegisteredMessages = {
|
72
|
+
content: ReducedMouseEvent;
|
73
|
+
type: 'mousedown' | 'mousemove';
|
74
|
+
} | {
|
75
|
+
content: NotifyProps;
|
76
|
+
type: 'notification';
|
77
|
+
} | {
|
78
|
+
content: {
|
79
|
+
mode: OverlayMode;
|
80
|
+
};
|
81
|
+
type: 'ctrl-space';
|
82
|
+
} | {
|
83
|
+
content: InfoEntry[];
|
84
|
+
type: 'tag-info';
|
85
|
+
} | RegisteredConfigMessages | UpdatedMessage | RegisteredElementMessages | OptionMessage | RequestResource | SourceMapMessage;
|
86
|
+
interface NotifyProps extends Record<string, unknown> {
|
87
|
+
data?: Record<string, string>;
|
88
|
+
description: string;
|
89
|
+
message: string;
|
90
|
+
status: 'info' | 'error' | 'default' | 'success' | 'processing' | 'warning' | undefined;
|
91
|
+
}
|
92
|
+
interface Reset {
|
93
|
+
configuration: PluginConfiguration;
|
94
|
+
contexts: Map<string, Component[]>;
|
95
|
+
tags: string[];
|
96
|
+
type: 'reset';
|
97
|
+
}
|
98
|
+
interface SingleUpdate {
|
99
|
+
configuration: PluginConfiguration;
|
100
|
+
context: Component;
|
101
|
+
type: 'update';
|
102
|
+
}
|
103
|
+
type Update = Reset | SingleUpdate;
|
104
|
+
declare const MIA_PREVIEW_ID = "__mia_preview_id";
|
105
|
+
declare const OVERLAY_Z_INDEX = 1000000;
|
106
|
+
declare const DEFAULT_MODE = "select";
|
107
|
+
declare function isRegisteredMessage(input: unknown): input is RegisteredMessages;
|
108
|
+
export type { NewConfiguration, UpdateConfiguration, OverlayMode, TagInfo, InfoEntry, ElementContent, StyledElementContent, NewConfigurationMessage, UpdateConfigurationMessage, RegisteredConfigMessages, RegisteredMessages, OptionMessage, NotifyProps, Reset, Update, RequestResource, SourceMapItems, };
|
109
|
+
export { MIA_PREVIEW_ID, OVERLAY_Z_INDEX, DEFAULT_MODE, isRegisteredMessage, };
|
110
|
+
export * from './handlebars';
|