tauri-kargo-tools 0.2.0 → 0.2.2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tauri-kargo-tools",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
4
4
  "description": "",
5
5
  "files": ["src"],
6
6
  "exports": { "./*": "./src/*" },
package/src/api.ts CHANGED
@@ -185,8 +185,8 @@ async writeFileBinary(
185
185
  /* =============== Routes: Explorer =============== */
186
186
 
187
187
  /** POST /api/explorer */
188
- explorer(body: T.ExplorerReq): Promise<T.ExplorerResult> {
189
- return this.postJson<T.ExplorerResult>("/api/explorer", body);
188
+ explorer(body: T.ExplorerRequest): Promise<T.ExplorerResponse> {
189
+ return this.postJson<T.ExplorerResponse>("/api/explorer", body);
190
190
  }
191
191
 
192
192
  /* =============== Routes: Servers =============== */
@@ -0,0 +1,497 @@
1
+ export type Type<T> =
2
+
3
+ | "string"
4
+
5
+ | "number"
6
+
7
+ | "boolean"
8
+
9
+ | { union: readonly string[]; }
10
+
11
+ | { ref: readonly (keyof T)[]; }
12
+
13
+
14
+
15
+ | { arrayOf: Type<T>; }
16
+
17
+ | { optional: Type<T>; };
18
+
19
+
20
+
21
+ export type Structure<T> = { [name: string]: Type<T>; };
22
+ export type EntityFieldValue = number | string | boolean | { ref: string } | EntityFieldValue[]
23
+ export type Entity = { [name: string]: EntityFieldValue }
24
+ export type EntityMap = { [id: string]: Entity }
25
+
26
+ export function createModel<T extends { [name: string]: Structure<T>; }>(def: T): T {
27
+ return def;
28
+ }
29
+
30
+
31
+
32
+ export class DataModel<T extends { [name: string]: Structure<T>; }> {
33
+
34
+ def: T;
35
+
36
+ map: { [id: string]: ModelElement<T>; } = {};
37
+ types: { [id: string]: keyof T } = {}
38
+ idx = 0;
39
+ idForModelElement: Map<ModelElement<T>, string>;
40
+ constructor(def: T) {
41
+ this.idForModelElement = new Map()
42
+ this.def = def;
43
+
44
+ }
45
+ getValues(): ModelElement<T>[] {
46
+ return Object.values(this.map)
47
+ }
48
+
49
+ getValue(ref: string): ModelElement<T> {
50
+ return this.map[ref];
51
+ }
52
+
53
+ is<K extends keyof T>(m: any, type: K): m is Interfaces<T>[K] {
54
+ const id = this.idForModelElement.get(m)
55
+ if (!id) {
56
+ return false
57
+ }
58
+ return this.types[id] === type
59
+
60
+ }
61
+ isRef<K extends keyof T>(ref: any, type: K): ref is Ref<T, K> {
62
+ if (typeof ref.ref !== "string") {
63
+ return false
64
+ }
65
+ const value = this.getValue(ref.ref)
66
+ if (ref.ref && this.is(value, type)) {
67
+ ref.getValue = () => value
68
+ return true
69
+ }
70
+ return false
71
+
72
+ }
73
+ initField(ref: string, name: string, value: EntityFieldValue) {
74
+ const entity = this.map[ref]
75
+ if (entity) {
76
+ const type = this.types[ref]
77
+ const typeField = this.def[type][name]
78
+ if (this.checkType(typeField, this.map, value, {})) {
79
+ (entity as any)[name] = value
80
+ }
81
+ }
82
+
83
+
84
+ }
85
+
86
+ init(value: EntityMap) {
87
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return undefined;
88
+
89
+ const map: { [id: string]: keyof T } = {};
90
+
91
+ for (const id in value) {
92
+ let ok = false;
93
+
94
+ for (const t in this.def) {
95
+ if (this.check(id, value, t as keyof T, map)) {
96
+ ok = true;
97
+ break;
98
+ }
99
+ }
100
+
101
+ if (!ok) return undefined; // ou throw new Error(`No type matches for ${id}`)
102
+ }
103
+ this.map = value as any
104
+ for (let e of Object.entries(this.map)) {
105
+ this.idForModelElement.set(e[1], e[0])
106
+ }
107
+ this.types = map
108
+
109
+ return map;
110
+ }
111
+
112
+
113
+ check<K extends keyof T>(id: string, value: any, type: K, map: { [id: string]: keyof T }): boolean {
114
+ if (map[id] === type) {
115
+ return true
116
+ }
117
+ if (map[id] !== undefined) {
118
+ return false
119
+ }
120
+
121
+
122
+ map[id] = type
123
+ const struct = this.def[type]
124
+ if (this.checkStructure(struct, value, value[id], map)) {
125
+ return true
126
+ }
127
+ delete map[id]
128
+ return false
129
+
130
+
131
+ }
132
+ checkStructure(
133
+ struct: Structure<T>,
134
+ value: any,
135
+ obj: any,
136
+ map: { [id: string]: keyof T }
137
+ ): boolean {
138
+ if (typeof obj !== "object" || obj === null || Array.isArray(obj)) return false;
139
+
140
+ // mode strict : pas de propriétés inconnues
141
+ for (const k of Object.keys(obj)) {
142
+ if (!(k in struct)) return false;
143
+ }
144
+
145
+ for (const [prop, t] of Object.entries(struct)) {
146
+ const v = (obj as any)[prop];
147
+
148
+ // optional
149
+ if (this.isOptionalType(t)) {
150
+ if (v === undefined) continue;
151
+ if (!this.checkType(t.optional, value, v, map)) return false;
152
+ continue;
153
+ }
154
+
155
+ // required
156
+ if (v === undefined) return false;
157
+ if (!this.checkType(t as Type<T>, value, v, map)) return false;
158
+ }
159
+
160
+ return true;
161
+ }
162
+
163
+ private checkType(
164
+ t: Type<T>,
165
+ root: any, // le "value" global (map d'objets)
166
+ v: any,
167
+ map: { [id: string]: keyof T }
168
+ ): boolean {
169
+ if (t === "string") return typeof v === "string";
170
+ if (t === "number") return typeof v === "number";
171
+ if (t === "boolean") return typeof v === "boolean";
172
+
173
+ if (this.isUnionType(t)) {
174
+ return typeof v === "string" && t.union.includes(v);
175
+ }
176
+
177
+ if (this.isArrayOfType(t)) {
178
+ return Array.isArray(v) && v.every(el => this.checkType(t.arrayOf, root, el, map));
179
+ }
180
+
181
+ if (this.isOptionalType(t)) {
182
+ return v === undefined || this.checkType(t.optional, root, v, map);
183
+ }
184
+
185
+ if (this.isRefType(t)) {
186
+ // runtime minimal : { ref: "$0" }
187
+ if (typeof v !== "object" || v === null || Array.isArray(v)) return false;
188
+ const rid = (v as any).ref;
189
+
190
+ if (typeof rid !== "string") return false;
191
+ if (!(rid in root)) return false;
192
+
193
+ // doit matcher AU MOINS un type autorisé
194
+ const allowed = t.ref as readonly (keyof T)[];
195
+ for (const candidate of allowed) {
196
+ if (this.check(rid, root, candidate, map)) {
197
+ (v as any).getValue = () => root[rid]
198
+ return true;
199
+ }
200
+ }
201
+ return false;
202
+ }
203
+
204
+ return false;
205
+ }
206
+
207
+ private isUnionType(x: any): x is { union: readonly string[] } {
208
+ return typeof x === "object" && x !== null && Array.isArray(x.union);
209
+ }
210
+ private isRefType(x: any): x is { ref: readonly (keyof T)[] } {
211
+ return typeof x === "object" && x !== null && Array.isArray(x.ref);
212
+ }
213
+ private isArrayOfType(x: any): x is { arrayOf: Type<T> } {
214
+ return typeof x === "object" && x !== null && "arrayOf" in x;
215
+ }
216
+ private isOptionalType(x: any): x is { optional: Type<T> } {
217
+ return typeof x === "object" && x !== null && "optional" in x;
218
+ }
219
+
220
+
221
+ createValue<K extends keyof T>(type: K, value: Interfaces<T>[K]): Ref<T, K> {
222
+
223
+ const ref = `$${this.idx}`;
224
+
225
+ this.idx++;
226
+
227
+ this.map[ref] = value as any;
228
+ this.types[ref] = type
229
+ this.idForModelElement.set(this.map[ref], ref)
230
+
231
+ return { ref: ref, getValue: () => value } as any;
232
+
233
+ }
234
+
235
+
236
+
237
+
238
+ }
239
+
240
+ export type RefUnion<TDefs extends { [name: string]: Structure<TDefs>; }> = {
241
+ [K in keyof TDefs]: Ref<TDefs, K>;
242
+ }[keyof TDefs];
243
+
244
+ type UnwrapOptional<V> = V extends { optional: infer O; } ? O : V;
245
+
246
+ export type Ref<TDefs extends { [name: string]: Structure<TDefs>; }, K extends keyof TDefs> = { ref: string, getValue(): Interfaces<TDefs>[K] }
247
+
248
+ // Résout un champ du DSL vers un type TypeScript concret
249
+
250
+ type ToTsType<
251
+
252
+ TDefs extends { [name: string]: Structure<TDefs>; },
253
+
254
+ V
255
+
256
+ > =
257
+
258
+ V extends "string" ? string :
259
+
260
+ V extends "number" ? number :
261
+
262
+ V extends "boolean" ? boolean :
263
+
264
+ V extends { ref: infer K; }
265
+
266
+ ? K extends readonly (keyof TDefs)[]
267
+
268
+ ? { [U in K[number]]: Ref<TDefs, U> }[K[number]]
269
+
270
+ : never
271
+
272
+ : V extends { arrayOf: infer O; }
273
+
274
+ ? ToTsType<TDefs, O>[]
275
+
276
+ : V extends { optional: infer O; }
277
+
278
+ ? ToTsType<TDefs, O>
279
+
280
+ : V extends { union: infer L; }
281
+
282
+ ? L extends readonly string[]
283
+
284
+ ? L[number]
285
+
286
+ : never
287
+
288
+ : never;
289
+
290
+
291
+
292
+ // Matérialise une "entité" (clé de TDefs) en interface concrète
293
+
294
+ export type ToInterface<
295
+
296
+ TDefs extends { [name: string]: Structure<TDefs>; },
297
+
298
+ Name extends keyof TDefs
299
+
300
+ > =
301
+
302
+ // Propriétés requises (pas 'optional')
303
+
304
+ {
305
+ -readonly [P in keyof TDefs[Name]as TDefs[Name][P] extends { optional: any; } ? never : P]:
306
+
307
+ ToTsType<TDefs, TDefs[Name][P]>;
308
+
309
+ }
310
+
311
+ &
312
+
313
+ // Propriétés optionnelles ('optional')
314
+
315
+ {
316
+
317
+ -readonly [P in keyof TDefs[Name]as TDefs[Name][P] extends { optional: any; } ? P : never]?:
318
+
319
+ ToTsType<TDefs, UnwrapOptional<TDefs[Name][P]>>;
320
+
321
+ };
322
+
323
+ // Matérialise l’ensemble des interfaces
324
+
325
+ type Interfaces<TDefs extends { [name: string]: Structure<TDefs>; }> = {
326
+
327
+ [K in keyof TDefs]: ToInterface<TDefs, K>;
328
+
329
+ };
330
+
331
+ type ModelElement<TDefs extends { [name: string]: Structure<TDefs>; }> = Interfaces<TDefs>[keyof TDefs];
332
+
333
+
334
+
335
+ type Def<TDefs> = { [name: string]: Structure<TDefs> };
336
+
337
+ export class TypeImplicationChecker<TDefs extends Def<TDefs>> {
338
+ constructor(public readonly def: TDefs) { }
339
+
340
+ /** Calcule la matrice implies[A][B] (point fixe) et la liste des incohérences A⇒B (A≠B). */
341
+ analyze() {
342
+ const names = Object.keys(this.def) as (keyof TDefs & string)[];
343
+ const implies: Record<string, Record<string, boolean>> = {};
344
+
345
+ for (const a of names) {
346
+ implies[a] = {};
347
+ for (const b of names) implies[a][b] = (a === b);
348
+ }
349
+
350
+ let changed = true;
351
+ while (changed) {
352
+ changed = false;
353
+
354
+ for (const a of names) {
355
+ for (const b of names) {
356
+ if (a === b) continue;
357
+ if (implies[a][b]) continue;
358
+
359
+ if (this.impliesStructure(a, b, implies)) {
360
+ implies[a][b] = true;
361
+ changed = true;
362
+ }
363
+ }
364
+ }
365
+ }
366
+
367
+ const problems: Array<{ A: string; B: string }> = [];
368
+ for (const a of names) {
369
+ for (const b of names) {
370
+ if (a !== b && implies[a][b]) problems.push({ A: a, B: b });
371
+ }
372
+ }
373
+
374
+ return { implies, problems };
375
+ }
376
+
377
+ /** Vrai si le système est cohérent au sens “aucun A⇒B avec A≠B”. */
378
+ isCoherent(): boolean {
379
+ return this.analyze().problems.length === 0;
380
+ }
381
+
382
+ /** Lève une erreur si incohérent. */
383
+ assertCoherent(): void {
384
+ const { problems } = this.analyze();
385
+ if (problems.length) {
386
+ const msg = problems.map(p => `${p.A} => ${p.B}`).join(", ");
387
+ throw new Error(`Incoherent type system (implications found): ${msg}`);
388
+ }
389
+ }
390
+
391
+ // ----------------- Implémentation interne -----------------
392
+
393
+ private isUnion(x: any): x is { union: readonly string[] } {
394
+ return typeof x === "object" && x !== null && Array.isArray(x.union);
395
+ }
396
+ private isRef(x: any): x is { ref: readonly string[] } {
397
+ return typeof x === "object" && x !== null && Array.isArray(x.ref);
398
+ }
399
+ private isArrayOf(x: any): x is { arrayOf: any } {
400
+ return typeof x === "object" && x !== null && "arrayOf" in x;
401
+ }
402
+ private isOptional(x: any): x is { optional: any } {
403
+ return typeof x === "object" && x !== null && "optional" in x;
404
+ }
405
+
406
+ private impliesType(a: Type<TDefs>, b: Type<TDefs>, implies: Record<string, Record<string, boolean>>): boolean {
407
+ // primitives
408
+ if (a === "string") return b === "string";
409
+ if (a === "number") return b === "number";
410
+ if (a === "boolean") return b === "boolean";
411
+
412
+ // union
413
+ if (this.isUnion(a)) {
414
+ if (b === "string") return true; // union de literals ⊆ string
415
+ if (!this.isUnion(b)) return false;
416
+ const A = new Set(a.union);
417
+ return b.union.every(x => A.has(x)); // A ⊆ B => union(A) ⇒ union(B)
418
+ }
419
+
420
+ // optional
421
+ if (this.isOptional(a)) {
422
+ if (!this.isOptional(b)) return false; // optional(X) ne peut pas impliquer Y (undefined possible)
423
+ return this.impliesType(a.optional, b.optional, implies);
424
+ }
425
+ if (this.isOptional(b)) {
426
+ // X ⇒ optional(Y) si X⇒Y
427
+ return this.impliesType(a, b.optional, implies);
428
+ }
429
+
430
+ // arrayOf
431
+ if (this.isArrayOf(a)) {
432
+ if (!this.isArrayOf(b)) return false;
433
+ return this.impliesType(a.arrayOf, b.arrayOf, implies);
434
+ }
435
+
436
+ // ref
437
+ if (this.isRef(a)) {
438
+ if (!this.isRef(b)) return false;
439
+ // {ref:[A1..]} ⇒ {ref:[B1..]} si ∀Ai ∃Bj : Ai ⇒ Bj
440
+ return a.ref.every(ai => b.ref.some(bj => implies[String(ai)]?.[String(bj)] === true));
441
+ }
442
+
443
+ return false;
444
+ }
445
+
446
+ /**
447
+ * Structure-level implication A⇒B en mode STRICT (comme votre checkStructure):
448
+ * - un objet A ne doit pas avoir de propriété inconnue pour B
449
+ * - les requis de B doivent être requis dans A
450
+ * - les types doivent impliquer
451
+ */
452
+ private impliesStructure(
453
+ A: keyof TDefs & string,
454
+ B: keyof TDefs & string,
455
+ implies: Record<string, Record<string, boolean>>
456
+ ): boolean {
457
+ const SA = this.def[A];
458
+ const SB = this.def[B];
459
+
460
+ // STRICT: toutes les props de A doivent exister dans B (sinon un objet A échoue chez B)
461
+ for (const p in SA) {
462
+ if (!(p in SB)) return false;
463
+ }
464
+
465
+ for (const p in SB) {
466
+ const tb = SB[p];
467
+ const ta = SA[p];
468
+
469
+ const bOpt = this.isOptional(tb);
470
+ const aOpt = ta !== undefined ? this.isOptional(ta) : false;
471
+
472
+ if (!bOpt) {
473
+ // B requiert p => A doit le requérir aussi
474
+ if (ta === undefined) return false;
475
+ if (aOpt) return false;
476
+ if (!this.impliesType(ta, tb, implies)) return false;
477
+ } else {
478
+ // B optionnel : A peut ne pas avoir p ; si A l'a, il doit impliquer le noyau de B
479
+ if (ta === undefined) continue;
480
+ const taCore = aOpt ? (ta as any).optional : ta;
481
+ const tbCore = (tb as any).optional;
482
+ if (!this.impliesType(taCore, tbCore, implies)) return false;
483
+ }
484
+ }
485
+
486
+ return true;
487
+ }
488
+ }
489
+
490
+ export interface DataModelReponse {
491
+ type: 'dataModelReponse';
492
+ value: any;
493
+ }
494
+ export interface RefReponse {
495
+ type: 'refReponse';
496
+ ref: string;
497
+ }
@@ -0,0 +1,62 @@
1
+ import { Ref, Structure, ToInterface, DataModel, RefUnion } from "./base";
2
+ export type KeysOfType<T, V> = { [K in keyof T]-?: T[K] extends V ? K : never }[keyof T];
3
+ export type Value = string | number | boolean | Value[]
4
+ export interface DataModelProp<T extends { [name: string]: Structure<T>; }, K extends keyof T, F extends keyof ToInterface<T, K>> {
5
+ ref: Ref<T, K>
6
+ field: F
7
+ value: ToInterface<T, K>[F]
8
+ }
9
+ export interface SetDataModelProp<T extends { [name: string]: Structure<T>; }, K extends keyof T, F extends keyof ToInterface<T, K>> extends DataModelProp<T, K, F> {
10
+ type: "setDataModelProp"
11
+ }
12
+
13
+ export class DataModelClient<T extends { [name: string]: Structure<T>; }> {
14
+ def: T;
15
+ resolveDataModel: (dm: DataModel<T>) => void = () => { };
16
+ resolveRefUnion: (ref: RefUnion<T>) => void = () => { };
17
+
18
+ async setProp<K extends keyof T, F extends KeysOfType<ToInterface<T, K>, Value>>(dvp: DataModelProp<T, K, F>): Promise<DataModel<T>> {
19
+ const setDataModelProp: SetDataModelProp<T, K, F> = { ...dvp, type: "setDataModelProp" }
20
+ self.postMessage(JSON.parse(JSON.stringify(setDataModelProp)))
21
+
22
+ const r = new Promise<DataModel<T>>((resolve) => {
23
+ this.resolveDataModel = resolve;
24
+ })
25
+ return r;
26
+
27
+ }
28
+ async getDataModel(): Promise<DataModel<T>> {
29
+ self.postMessage({ type: "getDataModel" })
30
+ const r = new Promise<DataModel<T>>((resolve) => {
31
+ this.resolveDataModel = resolve;
32
+ })
33
+ return r;
34
+ }
35
+ async getSelf() {
36
+ self.postMessage({ type: "getSelf" })
37
+ const r = new Promise<RefUnion<T>>((resolve) => {
38
+ this.resolveRefUnion = resolve;
39
+ })
40
+ return r;
41
+
42
+
43
+ }
44
+ constructor(def: T) {
45
+ this.def = def;
46
+ self.addEventListener("message", (event) => {
47
+ const data = event.data;
48
+ if (data.type === "refReponse") {
49
+ const refReponse = data as { type: 'refReponse', ref: string };
50
+ const r = { ref: refReponse.ref } as RefUnion<T>;
51
+ this.resolveRefUnion(r);
52
+ return;
53
+ }
54
+ if (data.type === "dataModelReponse") {
55
+ const r = new DataModel<T>(this.def)
56
+ r.init(data.value)
57
+ this.resolveDataModel(r);
58
+ }
59
+
60
+ })
61
+ }
62
+ }
@@ -0,0 +1,44 @@
1
+ import { set } from "../container";
2
+ import { Ref, Structure, ToInterface, DataModel, DataModelReponse, RefUnion } from "./base";
3
+ import { Value } from "./client";
4
+
5
+ export interface SetDataModelProp {
6
+ type: "setDataModelProp",
7
+ ref: { ref: string }
8
+ field: string
9
+ value: Value
10
+ }
11
+ export interface SimpleRef {
12
+ ref: string
13
+ }
14
+
15
+ export class DataModelServer<T extends { [name: string]: Structure<T>; }> extends DataModel<T> {
16
+
17
+ constructor(def: T) {
18
+ super(def)
19
+ }
20
+ process(worker: Worker, check: (setDataModelProp: SetDataModelProp) => boolean, ref:SimpleRef) {
21
+ worker.addEventListener("message", async (event) => {
22
+ const data = event.data;
23
+ if (data.type === "setDataModelProp") {
24
+ const setDataModelProp = data as SetDataModelProp;
25
+ if (check(setDataModelProp)) {
26
+ this.initField(setDataModelProp.ref.ref, setDataModelProp.field, setDataModelProp.value);
27
+ worker.postMessage(this.cloneMap());
28
+ }
29
+ }
30
+ if (data.type === "getDataModel") {
31
+ worker.postMessage(this.cloneMap());
32
+ }
33
+ if (data.type === "getSelf") {
34
+ const refReponse = { type: 'refReponse', ref: ref.ref };
35
+ worker.postMessage(refReponse);
36
+ }
37
+ })
38
+
39
+ }
40
+ cloneMap(): DataModelReponse {
41
+ return { type: 'dataModelReponse', value: JSON.parse(JSON.stringify(this.map)) };
42
+ }
43
+
44
+ }
@@ -0,0 +1,13 @@
1
+ import { createModel } from "../schema/base";
2
+
3
+
4
+ export const model = createModel({
5
+ Cell: {
6
+ nom: "string",
7
+ state: "boolean",
8
+ },
9
+ Groupe: {
10
+ state:"boolean",
11
+ membres: { arrayOf: { ref: ["Cell"] } }
12
+ }
13
+ })
package/src/test/index.ts CHANGED
@@ -1,5 +1,96 @@
1
1
  import * as test from "../test"
2
2
  import * as api from '../api'
3
+ import * as schema from "../schema/base"
4
+ import { DataModelServer, SetDataModelProp } from "../schema/server"
5
+ import { model } from "./data-model"
6
+ test.test("Test schema client server", async () => {
7
+
8
+ const server = new DataModelServer(model)
9
+ const state = server.createValue("Cell", { nom: "A", state: false })
10
+ const groupe = server.createValue("Groupe", { membres: [state], state: false })
11
+ let resolve: (b: SetDataModelProp[]) => void = () => { }
12
+ const p = new Promise<SetDataModelProp[]>((r) => {
13
+ resolve = r
14
+ })
15
+ const m: SetDataModelProp[] = []
16
+ const worker = new Worker(new URL("./worker.ts", import.meta.url), { type: "module" });
17
+ server.process(worker, (op) => {
18
+ m.push(op)
19
+ if (m.length >= 2) {
20
+ resolve(m)
21
+ }
22
+ return true
23
+ }, groupe)
24
+ const v = await p
25
+ test.assertEquals(v[0].ref.ref === (state.ref as any), true)
26
+ test.assertEquals(v[1].ref.ref === (groupe.ref as any), true)
27
+ worker.terminate()
28
+
29
+ })
30
+ test.test("Test schema simple un type", async () => {
31
+ const vue = new schema.DataModel({
32
+ Point: {
33
+ x: "number",
34
+ y: "number"
35
+ }
36
+ })
37
+ const p = vue.createValue("Point", { x: 10, y: 15 })
38
+ test.assertEquals(p.getValue().x, 10)
39
+ test.assertEquals(p.getValue().y, 15)
40
+ const anotherVue = new schema.DataModel({
41
+ Point: {
42
+ x: "number",
43
+ y: "number"
44
+ }
45
+ })
46
+ const copy = JSON.parse(JSON.stringify(vue.map))
47
+ const map = anotherVue.init(copy)
48
+ test.assertEquals(map !== undefined, true)
49
+ test.assertEquals(map![p.ref], "Point")
50
+ const p2 = anotherVue.getValue(p.ref)
51
+ test.assertEquals(p2.x, 10)
52
+ test.assertEquals(p2.y, 15)
53
+ console.log(map)
54
+
55
+
56
+
57
+ })
58
+
59
+ test.test("Test schema simple avec deux type", async () => {
60
+ const vue = new schema.DataModel({
61
+ Point: {
62
+ x: "number",
63
+ y: "number"
64
+ },
65
+ Personne: {
66
+ nom: "string",
67
+ prenom: "string"
68
+ }
69
+ })
70
+ const point = vue.createValue("Point", { x: 10, y: 15 })
71
+ const personne = vue.createValue("Personne", { nom: "Toto", prenom: "Lili" })
72
+ let testPoint = false
73
+ let testPersonne = false
74
+ for (const p of vue.getValues()) {
75
+ if (vue.is(p, "Point")) {
76
+ test.assertEquals(p.x, 10)
77
+ test.assertEquals(p.y, 15)
78
+ testPoint = true
79
+ }
80
+ if (vue.is(p, "Personne")) {
81
+ test.assertEquals(p.nom, "Toto")
82
+ test.assertEquals(p.prenom, "Lili")
83
+ testPersonne = true
84
+ }
85
+ }
86
+ test.assertEquals(testPoint, true)
87
+ test.assertEquals(testPersonne, true)
88
+
89
+
90
+
91
+
92
+
93
+ })
3
94
  test.test("Test read file", async () => {
4
95
 
5
96
  const client = api.createClient();
@@ -35,7 +126,8 @@ test.test("Test read file", async () => {
35
126
 
36
127
  }), false, "pas dans rep")
37
128
  }
38
-
129
+ rep = await client.explorer({ type: "array", path: "C:/Users/david/Documents/GitHub/tauriKargoExamples/examples/test-api-file-typescript" })
130
+ console.log(rep)
39
131
 
40
132
 
41
133
 
@@ -0,0 +1,27 @@
1
+ import { DataModelClient } from "../schema/client"
2
+ import { model } from "./data-model"
3
+
4
+
5
+ const client = new DataModelClient(model);
6
+
7
+
8
+ (async () => {
9
+ const dm = await client.getDataModel();
10
+ for (const o of dm.getValues()) {
11
+ if (dm.is(o, "Groupe")) {
12
+ for (const ref of o.membres) {
13
+ console.log(dm.map)
14
+ const tmp = await client.setProp({ ref: ref, field: "state", value: true });
15
+ console.log(tmp.map)
16
+ }
17
+
18
+
19
+ }
20
+ }
21
+ const selfRef = await client.getSelf();
22
+ if (dm.isRef(selfRef, "Groupe")) {
23
+ await client.setProp({ ref: selfRef, field: "state", value: true });
24
+ }
25
+
26
+
27
+ })()
package/src/types.ts CHANGED
@@ -104,29 +104,86 @@ export interface StopAllResp {
104
104
  export interface ExplorerReq {
105
105
  path?: string;
106
106
  }
107
+ // api-explorer
108
+ // =====================
109
+ // /api/explorer - types
110
+ // =====================
107
111
 
108
- export type ExplorerElement = ExplorerDirectory | ExplorerFile
112
+ export type ExplorerMode = "array" | "tree";
109
113
 
110
- export interface ExplorerFile {
114
+ export type ExplorerRequest = {
115
+ /** vide => CWD côté serveur ; relatif => CWD+rel ; absolu => tel quel */
116
+ path?: string;
117
+
118
+ /** "array" => content = fichiers à plat ; "tree" => content = arborescence */
119
+ type?: ExplorerMode;
120
+
121
+ /** profondeur max (enfants directs = 1). Si absent et type absent => default serveur: 1 */
122
+ maxDeep?: number;
123
+
124
+ /** nombre max de fichiers retournés (limite globale) */
125
+ maxSize?: number;
126
+ };
127
+
128
+ // ---- réponses ----
129
+
130
+ export type ExplorerErrorResponse = {
131
+ type: "error";
132
+ message: string;
133
+ };
134
+
135
+ export type ExplorerFileResponse = {
111
136
  type: "file";
137
+ /** chemin absolu (format "joli" Windows/UNC si applicable) */
112
138
  path: string;
113
139
  name: string;
140
+ /** chemin absolu du parent ou null si racine */
114
141
  parent: string | null;
115
- }
142
+ };
116
143
 
117
- export interface ExplorerDirectory {
118
- type: "directory";
119
- name:string;
144
+ export type ExplorerArrayFileItem = {
145
+ type:'file'
146
+ name: string;
147
+ /** chemin absolu du fichier */
120
148
  path: string;
121
- parent: string | null;
122
- content: ExplorerElement[];
123
- }
124
-
125
- export interface ExplorerError {
126
- type: "error";
127
- message: string;
128
- }
129
-
149
+ };
150
+
151
+ export type ExplorerTreeItem =
152
+ | {
153
+ type: "file";
154
+ name: string;
155
+ path: string; // absolu
156
+ }
157
+ | {
158
+ type: "directory";
159
+ name: string;
160
+ path: string; // absolu
161
+ /** peut être absent si maxDeep empêche d'expand */
162
+ content?: ExplorerTreeItem[];
163
+ };
164
+
165
+ export type ExplorerDirectoryResponse =
166
+ | {
167
+ type: "directory";
168
+ path: string; // absolu
169
+ parent: string | null;
170
+ /** si request.type === "array" */
171
+ content: ExplorerArrayFileItem[];
172
+ }
173
+ | {
174
+ type: "directory";
175
+ path: string; // absolu
176
+ parent: string | null;
177
+ /** si request.type === "tree" (ou mode absent côté compat) */
178
+ content: ExplorerTreeItem[];
179
+ };
180
+
181
+ export type ExplorerResponse =
182
+ | ExplorerErrorResponse
183
+ | ExplorerFileResponse
184
+ | ExplorerDirectoryResponse;
185
+
186
+ //
130
187
  export interface NewServerReq {
131
188
  code: string;
132
189
  executable: string;
@@ -156,10 +213,7 @@ export interface StopServerResp {
156
213
  ========================= */
157
214
 
158
215
  /** Result of /api/explorer (200) */
159
- export type ExplorerResult = ExplorerFile | ExplorerDirectory;
160
216
 
161
- /** Error shapes from /api/explorer (404/500) */
162
- export type ExplorerErrorResult = ExplorerError;
163
217
 
164
218
  /* =========================
165
219
  (Optionnel) Types d’IO par route