@platforma-sdk/ui-vue 1.57.3 → 1.58.0
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/.turbo/turbo-build.log +8 -8
- package/.turbo/turbo-formatter$colon$check.log +2 -2
- package/.turbo/turbo-linter$colon$check.log +2 -2
- package/.turbo/turbo-types$colon$check.log +1 -1
- package/CHANGELOG.md +28 -0
- package/dist/defineApp.d.ts +5 -5
- package/dist/defineApp.d.ts.map +1 -1
- package/dist/defineApp.js.map +1 -1
- package/dist/index.js +8 -8
- package/dist/internal/createAppV3.d.ts +6 -10
- package/dist/internal/createAppV3.d.ts.map +1 -1
- package/dist/internal/createAppV3.js +140 -105
- package/dist/internal/createAppV3.js.map +1 -1
- package/dist/internal/createAppV3.test.d.ts +2 -0
- package/dist/internal/createAppV3.test.d.ts.map +1 -0
- package/dist/lib.d.ts +3 -1
- package/dist/lib.d.ts.map +1 -1
- package/dist/usePlugin.d.ts +44 -0
- package/dist/usePlugin.d.ts.map +1 -0
- package/dist/usePlugin.js +14 -0
- package/dist/usePlugin.js.map +1 -0
- package/package.json +6 -6
- package/src/defineApp.ts +21 -11
- package/src/internal/createAppV2.test.ts +3 -0
- package/src/internal/createAppV3.test.ts +540 -0
- package/src/internal/createAppV3.ts +109 -41
- package/src/lib.ts +9 -1
- package/src/usePlugin.ts +65 -0
- package/dist/usePluginData.d.ts +0 -30
- package/dist/usePluginData.d.ts.map +0 -1
- package/dist/usePluginData.js +0 -22
- package/dist/usePluginData.js.map +0 -1
- package/src/usePluginData.ts +0 -63
|
@@ -1,116 +1,118 @@
|
|
|
1
|
-
import { delay as
|
|
2
|
-
import { uniqueId as
|
|
3
|
-
import { deepClone as
|
|
4
|
-
import { deriveDataFromStorage as
|
|
5
|
-
import { ref as
|
|
6
|
-
import { parseQuery as
|
|
7
|
-
import { ensureOutputHasStableFlag as
|
|
8
|
-
import { applyPatch as
|
|
9
|
-
import { UpdateSerializer as
|
|
10
|
-
import { watchIgnorable as
|
|
11
|
-
const
|
|
12
|
-
authorId: (r == null ? void 0 : r.authorId) ??
|
|
1
|
+
import { delay as ot } from "../lib/util/helpers/dist/utils.js";
|
|
2
|
+
import { uniqueId as z } from "../lib/util/helpers/dist/strings.js";
|
|
3
|
+
import { deepClone as p } from "../lib/util/helpers/dist/objects.js";
|
|
4
|
+
import { isPluginOutputKey as U, deriveDataFromStorage as V, unwrapResult as h, getPluginData as _, hasAbortError as at, pluginOutputPrefix as nt } from "@platforma-sdk/model";
|
|
5
|
+
import { ref as E, computed as g, reactive as M } from "vue";
|
|
6
|
+
import { parseQuery as rt } from "../urls.js";
|
|
7
|
+
import { ensureOutputHasStableFlag as ut, MultiError as R } from "../utils.js";
|
|
8
|
+
import { applyPatch as q } from "fast-json-patch";
|
|
9
|
+
import { UpdateSerializer as x } from "./UpdateSerializer.js";
|
|
10
|
+
import { watchIgnorable as C } from "@vueuse/core";
|
|
11
|
+
const F = 150, st = (r) => ({
|
|
12
|
+
authorId: (r == null ? void 0 : r.authorId) ?? z(),
|
|
13
13
|
localVersion: ((r == null ? void 0 : r.localVersion) ?? 0) + 1
|
|
14
|
-
}),
|
|
14
|
+
}), $ = (r) => {
|
|
15
15
|
try {
|
|
16
16
|
return JSON.stringify(r, null, 2);
|
|
17
17
|
} catch (u) {
|
|
18
18
|
return u instanceof Error ? u.message : String(u);
|
|
19
19
|
}
|
|
20
20
|
};
|
|
21
|
-
function
|
|
22
|
-
const n = (
|
|
23
|
-
|
|
24
|
-
`%c>>> %c${
|
|
21
|
+
function bt(r, u, S) {
|
|
22
|
+
const n = (t, ...e) => {
|
|
23
|
+
S.debug && console.log(
|
|
24
|
+
`%c>>> %c${t}`,
|
|
25
25
|
"color: orange; font-weight: bold",
|
|
26
26
|
"color: orange",
|
|
27
|
-
...
|
|
27
|
+
...e.map((o) => $(o))
|
|
28
28
|
);
|
|
29
|
-
},
|
|
29
|
+
}, I = (t, ...e) => {
|
|
30
30
|
console.error(
|
|
31
|
-
`%c>>> %c${
|
|
31
|
+
`%c>>> %c${t}`,
|
|
32
32
|
"color: red; font-weight: bold",
|
|
33
33
|
"color: red",
|
|
34
|
-
...
|
|
34
|
+
...e.map((o) => $(o))
|
|
35
35
|
);
|
|
36
|
-
},
|
|
36
|
+
}, i = {
|
|
37
37
|
isExternalSnapshot: !1,
|
|
38
38
|
author: {
|
|
39
|
-
authorId:
|
|
39
|
+
authorId: z(),
|
|
40
40
|
localVersion: 0
|
|
41
41
|
}
|
|
42
|
-
}, T = () => (
|
|
43
|
-
let
|
|
44
|
-
return
|
|
45
|
-
},
|
|
46
|
-
{ operation: "update-plugin-data", pluginId:
|
|
42
|
+
}, T = () => (i.author = st(i.author), n("nextAuthorMarker", i.author), i.author), m = E(!1), w = E(r.uTag), y = S.debounceSpan ?? 200, O = new x({ debounceSpan: y }), D = /* @__PURE__ */ new Map(), H = (t) => {
|
|
43
|
+
let e = D.get(t);
|
|
44
|
+
return e || (e = new x({ debounceSpan: y }), D.set(t, e)), e;
|
|
45
|
+
}, J = new x({ debounceSpan: y }), P = /* @__PURE__ */ new Map(), a = E(r.value), j = async (t) => u.mutateStorage({ operation: "update-block-data", value: t }, T()), K = async (t, e) => u.mutateStorage(
|
|
46
|
+
{ operation: "update-plugin-data", pluginId: t, value: e },
|
|
47
47
|
T()
|
|
48
|
-
),
|
|
49
|
-
const
|
|
50
|
-
([
|
|
51
|
-
var
|
|
52
|
-
return (
|
|
53
|
-
|
|
54
|
-
|
|
48
|
+
), L = async (t) => u.setNavigationState(t), B = g(() => {
|
|
49
|
+
const t = Object.entries(a.value.outputs).filter(([e]) => !U(e)).map(
|
|
50
|
+
([e, o]) => {
|
|
51
|
+
var s;
|
|
52
|
+
return (s = u.blockModelInfo.outputs[e]) != null && s.withStatus ? [e, ut(o)] : [
|
|
53
|
+
e,
|
|
54
|
+
o.ok && o.value !== void 0 ? o.value : void 0
|
|
55
55
|
];
|
|
56
56
|
}
|
|
57
57
|
);
|
|
58
|
-
return Object.fromEntries(
|
|
59
|
-
}),
|
|
60
|
-
const
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
return Object.fromEntries(e);
|
|
67
|
-
}), d = y({
|
|
58
|
+
return Object.fromEntries(t);
|
|
59
|
+
}), G = g(() => {
|
|
60
|
+
const t = Object.entries(a.value.outputs).filter(([e]) => !U(e)).map(([e, o]) => [
|
|
61
|
+
e,
|
|
62
|
+
o && o.ok === !1 ? new R(o.errors) : void 0
|
|
63
|
+
]);
|
|
64
|
+
return Object.fromEntries(t);
|
|
65
|
+
}), f = M({
|
|
68
66
|
apiVersion: 3,
|
|
69
67
|
error: "",
|
|
70
68
|
model: {
|
|
71
|
-
data:
|
|
72
|
-
outputs:
|
|
73
|
-
outputErrors:
|
|
69
|
+
data: p(V(a.value.blockStorage)),
|
|
70
|
+
outputs: B,
|
|
71
|
+
outputErrors: G
|
|
74
72
|
}
|
|
75
|
-
}), { ignoreUpdates:
|
|
76
|
-
() =>
|
|
77
|
-
(
|
|
78
|
-
const
|
|
79
|
-
n("setDataQueue appModel.model, data",
|
|
73
|
+
}), { ignoreUpdates: A } = C(
|
|
74
|
+
() => f.model,
|
|
75
|
+
(t) => {
|
|
76
|
+
const e = p(t);
|
|
77
|
+
n("setDataQueue appModel.model, data", e.data), O.run(() => j(e.data).then(h));
|
|
80
78
|
},
|
|
81
79
|
{ deep: !0 }
|
|
82
|
-
),
|
|
83
|
-
n("updateAppModel",
|
|
80
|
+
), W = (t) => {
|
|
81
|
+
n("updateAppModel", t), f.model.data = p(t.data);
|
|
84
82
|
};
|
|
85
83
|
(async () => {
|
|
86
|
-
var
|
|
84
|
+
var t, e;
|
|
87
85
|
for (window.addEventListener("beforeunload", () => {
|
|
88
|
-
m.value = !0, u.dispose().then(
|
|
89
|
-
|
|
86
|
+
m.value = !0, u.dispose().then(h).catch((o) => {
|
|
87
|
+
I("error in dispose", o);
|
|
90
88
|
});
|
|
91
89
|
}); !m.value; )
|
|
92
90
|
try {
|
|
93
|
-
const
|
|
94
|
-
if (n("patches.length",
|
|
95
|
-
await new Promise((
|
|
91
|
+
const o = await u.getPatches(w.value).then(h);
|
|
92
|
+
if (n("patches.length", o.value.length), n("uTagRef.value", w.value), n("patches.uTag", o.uTag), n("patches.author", o.author), n("data.author", i.author), w.value = o.uTag, o.value.length === 0) {
|
|
93
|
+
await new Promise((c) => setTimeout(c, F));
|
|
96
94
|
continue;
|
|
97
95
|
}
|
|
98
|
-
const
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
for (const
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
96
|
+
const s = ((t = i.author) == null ? void 0 : t.authorId) !== ((e = o.author) == null ? void 0 : e.authorId);
|
|
97
|
+
s || i.isExternalSnapshot ? (n("got external changes, applying them to the snapshot", o.value), A(() => {
|
|
98
|
+
a.value = q(a.value, o.value, !1, !1).newDocument, W({ data: V(a.value.blockStorage) });
|
|
99
|
+
for (const [c, b] of P)
|
|
100
|
+
b.ignoreUpdates(() => {
|
|
101
|
+
b.model.data = p(
|
|
102
|
+
_(a.value.blockStorage, c)
|
|
103
|
+
);
|
|
104
|
+
});
|
|
105
|
+
i.isExternalSnapshot = s;
|
|
106
|
+
})) : (n("outputs changed", o.value), A(() => {
|
|
107
|
+
a.value = q(a.value, o.value).newDocument;
|
|
108
|
+
})), await new Promise((c) => setTimeout(c, F));
|
|
109
|
+
} catch (o) {
|
|
110
|
+
at(o) ? (n("patches loop aborted"), m.value = !0) : (I("error in patches loop", o), await new Promise((s) => setTimeout(s, 1e3)));
|
|
109
111
|
}
|
|
110
112
|
})();
|
|
111
|
-
const
|
|
112
|
-
cloneData:
|
|
113
|
-
cloneNavigationState:
|
|
113
|
+
const k = () => p(f.model.data), Q = () => p(a.value.navigationState), X = {
|
|
114
|
+
cloneData: k,
|
|
115
|
+
cloneNavigationState: Q,
|
|
114
116
|
/**
|
|
115
117
|
* Updates the UI state by applying a callback.
|
|
116
118
|
*
|
|
@@ -118,9 +120,9 @@ function pe(r, u, f) {
|
|
|
118
120
|
* @returns A promise resolving after the update is applied.
|
|
119
121
|
* @todo Make it mutable since there is already an initial one
|
|
120
122
|
*/
|
|
121
|
-
updateData(
|
|
122
|
-
const
|
|
123
|
-
return n("updateData",
|
|
123
|
+
updateData(t) {
|
|
124
|
+
const e = t(k());
|
|
125
|
+
return n("updateData", e), f.model.data = e, O.run(() => j(e).then(h));
|
|
124
126
|
},
|
|
125
127
|
/**
|
|
126
128
|
* Navigates to a specific href by updating the navigation state.
|
|
@@ -128,42 +130,75 @@ function pe(r, u, f) {
|
|
|
128
130
|
* @param href - The target href to navigate to.
|
|
129
131
|
* @returns A promise resolving after the navigation state is updated.
|
|
130
132
|
*/
|
|
131
|
-
navigateTo(
|
|
132
|
-
const
|
|
133
|
-
return
|
|
133
|
+
navigateTo(t) {
|
|
134
|
+
const e = Q();
|
|
135
|
+
return e.href = t, J.run(() => L(e).then(h));
|
|
134
136
|
},
|
|
135
137
|
async allSettled() {
|
|
136
|
-
await
|
|
137
|
-
const
|
|
138
|
-
|
|
139
|
-
...Array.from(
|
|
138
|
+
await ot(0);
|
|
139
|
+
const t = [
|
|
140
|
+
O.allSettled(),
|
|
141
|
+
...Array.from(D.values()).map((e) => e.allSettled())
|
|
140
142
|
];
|
|
141
|
-
await Promise.all(
|
|
143
|
+
await Promise.all(t);
|
|
142
144
|
}
|
|
143
|
-
},
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
)
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
145
|
+
}, Y = (t) => {
|
|
146
|
+
const e = nt(t), o = g(() => {
|
|
147
|
+
const l = {};
|
|
148
|
+
for (const [v, d] of Object.entries(
|
|
149
|
+
a.value.outputs
|
|
150
|
+
))
|
|
151
|
+
v.startsWith(e) && (l[v.slice(e.length)] = d.ok && d.value !== void 0 ? d.value : void 0);
|
|
152
|
+
return l;
|
|
153
|
+
}), s = g(() => {
|
|
154
|
+
const l = {};
|
|
155
|
+
for (const [v, d] of Object.entries(
|
|
156
|
+
a.value.outputs
|
|
157
|
+
))
|
|
158
|
+
v.startsWith(e) && (l[v.slice(e.length)] = d && d.ok === !1 ? new R(d.errors) : void 0);
|
|
159
|
+
return l;
|
|
160
|
+
}), c = M({
|
|
161
|
+
data: p(_(a.value.blockStorage, t)),
|
|
162
|
+
outputs: o,
|
|
163
|
+
outputErrors: s
|
|
164
|
+
}), { ignoreUpdates: b } = C(
|
|
165
|
+
() => c.data,
|
|
166
|
+
(l) => {
|
|
167
|
+
l !== void 0 && (n("plugin setData", t, l), H(t).run(
|
|
168
|
+
() => K(t, p(l)).then(h)
|
|
169
|
+
));
|
|
170
|
+
},
|
|
171
|
+
{ deep: !0 }
|
|
172
|
+
);
|
|
173
|
+
return {
|
|
174
|
+
model: c,
|
|
175
|
+
ignoreUpdates: b
|
|
176
|
+
};
|
|
177
|
+
}, Z = {
|
|
178
|
+
getOrCreatePluginState(t) {
|
|
179
|
+
const e = P.get(t);
|
|
180
|
+
if (e)
|
|
181
|
+
return e;
|
|
182
|
+
const o = Y(t);
|
|
183
|
+
return P.set(t, o), o;
|
|
152
184
|
}
|
|
153
|
-
},
|
|
185
|
+
}, tt = Object.fromEntries(
|
|
186
|
+
u.blockModelInfo.pluginIds.map((t) => [t, t])
|
|
187
|
+
), et = {
|
|
154
188
|
closedRef: m,
|
|
155
|
-
snapshot:
|
|
156
|
-
|
|
157
|
-
|
|
189
|
+
snapshot: a,
|
|
190
|
+
plugins: tt,
|
|
191
|
+
queryParams: g(() => rt(a.value.navigationState.href)),
|
|
192
|
+
href: g(() => a.value.navigationState.href),
|
|
158
193
|
hasErrors: g(
|
|
159
|
-
() => Object.values(
|
|
194
|
+
() => Object.values(a.value.outputs).some((t) => !(t != null && t.ok))
|
|
160
195
|
)
|
|
161
|
-
},
|
|
162
|
-
return
|
|
196
|
+
}, N = Object.assign(M(Object.assign(f, et)), X);
|
|
197
|
+
return S.debug && (globalThis.__block_app__ = N), { app: N, pluginAccess: Z };
|
|
163
198
|
}
|
|
164
199
|
export {
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
200
|
+
bt as createAppV3,
|
|
201
|
+
st as createNextAuthorMarker,
|
|
202
|
+
F as patchPoolingDelay
|
|
168
203
|
};
|
|
169
204
|
//# sourceMappingURL=createAppV3.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createAppV3.js","sources":["../../src/internal/createAppV3.ts"],"sourcesContent":["import { deepClone, delay, uniqueId } from \"@milaboratories/helpers\";\nimport type { Mutable } from \"@milaboratories/helpers\";\nimport type {\n NavigationState,\n BlockOutputsBase,\n BlockStateV3,\n PlatformaV3,\n ValueWithUTag,\n AuthorMarker,\n PlatformaExtended,\n} from \"@platforma-sdk/model\";\nimport {\n hasAbortError,\n unwrapResult,\n deriveDataFromStorage,\n getPluginData,\n} from \"@platforma-sdk/model\";\nimport type { Ref } from \"vue\";\nimport { reactive, computed, ref } from \"vue\";\nimport type { OutputValues, OutputErrors, AppSettings } from \"../types\";\nimport { parseQuery } from \"../urls\";\nimport { ensureOutputHasStableFlag, MultiError } from \"../utils\";\nimport { applyPatch } from \"fast-json-patch\";\nimport { UpdateSerializer } from \"./UpdateSerializer\";\nimport { watchIgnorable } from \"@vueuse/core\";\n\nexport const patchPoolingDelay = 150;\n\n/** Internal interface for plugin data access — injected separately from the app. */\nexport interface PluginDataAccess {\n readonly pluginDataMap: Record<string, unknown>;\n setPluginData(pluginId: string, value: unknown): Promise<boolean>;\n initPluginDataSlot(pluginId: string): void;\n}\n\nexport const createNextAuthorMarker = (marker: AuthorMarker | undefined): AuthorMarker => ({\n authorId: marker?.authorId ?? uniqueId(),\n localVersion: (marker?.localVersion ?? 0) + 1,\n});\n\nconst stringifyForDebug = (v: unknown) => {\n try {\n return JSON.stringify(v, null, 2);\n } catch (err) {\n return err instanceof Error ? err.message : String(err);\n }\n};\n\n/**\n * Creates an application instance with reactive state management, outputs, and methods for state updates and navigation.\n *\n * @template Args - The type of arguments used in the application.\n * @template Outputs - The type of block outputs extending `BlockOutputsBase`.\n * @template Data - The type of the block data.\n * @template Href - The type of navigation href, defaulting to a string starting with `/`.\n *\n * @param state - Initial state of the application, including args, outputs, UI state, and navigation state.\n * @param platforma - A platform interface for interacting with block states.\n * @param settings - Application settings, such as debug flags.\n *\n * @returns A reactive application object with methods, getters, and state.\n */\nexport function createAppV3<\n Data = unknown,\n Args = unknown,\n Outputs extends BlockOutputsBase = BlockOutputsBase,\n Href extends `/${string}` = `/${string}`,\n>(\n state: ValueWithUTag<BlockStateV3<Data, Outputs, Href>>,\n platforma: PlatformaExtended<PlatformaV3<Data, Args, Outputs, Href>>,\n settings: AppSettings,\n) {\n const debug = (msg: string, ...rest: unknown[]) => {\n if (settings.debug) {\n console.log(\n `%c>>> %c${msg}`,\n \"color: orange; font-weight: bold\",\n \"color: orange\",\n ...rest.map((r) => stringifyForDebug(r)),\n );\n }\n };\n\n const error = (msg: string, ...rest: unknown[]) => {\n console.error(\n `%c>>> %c${msg}`,\n \"color: red; font-weight: bold\",\n \"color: red\",\n ...rest.map((r) => stringifyForDebug(r)),\n );\n };\n\n const data = {\n isExternalSnapshot: false,\n author: {\n authorId: uniqueId(),\n localVersion: 0,\n },\n };\n\n const nextAuthorMarker = () => {\n data.author = createNextAuthorMarker(data.author);\n debug(\"nextAuthorMarker\", data.author);\n return data.author;\n };\n\n const closedRef = ref(false);\n\n const uTagRef = ref(state.uTag);\n\n const debounceSpan = settings.debounceSpan ?? 200;\n\n const setDataQueue = new UpdateSerializer({ debounceSpan });\n const pluginDataQueues = new Map<string, UpdateSerializer>();\n const getPluginDataQueue = (pluginId: string): UpdateSerializer => {\n let queue = pluginDataQueues.get(pluginId);\n if (!queue) {\n queue = new UpdateSerializer({ debounceSpan });\n pluginDataQueues.set(pluginId, queue);\n }\n return queue;\n };\n const setNavigationStateQueue = new UpdateSerializer({ debounceSpan });\n\n /** Reactive map of plugin data keyed by pluginId. Optimistic state for plugin components. */\n const pluginDataMap = reactive<Record<string, unknown>>({});\n /**\n * Reactive snapshot of the application state, including args, outputs, UI state, and navigation state.\n */\n const snapshot = ref<{\n outputs: Partial<Outputs>;\n blockStorage: unknown;\n navigationState: NavigationState<Href>;\n }>(state.value) as Ref<{\n outputs: Partial<Outputs>;\n blockStorage: unknown;\n navigationState: NavigationState<Href>;\n }>;\n\n const updateData = async (value: Data) => {\n return platforma.mutateStorage({ operation: \"update-block-data\", value }, nextAuthorMarker());\n };\n\n const updatePluginData = async (pluginId: string, value: unknown) => {\n return platforma.mutateStorage(\n { operation: \"update-plugin-data\", pluginId, value },\n nextAuthorMarker(),\n );\n };\n\n /** Derives plugin data for a given pluginId from the current snapshot. */\n const derivePluginDataFromSnapshot = (pluginId: string): unknown => {\n return getPluginData(snapshot.value.blockStorage, pluginId);\n };\n\n const setNavigationState = async (state: NavigationState<Href>) => {\n return platforma.setNavigationState(state);\n };\n\n const outputs = computed<OutputValues<Outputs>>(() => {\n const entries = Object.entries(snapshot.value.outputs as Partial<Readonly<Outputs>>).map(\n ([k, outputWithStatus]) =>\n platforma.blockModelInfo.outputs[k]?.withStatus\n ? [k, ensureOutputHasStableFlag(outputWithStatus)]\n : [\n k,\n outputWithStatus.ok && outputWithStatus.value !== undefined\n ? outputWithStatus.value\n : undefined,\n ],\n );\n return Object.fromEntries(entries);\n });\n\n const outputErrors = computed<OutputErrors<Outputs>>(() => {\n const entries = Object.entries(snapshot.value.outputs as Partial<Readonly<Outputs>>).map(\n ([k, vOrErr]) => [\n k,\n vOrErr && vOrErr.ok === false ? new MultiError(vOrErr.errors) : undefined,\n ],\n );\n return Object.fromEntries(entries);\n });\n\n const appModel = reactive({\n apiVersion: 3,\n error: \"\",\n model: {\n data: deepClone(deriveDataFromStorage<Data>(snapshot.value.blockStorage)) as Data,\n outputs,\n outputErrors,\n },\n }) as {\n error: string;\n model: {\n data: Data;\n outputs: OutputValues<Outputs>;\n outputErrors: OutputErrors<Outputs>;\n };\n };\n\n const { ignoreUpdates } = watchIgnorable(\n () => appModel.model,\n (_newData) => {\n const newData = deepClone(_newData);\n debug(\"setDataQueue appModel.model, data\", newData.data);\n setDataQueue.run(() => updateData(newData.data).then(unwrapResult));\n },\n { deep: true },\n );\n\n const updateAppModel = (newData: { data: Data }) => {\n debug(\"updateAppModel\", newData);\n appModel.model.data = deepClone(newData.data) as Data;\n };\n\n (async () => {\n window.addEventListener(\"beforeunload\", () => {\n closedRef.value = true;\n platforma\n .dispose()\n .then(unwrapResult)\n .catch((err) => {\n error(\"error in dispose\", err);\n });\n });\n\n while (!closedRef.value) {\n try {\n const patches = await platforma.getPatches(uTagRef.value).then(unwrapResult);\n\n debug(\"patches.length\", patches.value.length);\n debug(\"uTagRef.value\", uTagRef.value);\n debug(\"patches.uTag\", patches.uTag);\n debug(\"patches.author\", patches.author);\n debug(\"data.author\", data.author);\n\n uTagRef.value = patches.uTag;\n\n if (patches.value.length === 0) {\n await new Promise((resolve) => setTimeout(resolve, patchPoolingDelay));\n continue;\n }\n\n const isAuthorChanged = data.author?.authorId !== patches.author?.authorId;\n\n // Immutable behavior, apply external changes to the snapshot\n if (isAuthorChanged || data.isExternalSnapshot) {\n debug(\"got external changes, applying them to the snapshot\", patches.value);\n ignoreUpdates(() => {\n snapshot.value = applyPatch(snapshot.value, patches.value, false, false).newDocument;\n updateAppModel({ data: deriveDataFromStorage<Data>(snapshot.value.blockStorage) });\n // Reconcile plugin data from external source\n for (const pluginId of Object.keys(pluginDataMap)) {\n pluginDataMap[pluginId] = derivePluginDataFromSnapshot(pluginId);\n }\n data.isExternalSnapshot = isAuthorChanged;\n });\n } else {\n // Mutable behavior\n debug(\"outputs changed\", patches.value);\n ignoreUpdates(() => {\n snapshot.value = applyPatch(snapshot.value, patches.value).newDocument;\n });\n }\n\n await new Promise((resolve) => setTimeout(resolve, patchPoolingDelay));\n } catch (err) {\n if (hasAbortError(err)) {\n debug(\"patches loop aborted\");\n closedRef.value = true;\n } else {\n error(\"error in patches loop\", err);\n await new Promise((resolve) => setTimeout(resolve, 1000));\n }\n }\n }\n })();\n\n const cloneData = () => deepClone(appModel.model.data) as Data;\n const cloneNavigationState = () =>\n deepClone(snapshot.value.navigationState) as Mutable<NavigationState<Href>>;\n\n const methods = {\n cloneData,\n cloneNavigationState,\n /**\n * Updates the UI state by applying a callback.\n *\n * @param cb - Callback to modify the current UI state.\n * @returns A promise resolving after the update is applied.\n * @todo Make it mutable since there is already an initial one\n */\n updateData(cb: (data: Data) => Data): Promise<boolean> {\n const newData = cb(cloneData());\n debug(\"updateData\", newData);\n appModel.model.data = newData;\n return setDataQueue.run(() => updateData(newData).then(unwrapResult));\n },\n /**\n * Navigates to a specific href by updating the navigation state.\n *\n * @param href - The target href to navigate to.\n * @returns A promise resolving after the navigation state is updated.\n */\n navigateTo(href: Href) {\n const newState = cloneNavigationState();\n newState.href = href;\n return setNavigationStateQueue.run(() => setNavigationState(newState).then(unwrapResult));\n },\n async allSettled() {\n await delay(0);\n const allQueues = [\n setDataQueue.allSettled(),\n ...Array.from(pluginDataQueues.values()).map((q) => q.allSettled()),\n ];\n await Promise.all(allQueues);\n },\n };\n\n /** Plugin internals — provided via separate injection key, not exposed on useApp(). */\n const pluginAccess: PluginDataAccess = {\n pluginDataMap,\n setPluginData(pluginId: string, value: unknown): Promise<boolean> {\n pluginDataMap[pluginId] = value;\n debug(\"setPluginData\", pluginId, value);\n return getPluginDataQueue(pluginId).run(() =>\n updatePluginData(pluginId, value).then(unwrapResult),\n );\n },\n initPluginDataSlot(pluginId: string): void {\n if (!(pluginId in pluginDataMap)) {\n pluginDataMap[pluginId] = derivePluginDataFromSnapshot(pluginId);\n }\n },\n };\n\n const getters = {\n closedRef,\n snapshot,\n queryParams: computed(() => parseQuery<Href>(snapshot.value.navigationState.href as Href)),\n href: computed(() => snapshot.value.navigationState.href),\n hasErrors: computed(() =>\n Object.values(snapshot.value.outputs as Partial<Readonly<Outputs>>).some((v) => !v?.ok),\n ),\n };\n\n const app = Object.assign(reactive(Object.assign(appModel, getters)), methods);\n\n if (settings.debug) {\n // @ts-expect-error (to inspect in console in debug mode)\n globalThis.__block_app__ = app;\n }\n\n return { app, pluginAccess };\n}\n\nexport type BaseAppV3<\n Data = unknown,\n Args = unknown,\n Outputs extends BlockOutputsBase = BlockOutputsBase,\n Href extends `/${string}` = `/${string}`,\n> = ReturnType<typeof createAppV3<Data, Args, Outputs, Href>>[\"app\"];\n"],"names":["patchPoolingDelay","createNextAuthorMarker","marker","uniqueId","stringifyForDebug","v","err","createAppV3","state","platforma","settings","debug","msg","rest","r","error","data","nextAuthorMarker","closedRef","ref","uTagRef","debounceSpan","setDataQueue","UpdateSerializer","pluginDataQueues","getPluginDataQueue","pluginId","queue","setNavigationStateQueue","pluginDataMap","reactive","snapshot","updateData","value","updatePluginData","derivePluginDataFromSnapshot","getPluginData","setNavigationState","outputs","computed","entries","k","outputWithStatus","_a","ensureOutputHasStableFlag","outputErrors","vOrErr","MultiError","appModel","deepClone","deriveDataFromStorage","ignoreUpdates","watchIgnorable","_newData","newData","unwrapResult","updateAppModel","patches","resolve","isAuthorChanged","_b","applyPatch","hasAbortError","cloneData","cloneNavigationState","methods","cb","href","newState","delay","allQueues","q","pluginAccess","getters","parseQuery","app"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"createAppV3.js","sources":["../../src/internal/createAppV3.ts"],"sourcesContent":["import { deepClone, delay, uniqueId } from \"@milaboratories/helpers\";\nimport type { Mutable } from \"@milaboratories/helpers\";\nimport type {\n NavigationState,\n BlockOutputsBase,\n BlockStateV3,\n PlatformaV3,\n ValueWithUTag,\n AuthorMarker,\n PlatformaExtended,\n InferPluginHandles,\n PluginHandle,\n InferFactoryData,\n InferFactoryOutputs,\n PluginFactoryLike,\n} from \"@platforma-sdk/model\";\nimport {\n hasAbortError,\n unwrapResult,\n deriveDataFromStorage,\n getPluginData,\n isPluginOutputKey,\n pluginOutputPrefix,\n} from \"@platforma-sdk/model\";\nimport type { Ref } from \"vue\";\nimport { reactive, computed, ref } from \"vue\";\nimport type { OutputValues, OutputErrors, AppSettings } from \"../types\";\nimport { parseQuery } from \"../urls\";\nimport { ensureOutputHasStableFlag, MultiError } from \"../utils\";\nimport { applyPatch } from \"fast-json-patch\";\nimport { UpdateSerializer } from \"./UpdateSerializer\";\nimport { watchIgnorable } from \"@vueuse/core\";\nimport type { PluginState, PluginAccess } from \"../usePlugin\";\n\nexport const patchPoolingDelay = 150;\n\n/** Internal per-plugin state with reconciliation support. */\ninterface InternalPluginState<Data = unknown, Outputs = unknown> extends PluginState<\n Data,\n Outputs\n> {\n readonly ignoreUpdates: (fn: () => void) => void;\n}\n\nexport const createNextAuthorMarker = (marker: AuthorMarker | undefined): AuthorMarker => ({\n authorId: marker?.authorId ?? uniqueId(),\n localVersion: (marker?.localVersion ?? 0) + 1,\n});\n\nconst stringifyForDebug = (v: unknown) => {\n try {\n return JSON.stringify(v, null, 2);\n } catch (err) {\n return err instanceof Error ? err.message : String(err);\n }\n};\n\n/**\n * Creates an application instance with reactive state management, outputs, and methods for state updates and navigation.\n *\n * @template Args - The type of arguments used in the application.\n * @template Outputs - The type of block outputs extending `BlockOutputsBase`.\n * @template Data - The type of the block data.\n * @template Href - The type of navigation href, defaulting to a string starting with `/`.\n *\n * @param state - Initial state of the application, including args, outputs, UI state, and navigation state.\n * @param platforma - A platform interface for interacting with block states.\n * @param settings - Application settings, such as debug flags.\n *\n * @returns A reactive application object with methods, getters, and state.\n */\nexport function createAppV3<\n Data = unknown,\n Args = unknown,\n Outputs extends BlockOutputsBase = BlockOutputsBase,\n Href extends `/${string}` = `/${string}`,\n Plugins extends Record<string, unknown> = Record<string, unknown>,\n>(\n state: ValueWithUTag<BlockStateV3<Data, Outputs, Href>>,\n platforma: PlatformaExtended<PlatformaV3<Data, Args, Outputs, Href, Plugins>>,\n settings: AppSettings,\n) {\n const debug = (msg: string, ...rest: unknown[]) => {\n if (settings.debug) {\n console.log(\n `%c>>> %c${msg}`,\n \"color: orange; font-weight: bold\",\n \"color: orange\",\n ...rest.map((r) => stringifyForDebug(r)),\n );\n }\n };\n\n const error = (msg: string, ...rest: unknown[]) => {\n console.error(\n `%c>>> %c${msg}`,\n \"color: red; font-weight: bold\",\n \"color: red\",\n ...rest.map((r) => stringifyForDebug(r)),\n );\n };\n\n const data = {\n isExternalSnapshot: false,\n author: {\n authorId: uniqueId(),\n localVersion: 0,\n },\n };\n\n const nextAuthorMarker = () => {\n data.author = createNextAuthorMarker(data.author);\n debug(\"nextAuthorMarker\", data.author);\n return data.author;\n };\n\n const closedRef = ref(false);\n\n const uTagRef = ref(state.uTag);\n\n const debounceSpan = settings.debounceSpan ?? 200;\n\n const setDataQueue = new UpdateSerializer({ debounceSpan });\n const pluginDataQueues = new Map<PluginHandle, UpdateSerializer>();\n const getPluginDataQueue = (handle: PluginHandle): UpdateSerializer => {\n let queue = pluginDataQueues.get(handle);\n if (!queue) {\n queue = new UpdateSerializer({ debounceSpan });\n pluginDataQueues.set(handle, queue);\n }\n return queue;\n };\n const setNavigationStateQueue = new UpdateSerializer({ debounceSpan });\n\n /** Lazily-created per-plugin reactive states. */\n const pluginStates = new Map<PluginHandle, InternalPluginState>();\n /**\n * Reactive snapshot of the application state, including args, outputs, UI state, and navigation state.\n */\n const snapshot = ref<{\n outputs: Partial<Outputs>;\n blockStorage: unknown;\n navigationState: NavigationState<Href>;\n }>(state.value) as Ref<{\n outputs: Partial<Outputs>;\n blockStorage: unknown;\n navigationState: NavigationState<Href>;\n }>;\n\n const updateData = async (value: Data) => {\n return platforma.mutateStorage({ operation: \"update-block-data\", value }, nextAuthorMarker());\n };\n\n const updatePluginData = async (handle: PluginHandle, value: unknown) => {\n return platforma.mutateStorage(\n { operation: \"update-plugin-data\", pluginId: handle, value },\n nextAuthorMarker(),\n );\n };\n\n const setNavigationState = async (state: NavigationState<Href>) => {\n return platforma.setNavigationState(state);\n };\n\n const outputs = computed<OutputValues<Outputs>>(() => {\n const entries = Object.entries(snapshot.value.outputs as Partial<Readonly<Outputs>>)\n .filter(([k]) => !isPluginOutputKey(k))\n .map(([k, outputWithStatus]) =>\n platforma.blockModelInfo.outputs[k]?.withStatus\n ? [k, ensureOutputHasStableFlag(outputWithStatus)]\n : [\n k,\n outputWithStatus.ok && outputWithStatus.value !== undefined\n ? outputWithStatus.value\n : undefined,\n ],\n );\n return Object.fromEntries(entries);\n });\n\n const outputErrors = computed<OutputErrors<Outputs>>(() => {\n const entries = Object.entries(snapshot.value.outputs as Partial<Readonly<Outputs>>)\n .filter(([k]) => !isPluginOutputKey(k))\n .map(([k, vOrErr]) => [\n k,\n vOrErr && vOrErr.ok === false ? new MultiError(vOrErr.errors) : undefined,\n ]);\n return Object.fromEntries(entries);\n });\n\n const appModel = reactive({\n apiVersion: 3,\n error: \"\",\n model: {\n data: deepClone(deriveDataFromStorage<Data>(snapshot.value.blockStorage)) as Data,\n outputs,\n outputErrors,\n },\n }) as {\n error: string;\n model: {\n data: Data;\n outputs: OutputValues<Outputs>;\n outputErrors: OutputErrors<Outputs>;\n };\n };\n\n const { ignoreUpdates } = watchIgnorable(\n () => appModel.model,\n (_newData) => {\n const newData = deepClone(_newData);\n debug(\"setDataQueue appModel.model, data\", newData.data);\n setDataQueue.run(() => updateData(newData.data).then(unwrapResult));\n },\n { deep: true },\n );\n\n const updateAppModel = (newData: { data: Data }) => {\n debug(\"updateAppModel\", newData);\n appModel.model.data = deepClone(newData.data) as Data;\n };\n\n (async () => {\n window.addEventListener(\"beforeunload\", () => {\n closedRef.value = true;\n platforma\n .dispose()\n .then(unwrapResult)\n .catch((err) => {\n error(\"error in dispose\", err);\n });\n });\n\n while (!closedRef.value) {\n try {\n const patches = await platforma.getPatches(uTagRef.value).then(unwrapResult);\n\n debug(\"patches.length\", patches.value.length);\n debug(\"uTagRef.value\", uTagRef.value);\n debug(\"patches.uTag\", patches.uTag);\n debug(\"patches.author\", patches.author);\n debug(\"data.author\", data.author);\n\n uTagRef.value = patches.uTag;\n\n if (patches.value.length === 0) {\n await new Promise((resolve) => setTimeout(resolve, patchPoolingDelay));\n continue;\n }\n\n const isAuthorChanged = data.author?.authorId !== patches.author?.authorId;\n\n // Immutable behavior, apply external changes to the snapshot\n if (isAuthorChanged || data.isExternalSnapshot) {\n debug(\"got external changes, applying them to the snapshot\", patches.value);\n ignoreUpdates(() => {\n snapshot.value = applyPatch(snapshot.value, patches.value, false, false).newDocument;\n updateAppModel({ data: deriveDataFromStorage<Data>(snapshot.value.blockStorage) });\n // Reconcile plugin data from external source\n for (const [handle, pluginState] of pluginStates) {\n pluginState.ignoreUpdates(() => {\n pluginState.model.data = deepClone(\n getPluginData(snapshot.value.blockStorage, handle),\n );\n });\n }\n data.isExternalSnapshot = isAuthorChanged;\n });\n } else {\n // Mutable behavior\n debug(\"outputs changed\", patches.value);\n ignoreUpdates(() => {\n snapshot.value = applyPatch(snapshot.value, patches.value).newDocument;\n });\n }\n\n await new Promise((resolve) => setTimeout(resolve, patchPoolingDelay));\n } catch (err) {\n if (hasAbortError(err)) {\n debug(\"patches loop aborted\");\n closedRef.value = true;\n } else {\n error(\"error in patches loop\", err);\n await new Promise((resolve) => setTimeout(resolve, 1000));\n }\n }\n }\n })();\n\n const cloneData = () => deepClone(appModel.model.data) as Data;\n const cloneNavigationState = () =>\n deepClone(snapshot.value.navigationState) as Mutable<NavigationState<Href>>;\n\n const methods = {\n cloneData,\n cloneNavigationState,\n /**\n * Updates the UI state by applying a callback.\n *\n * @param cb - Callback to modify the current UI state.\n * @returns A promise resolving after the update is applied.\n * @todo Make it mutable since there is already an initial one\n */\n updateData(cb: (data: Data) => Data): Promise<boolean> {\n const newData = cb(cloneData());\n debug(\"updateData\", newData);\n appModel.model.data = newData;\n return setDataQueue.run(() => updateData(newData).then(unwrapResult));\n },\n /**\n * Navigates to a specific href by updating the navigation state.\n *\n * @param href - The target href to navigate to.\n * @returns A promise resolving after the navigation state is updated.\n */\n navigateTo(href: Href) {\n const newState = cloneNavigationState();\n newState.href = href;\n return setNavigationStateQueue.run(() => setNavigationState(newState).then(unwrapResult));\n },\n async allSettled() {\n await delay(0);\n const allQueues = [\n setDataQueue.allSettled(),\n ...Array.from(pluginDataQueues.values()).map((q) => q.allSettled()),\n ];\n await Promise.all(allQueues);\n },\n };\n\n /** Creates a lazily-cached per-plugin reactive state. */\n const createPluginState = <F extends PluginFactoryLike>(\n handle: PluginHandle<F>,\n ): InternalPluginState<InferFactoryData<F>, InferFactoryOutputs<F>> => {\n const prefix = pluginOutputPrefix(handle);\n\n const pluginOutputs = computed(() => {\n const result: Record<string, unknown> = {};\n for (const [key, outputWithStatus] of Object.entries(\n snapshot.value.outputs as Partial<Readonly<Outputs>>,\n )) {\n if (!key.startsWith(prefix)) continue;\n result[key.slice(prefix.length)] =\n outputWithStatus.ok && outputWithStatus.value !== undefined\n ? outputWithStatus.value\n : undefined;\n }\n return result;\n });\n\n const pluginOutputErrors = computed(() => {\n const result: Record<string, Error | undefined> = {};\n for (const [key, vOrErr] of Object.entries(\n snapshot.value.outputs as Partial<Readonly<Outputs>>,\n )) {\n if (!key.startsWith(prefix)) continue;\n result[key.slice(prefix.length)] =\n vOrErr && vOrErr.ok === false ? new MultiError(vOrErr.errors) : undefined;\n }\n return result;\n });\n\n const pluginModel = reactive({\n data: deepClone(getPluginData(snapshot.value.blockStorage, handle)),\n outputs: pluginOutputs,\n outputErrors: pluginOutputErrors,\n }) as InternalPluginState<InferFactoryData<F>, InferFactoryOutputs<F>>[\"model\"];\n\n const { ignoreUpdates } = watchIgnorable(\n () => pluginModel.data,\n (newData) => {\n if (newData === undefined) return;\n debug(\"plugin setData\", handle, newData);\n getPluginDataQueue(handle).run(() =>\n updatePluginData(handle, deepClone(newData)).then(unwrapResult),\n );\n },\n { deep: true },\n );\n\n return {\n model: pluginModel,\n ignoreUpdates,\n };\n };\n\n /** Plugin internals — provided via separate injection key, not exposed on useApp(). */\n const pluginAccess: PluginAccess = {\n getOrCreatePluginState<F extends PluginFactoryLike>(handle: PluginHandle<F>) {\n const existing = pluginStates.get(handle);\n if (existing) {\n return existing as unknown as PluginState<InferFactoryData<F>, InferFactoryOutputs<F>>;\n }\n const state = createPluginState(handle);\n pluginStates.set(handle, state);\n return state;\n },\n };\n\n const plugins = Object.fromEntries(\n platforma.blockModelInfo.pluginIds.map((id) => [id, id]),\n ) as InferPluginHandles<Plugins>;\n\n const getters = {\n closedRef,\n snapshot,\n plugins,\n queryParams: computed(() => parseQuery<Href>(snapshot.value.navigationState.href as Href)),\n href: computed(() => snapshot.value.navigationState.href),\n hasErrors: computed(() =>\n Object.values(snapshot.value.outputs as Partial<Readonly<Outputs>>).some((v) => !v?.ok),\n ),\n };\n\n const app = Object.assign(reactive(Object.assign(appModel, getters)), methods);\n\n if (settings.debug) {\n // @ts-expect-error (to inspect in console in debug mode)\n globalThis.__block_app__ = app;\n }\n\n return { app, pluginAccess };\n}\n\nexport type BaseAppV3<\n Data = unknown,\n Args = unknown,\n Outputs extends BlockOutputsBase = BlockOutputsBase,\n Href extends `/${string}` = `/${string}`,\n Plugins extends Record<string, unknown> = Record<string, unknown>,\n> = ReturnType<typeof createAppV3<Data, Args, Outputs, Href, Plugins>>[\"app\"];\n"],"names":["patchPoolingDelay","createNextAuthorMarker","marker","uniqueId","stringifyForDebug","v","err","createAppV3","state","platforma","settings","debug","msg","rest","r","error","data","nextAuthorMarker","closedRef","ref","uTagRef","debounceSpan","setDataQueue","UpdateSerializer","pluginDataQueues","getPluginDataQueue","handle","queue","setNavigationStateQueue","pluginStates","snapshot","updateData","value","updatePluginData","setNavigationState","outputs","computed","entries","k","isPluginOutputKey","outputWithStatus","_a","ensureOutputHasStableFlag","outputErrors","vOrErr","MultiError","appModel","reactive","deepClone","deriveDataFromStorage","ignoreUpdates","watchIgnorable","_newData","newData","unwrapResult","updateAppModel","patches","resolve","isAuthorChanged","_b","applyPatch","pluginState","getPluginData","hasAbortError","cloneData","cloneNavigationState","methods","cb","href","newState","delay","allQueues","q","createPluginState","prefix","pluginOutputPrefix","pluginOutputs","result","key","pluginOutputErrors","pluginModel","pluginAccess","existing","plugins","id","getters","parseQuery","app"],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"createAppV3.test.d.ts","sourceRoot":"","sources":["../../src/internal/createAppV3.test.ts"],"names":[],"mappings":""}
|
package/dist/lib.d.ts
CHANGED
|
@@ -19,7 +19,9 @@ export * from './components/PlAnnotations';
|
|
|
19
19
|
export * from './components/PlBtnExportArchive';
|
|
20
20
|
export * from './components/PlAdvancedFilter';
|
|
21
21
|
export * from './defineApp';
|
|
22
|
-
export {
|
|
22
|
+
export { usePlugin } from './usePlugin';
|
|
23
|
+
export type { PluginState } from './usePlugin';
|
|
24
|
+
export type { PluginHandle, PluginFactoryLike, InferPluginHandle, InferFactoryData, InferFactoryOutputs, } from '@platforma-sdk/model';
|
|
23
25
|
export * from './createModel';
|
|
24
26
|
export * from './types';
|
|
25
27
|
export * from './defineStore';
|
package/dist/lib.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lib.d.ts","sourceRoot":"","sources":["../src/lib.ts"],"names":[],"mappings":"AAAA,OAAO,kBAAkB,CAAC;AAE1B,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,8BAA8B,CAAC;AACtE,OAAO,EAAE,OAAO,IAAI,eAAe,EAAE,MAAM,gDAAgD,CAAC;AAC5F,OAAO,EAAE,OAAO,IAAI,kBAAkB,EAAE,MAAM,mDAAmD,CAAC;AAClG,OAAO,EAAE,OAAO,IAAI,iBAAiB,EAAE,MAAM,kDAAkD,CAAC;AAEhG,YAAY,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAE3D,cAAc,aAAa,CAAC;AAE5B,cAAc,+BAA+B,CAAC;AAE9C,cAAc,2BAA2B,CAAC;AAC1C,cAAc,+BAA+B,CAAC;AAC9C,cAAc,gCAAgC,CAAC;AAC/C,cAAc,qCAAqC,CAAC;AACpD,cAAc,sCAAsC,CAAC;AAErD,cAAc,4BAA4B,CAAC;AAE3C,cAAc,8BAA8B,CAAC;AAE7C,cAAc,oCAAoC,CAAC;AAEnD,cAAc,oCAAoC,CAAC;AAEnD,cAAc,6BAA6B,CAAC;AAE5C,cAAc,4BAA4B,CAAC;AAE3C,cAAc,iCAAiC,CAAC;AAEhD,cAAc,+BAA+B,CAAC;AAE9C,cAAc,aAAa,CAAC;AAE5B,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"lib.d.ts","sourceRoot":"","sources":["../src/lib.ts"],"names":[],"mappings":"AAAA,OAAO,kBAAkB,CAAC;AAE1B,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,8BAA8B,CAAC;AACtE,OAAO,EAAE,OAAO,IAAI,eAAe,EAAE,MAAM,gDAAgD,CAAC;AAC5F,OAAO,EAAE,OAAO,IAAI,kBAAkB,EAAE,MAAM,mDAAmD,CAAC;AAClG,OAAO,EAAE,OAAO,IAAI,iBAAiB,EAAE,MAAM,kDAAkD,CAAC;AAEhG,YAAY,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAE3D,cAAc,aAAa,CAAC;AAE5B,cAAc,+BAA+B,CAAC;AAE9C,cAAc,2BAA2B,CAAC;AAC1C,cAAc,+BAA+B,CAAC;AAC9C,cAAc,gCAAgC,CAAC;AAC/C,cAAc,qCAAqC,CAAC;AACpD,cAAc,sCAAsC,CAAC;AAErD,cAAc,4BAA4B,CAAC;AAE3C,cAAc,8BAA8B,CAAC;AAE7C,cAAc,oCAAoC,CAAC;AAEnD,cAAc,oCAAoC,CAAC;AAEnD,cAAc,6BAA6B,CAAC;AAE5C,cAAc,4BAA4B,CAAC;AAE3C,cAAc,iCAAiC,CAAC;AAEhD,cAAc,+BAA+B,CAAC;AAE9C,cAAc,aAAa,CAAC;AAE5B,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,YAAY,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC/C,YAAY,EACV,YAAY,EACZ,iBAAiB,EACjB,iBAAiB,EACjB,gBAAgB,EAChB,mBAAmB,GACpB,MAAM,sBAAsB,CAAC;AAE9B,cAAc,eAAe,CAAC;AAE9B,cAAc,SAAS,CAAC;AAExB,cAAc,eAAe,CAAC;AAE9B,cAAc,UAAU,CAAC;AAEzB,cAAc,SAAS,CAAC;AAExB,cAAc,cAAc,CAAC;AAE7B,cAAc,kBAAkB,CAAC;AAEjC,cAAc,2BAA2B,CAAC;AAE1C,cAAc,uBAAuB,CAAC;AAEtC,mBAAmB,uBAAuB,CAAC"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { Reactive } from 'vue';
|
|
2
|
+
import { PluginHandle, InferFactoryData, InferFactoryOutputs, PluginFactoryLike } from '@platforma-sdk/model';
|
|
3
|
+
/** Per-plugin reactive model exposed to consumers via usePlugin(). */
|
|
4
|
+
export interface PluginState<Data = unknown, Outputs = unknown> {
|
|
5
|
+
readonly model: Reactive<{
|
|
6
|
+
data: Data;
|
|
7
|
+
outputs: Outputs extends Record<string, unknown> ? {
|
|
8
|
+
[K in keyof Outputs]: Outputs[K] | undefined;
|
|
9
|
+
} : Record<string, unknown>;
|
|
10
|
+
outputErrors: Outputs extends Record<string, unknown> ? {
|
|
11
|
+
[K in keyof Outputs]?: Error;
|
|
12
|
+
} : Record<string, Error | undefined>;
|
|
13
|
+
}>;
|
|
14
|
+
}
|
|
15
|
+
/** Internal interface for plugin access — provided via Vue injection to usePlugin(). */
|
|
16
|
+
export interface PluginAccess {
|
|
17
|
+
getOrCreatePluginState<F extends PluginFactoryLike>(handle: PluginHandle<F>): PluginState<InferFactoryData<F>, InferFactoryOutputs<F>>;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Composable for accessing a plugin's reactive model: data, outputs, and outputErrors.
|
|
21
|
+
*
|
|
22
|
+
* Mirrors the `app.model` access pattern — `plugin.model.data` is reactive and deep-watched,
|
|
23
|
+
* mutations are automatically queued and sent to storage.
|
|
24
|
+
*
|
|
25
|
+
* @param handle - Opaque plugin handle obtained from `app.plugins`.
|
|
26
|
+
* @typeParam F - The plugin factory type (inferred from the handle)
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* ```vue
|
|
30
|
+
* <script setup lang="ts">
|
|
31
|
+
* import { usePlugin, type InferPluginHandle } from '@platforma-sdk/ui-vue';
|
|
32
|
+
* import type { CounterPlugin } from './plugins/counter';
|
|
33
|
+
*
|
|
34
|
+
* const props = defineProps<{ instance: InferPluginHandle<CounterPlugin> }>();
|
|
35
|
+
* const plugin = usePlugin(props.instance);
|
|
36
|
+
*
|
|
37
|
+
* plugin.model.data.count += 1; // reactive, triggers storage update
|
|
38
|
+
* plugin.model.outputs.displayText // computed, plugin's own outputs only
|
|
39
|
+
* plugin.model.outputErrors.displayText // Error | undefined
|
|
40
|
+
* </script>
|
|
41
|
+
* ```
|
|
42
|
+
*/
|
|
43
|
+
export declare function usePlugin<F extends PluginFactoryLike>(handle: PluginHandle<F>): PluginState<InferFactoryData<F>, InferFactoryOutputs<F>>;
|
|
44
|
+
//# sourceMappingURL=usePlugin.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"usePlugin.d.ts","sourceRoot":"","sources":["../src/usePlugin.ts"],"names":[],"mappings":"AAAA,OAAO,EAAU,KAAK,QAAQ,EAAE,MAAM,KAAK,CAAC;AAE5C,OAAO,KAAK,EACV,YAAY,EACZ,gBAAgB,EAChB,mBAAmB,EACnB,iBAAiB,EAClB,MAAM,sBAAsB,CAAC;AAE9B,sEAAsE;AACtE,MAAM,WAAW,WAAW,CAAC,IAAI,GAAG,OAAO,EAAE,OAAO,GAAG,OAAO;IAC5D,QAAQ,CAAC,KAAK,EAAE,QAAQ,CAAC;QACvB,IAAI,EAAE,IAAI,CAAC;QACX,OAAO,EAAE,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC5C;aAAG,CAAC,IAAI,MAAM,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC,GAAG,SAAS;SAAE,GAChD,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QAC5B,YAAY,EAAE,OAAO,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACjD;aAAG,CAAC,IAAI,MAAM,OAAO,CAAC,CAAC,EAAE,KAAK;SAAE,GAChC,MAAM,CAAC,MAAM,EAAE,KAAK,GAAG,SAAS,CAAC,CAAC;KACvC,CAAC,CAAC;CACJ;AAED,wFAAwF;AACxF,MAAM,WAAW,YAAY;IAC3B,sBAAsB,CAAC,CAAC,SAAS,iBAAiB,EAChD,MAAM,EAAE,YAAY,CAAC,CAAC,CAAC,GACtB,WAAW,CAAC,gBAAgB,CAAC,CAAC,CAAC,EAAE,mBAAmB,CAAC,CAAC,CAAC,CAAC,CAAC;CAC7D;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,SAAS,CAAC,CAAC,SAAS,iBAAiB,EAAE,MAAM,EAAE,YAAY,CAAC,CAAC,CAAC,4DAW7E"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { inject as t } from "vue";
|
|
2
|
+
import { pluginDataKey as i } from "./defineApp.js";
|
|
3
|
+
function s(r) {
|
|
4
|
+
const e = t(i);
|
|
5
|
+
if (!e)
|
|
6
|
+
throw new Error(
|
|
7
|
+
"usePlugin requires a V3 block (BlockModelV3). Make sure the block uses apiVersion 3 and the plugin is installed."
|
|
8
|
+
);
|
|
9
|
+
return e.getOrCreatePluginState(r);
|
|
10
|
+
}
|
|
11
|
+
export {
|
|
12
|
+
s as usePlugin
|
|
13
|
+
};
|
|
14
|
+
//# sourceMappingURL=usePlugin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"usePlugin.js","sources":["../src/usePlugin.ts"],"sourcesContent":["import { inject, type Reactive } from \"vue\";\nimport { pluginDataKey } from \"./defineApp\";\nimport type {\n PluginHandle,\n InferFactoryData,\n InferFactoryOutputs,\n PluginFactoryLike,\n} from \"@platforma-sdk/model\";\n\n/** Per-plugin reactive model exposed to consumers via usePlugin(). */\nexport interface PluginState<Data = unknown, Outputs = unknown> {\n readonly model: Reactive<{\n data: Data;\n outputs: Outputs extends Record<string, unknown>\n ? { [K in keyof Outputs]: Outputs[K] | undefined }\n : Record<string, unknown>;\n outputErrors: Outputs extends Record<string, unknown>\n ? { [K in keyof Outputs]?: Error }\n : Record<string, Error | undefined>;\n }>;\n}\n\n/** Internal interface for plugin access — provided via Vue injection to usePlugin(). */\nexport interface PluginAccess {\n getOrCreatePluginState<F extends PluginFactoryLike>(\n handle: PluginHandle<F>,\n ): PluginState<InferFactoryData<F>, InferFactoryOutputs<F>>;\n}\n\n/**\n * Composable for accessing a plugin's reactive model: data, outputs, and outputErrors.\n *\n * Mirrors the `app.model` access pattern — `plugin.model.data` is reactive and deep-watched,\n * mutations are automatically queued and sent to storage.\n *\n * @param handle - Opaque plugin handle obtained from `app.plugins`.\n * @typeParam F - The plugin factory type (inferred from the handle)\n *\n * @example\n * ```vue\n * <script setup lang=\"ts\">\n * import { usePlugin, type InferPluginHandle } from '@platforma-sdk/ui-vue';\n * import type { CounterPlugin } from './plugins/counter';\n *\n * const props = defineProps<{ instance: InferPluginHandle<CounterPlugin> }>();\n * const plugin = usePlugin(props.instance);\n *\n * plugin.model.data.count += 1; // reactive, triggers storage update\n * plugin.model.outputs.displayText // computed, plugin's own outputs only\n * plugin.model.outputErrors.displayText // Error | undefined\n * </script>\n * ```\n */\nexport function usePlugin<F extends PluginFactoryLike>(handle: PluginHandle<F>) {\n const access = inject<PluginAccess>(pluginDataKey);\n\n if (!access) {\n throw new Error(\n \"usePlugin requires a V3 block (BlockModelV3). \" +\n \"Make sure the block uses apiVersion 3 and the plugin is installed.\",\n );\n }\n\n return access.getOrCreatePluginState<F>(handle);\n}\n"],"names":["usePlugin","handle","access","inject","pluginDataKey"],"mappings":";;AAqDO,SAASA,EAAuCC,GAAyB;AAC9E,QAAMC,IAASC,EAAqBC,CAAa;AAEjD,MAAI,CAACF;AACH,UAAM,IAAI;AAAA,MACR;AAAA,IAAA;AAKJ,SAAOA,EAAO,uBAA0BD,CAAM;AAChD;"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@platforma-sdk/ui-vue",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.58.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"sideEffects": [
|
|
6
6
|
"**/*.css"
|
|
@@ -28,8 +28,8 @@
|
|
|
28
28
|
"lru-cache": "^11.2.2",
|
|
29
29
|
"vue": "^3.5.24",
|
|
30
30
|
"zod": "~3.23.8",
|
|
31
|
-
"@
|
|
32
|
-
"@
|
|
31
|
+
"@milaboratories/uikit": "2.10.33",
|
|
32
|
+
"@platforma-sdk/model": "1.58.0"
|
|
33
33
|
},
|
|
34
34
|
"devDependencies": {
|
|
35
35
|
"@faker-js/faker": "^9.2.0",
|
|
@@ -44,10 +44,10 @@
|
|
|
44
44
|
"typescript": "~5.6.3",
|
|
45
45
|
"vite": "^6.4.1",
|
|
46
46
|
"vitest": "^4.0.16",
|
|
47
|
-
"@milaboratories/
|
|
48
|
-
"@milaboratories/ts-builder": "1.2.11",
|
|
47
|
+
"@milaboratories/build-configs": "1.5.0",
|
|
49
48
|
"@milaboratories/ts-configs": "1.2.1",
|
|
50
|
-
"@milaboratories/
|
|
49
|
+
"@milaboratories/ts-builder": "1.2.11",
|
|
50
|
+
"@milaboratories/helpers": "1.13.5"
|
|
51
51
|
},
|
|
52
52
|
"scripts": {
|
|
53
53
|
"dev": "ts-builder serve --target browser-lib",
|