@micro-lc/preview 0.5.0-rc4 → 0.5.0-rc6
Sign up to get free protection for your applications and to get access to all the features.
- 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';
|