@ultrst-armada/kloks 1.2.0 → 1.3.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/package.json +2 -1
- package/packages/audio-midi/dist/chunk-3GRGELA4.js +6 -6
- package/packages/audio-midi/dist/index.d.ts +3 -3
- package/packages/audio-midi/dist/index.js +10 -10
- package/packages/backend/dist/BaseAccountProvider-DduKf5HG.d.ts +1 -1
- package/packages/backend/dist/BaseStorageAdapter-tosOoSUg.d.ts +1 -1
- package/packages/backend/dist/api/index.d.ts +1 -1
- package/packages/backend/dist/auth/index.d.ts +1 -1
- package/packages/backend/dist/chunk-4ZFXEFOE.js +2 -2
- package/packages/backend/dist/chunk-BM4QMVPA.js +338 -0
- package/packages/backend/dist/chunk-J2J24JVC.js +1 -1
- package/packages/backend/dist/chunk-KTLD7Z4L.js +3 -3
- package/packages/backend/dist/{chunk-MN54ZIKZ.js → chunk-KX3OTBQ3.js} +309 -4
- package/packages/backend/dist/chunk-RCCQ7IRM.js +3 -3
- package/packages/backend/dist/chunk-TWCEVES2.js +1 -1
- package/packages/backend/dist/control-plane/index.d.ts +1 -1
- package/packages/backend/dist/index.d.ts +146 -5
- package/packages/backend/dist/index.js +50 -19
- package/packages/backend/dist/krnl-services.js +2 -2
- package/packages/backend/dist/services/index.d.ts +168 -1
- package/packages/backend/dist/services/index.js +15 -3
- package/packages/backend/dist/srkl/index.d.ts +108 -1
- package/packages/backend/dist/srkl/index.js +15 -1
- package/packages/backend/dist/storage/index.d.ts +1 -1
- package/packages/backend/dist/workspace/index.d.ts +1 -1
- package/packages/orchestrations/dist/index.d.ts +3 -3
- package/packages/orchestrations/dist/index.js +3 -3
- package/packages/services/dist/index.d.ts +3 -3
- package/packages/state-graph/dist/index.js +11 -11
- package/packages/backend/dist/chunk-P4HL4C6V.js +0 -149
- package/packages/backend/dist/{chunk-OYQKQAEF.js → chunk-CU7RZBMF.js} +4 -4
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ultrst-armada/kloks",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public",
|
|
@@ -103,6 +103,7 @@
|
|
|
103
103
|
"scripts": {
|
|
104
104
|
"build": "pnpm -r run build",
|
|
105
105
|
"postbuild": "node -e \"import('fs').then(fs => { for (const pkg of fs.readdirSync('packages')) { const d = 'packages/' + pkg + '/dist'; if (!fs.existsSync(d)) continue; for (const f of fs.readdirSync(d, { recursive: true })) { const p = d + '/' + f; if (fs.statSync(p).isFile() && (p.endsWith('.js') || p.endsWith('.d.ts'))) { const c = fs.readFileSync(p, 'utf8'); const n = c.replaceAll('@kloks/', '@ultrst-armada/kloks/').replaceAll('@ultrst/kloks/', '@ultrst-armada/kloks/').replaceAll('\\\"kloks/', '\\\"@ultrst-armada/kloks/').replaceAll('\\'kloks/', '\\'@ultrst-armada/kloks/'); if (c !== n) fs.writeFileSync(p, n); } } } })\"",
|
|
106
|
+
"prepublishOnly": "npm run postbuild",
|
|
106
107
|
"test": "vitest run",
|
|
107
108
|
"typecheck": "pnpm -r run typecheck",
|
|
108
109
|
"clean": "pnpm -r run clean"
|
|
@@ -33,7 +33,7 @@ var DEFAULT_NAMED_BUSSES = [
|
|
|
33
33
|
];
|
|
34
34
|
|
|
35
35
|
// src/engine/audio/synthEngine/synthEngine.ts
|
|
36
|
-
import { midiToFreq } from "@kloks/state-graph";
|
|
36
|
+
import { midiToFreq } from "@ultrst-armada/kloks/state-graph";
|
|
37
37
|
|
|
38
38
|
// src/engine/audio/outputRouter.ts
|
|
39
39
|
var MultiChannelOutputRouter = class {
|
|
@@ -320,7 +320,7 @@ function createAudioGraphNode(node) {
|
|
|
320
320
|
}
|
|
321
321
|
|
|
322
322
|
// src/engine/audio/synthEngine/synthEngine.ts
|
|
323
|
-
import { getActiveEffectsForNode } from "@kloks/state-graph";
|
|
323
|
+
import { getActiveEffectsForNode } from "@ultrst-armada/kloks/state-graph";
|
|
324
324
|
|
|
325
325
|
// src/engine/audio/dsp/saturationCurves.ts
|
|
326
326
|
function generateSaturationCurve(type, drive = 1.5, samples = 4096) {
|
|
@@ -2090,14 +2090,14 @@ function buildOutputChannelList(totalChannels, descriptions = {}) {
|
|
|
2090
2090
|
}
|
|
2091
2091
|
|
|
2092
2092
|
// src/engine/audio/audioInputManager/audioInputManager.ts
|
|
2093
|
-
import { topLevelIoRegistry } from "@kloks/state-graph";
|
|
2093
|
+
import { topLevelIoRegistry } from "@ultrst-armada/kloks/state-graph";
|
|
2094
2094
|
import {
|
|
2095
2095
|
loadStoredConfig,
|
|
2096
2096
|
saveStoredConfig,
|
|
2097
2097
|
enumerateSystemAudioDevices
|
|
2098
|
-
} from "@kloks/state-graph";
|
|
2099
|
-
import { acquireAudioInputStream, stopAudioMediaStream } from "@kloks/state-graph";
|
|
2100
|
-
import { AudioBusDriver } from "@kloks/state-graph";
|
|
2098
|
+
} from "@ultrst-armada/kloks/state-graph";
|
|
2099
|
+
import { acquireAudioInputStream, stopAudioMediaStream } from "@ultrst-armada/kloks/state-graph";
|
|
2100
|
+
import { AudioBusDriver } from "@ultrst-armada/kloks/state-graph";
|
|
2101
2101
|
var DEFAULT_NAMED_INPUTS = DEFAULT_NAMED_BUSSES;
|
|
2102
2102
|
var AudioInputManager = class {
|
|
2103
2103
|
audioCtx = null;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as _ultrst_armada_krnl from '@ultrst-armada/krnl';
|
|
2
2
|
import { ServiceDefinition, IService } from '@ultrst-armada/krnl';
|
|
3
|
-
import { INodeVisuals, SpaceNode, NodeConnection, MidiNote, SynthPreset, ContainerNode, PlayerNode, SamplePresetType, AudioTrackData, HardwareChannelInfo, NamedAudioBus, AudioDeviceInfo, ConnectionsConfig, MidiInputDevice, StoredPacket, RecorderNode, CurveTrackData, CurvePoint, EffectNode, CompressorEffectConfig, MultibandCompressorEffectConfig } from '@kloks/state-graph';
|
|
4
|
-
export { AudioConcepts, BufferConcepts, BufferCycleCalculation } from '@kloks/state-graph';
|
|
3
|
+
import { INodeVisuals, SpaceNode, NodeConnection, MidiNote, SynthPreset, ContainerNode, PlayerNode, SamplePresetType, AudioTrackData, HardwareChannelInfo, NamedAudioBus, AudioDeviceInfo, ConnectionsConfig, MidiInputDevice, StoredPacket, RecorderNode, CurveTrackData, CurvePoint, EffectNode, CompressorEffectConfig, MultibandCompressorEffectConfig } from '@ultrst-armada/kloks/state-graph';
|
|
4
|
+
export { AudioConcepts, BufferConcepts, BufferCycleCalculation } from '@ultrst-armada/kloks/state-graph';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Pro-Audio Hardware Input/Output Manager (Named Bus Architecture)
|
|
@@ -934,7 +934,7 @@ declare const OscServiceFacade: {
|
|
|
934
934
|
|
|
935
935
|
/**
|
|
936
936
|
* @file index.ts
|
|
937
|
-
* @description @kloks/audio-midi — KrnlTS-backed Pro-Audio Named Bus & Reactive Web MIDI Services.
|
|
937
|
+
* @description @ultrst-armada/kloks/audio-midi — KrnlTS-backed Pro-Audio Named Bus & Reactive Web MIDI Services.
|
|
938
938
|
* Converted from Skipo audio-midi engine into first-class KrnlTS services and orchestrators.
|
|
939
939
|
*/
|
|
940
940
|
|
|
@@ -607,7 +607,7 @@ var MidiManager = class {
|
|
|
607
607
|
import {
|
|
608
608
|
AudioConcepts,
|
|
609
609
|
BufferConcepts
|
|
610
|
-
} from "@kloks/state-graph";
|
|
610
|
+
} from "@ultrst-armada/kloks/state-graph";
|
|
611
611
|
|
|
612
612
|
// src/engine/index.ts
|
|
613
613
|
import {
|
|
@@ -615,7 +615,7 @@ import {
|
|
|
615
615
|
configureControlDispatcher,
|
|
616
616
|
configureNodePerformanceTracker,
|
|
617
617
|
configureSolidifyExecution
|
|
618
|
-
} from "@kloks/state-graph";
|
|
618
|
+
} from "@ultrst-armada/kloks/state-graph";
|
|
619
619
|
|
|
620
620
|
// src/engine/audio/retrospectiveBufferEngine/retrospectiveBufferEngine.ts
|
|
621
621
|
var RetrospectiveBufferEngine = class {
|
|
@@ -896,9 +896,9 @@ var RetrospectiveBufferEngine = class {
|
|
|
896
896
|
var retrospectiveBufferEngine = new RetrospectiveBufferEngine();
|
|
897
897
|
|
|
898
898
|
// src/engine/audio/sequencerEngine/sequencerEngine.ts
|
|
899
|
-
import { shouldTriggerFireAtClock } from "@kloks/state-graph";
|
|
900
|
-
import { getEffectsAffectingWire } from "@kloks/state-graph";
|
|
901
|
-
import { getActiveEffectsForNode as getActiveEffectsForNode2 } from "@kloks/state-graph";
|
|
899
|
+
import { shouldTriggerFireAtClock } from "@ultrst-armada/kloks/state-graph";
|
|
900
|
+
import { getEffectsAffectingWire } from "@ultrst-armada/kloks/state-graph";
|
|
901
|
+
import { getActiveEffectsForNode as getActiveEffectsForNode2 } from "@ultrst-armada/kloks/state-graph";
|
|
902
902
|
|
|
903
903
|
// src/engine/audio/curveEngine.ts
|
|
904
904
|
var CurveEngine = class {
|
|
@@ -1010,7 +1010,7 @@ var CurveEngine = class {
|
|
|
1010
1010
|
var curveEngine = new CurveEngine();
|
|
1011
1011
|
|
|
1012
1012
|
// src/engine/audio/sequencerStepProcessor.ts
|
|
1013
|
-
import { getActiveEffectsForNode } from "@kloks/state-graph";
|
|
1013
|
+
import { getActiveEffectsForNode } from "@ultrst-armada/kloks/state-graph";
|
|
1014
1014
|
function playContainerNoteWithEffects(container, note, nodes, connections, bpm, isPlaying, nodeVisuals) {
|
|
1015
1015
|
const activeEffects = getActiveEffectsForNode(container, nodes, connections);
|
|
1016
1016
|
const delayEffect = activeEffects.find((e) => e.effectType === "delay");
|
|
@@ -1088,8 +1088,8 @@ function processActiveContainers(activeContainerPlayheads, containerMap, prevBea
|
|
|
1088
1088
|
}
|
|
1089
1089
|
|
|
1090
1090
|
// src/engine/audio/sequencerEngine/sequencerEngine.ts
|
|
1091
|
-
import { calculateNextQuantizeBeat } from "@kloks/state-graph";
|
|
1092
|
-
import { executeCodeNodeScript } from "@kloks/state-graph";
|
|
1091
|
+
import { calculateNextQuantizeBeat } from "@ultrst-armada/kloks/state-graph";
|
|
1092
|
+
import { executeCodeNodeScript } from "@ultrst-armada/kloks/state-graph";
|
|
1093
1093
|
var SequencerEngine = class {
|
|
1094
1094
|
bpm = 120;
|
|
1095
1095
|
isPlaying = false;
|
|
@@ -1669,8 +1669,8 @@ var SequencerEngine = class {
|
|
|
1669
1669
|
var sequencerEngine = new SequencerEngine();
|
|
1670
1670
|
|
|
1671
1671
|
// src/engine/midi/midiManager/midiManager.ts
|
|
1672
|
-
import { midiToNoteName } from "@kloks/state-graph";
|
|
1673
|
-
import { topLevelIoRegistry } from "@kloks/state-graph";
|
|
1672
|
+
import { midiToNoteName } from "@ultrst-armada/kloks/state-graph";
|
|
1673
|
+
import { topLevelIoRegistry } from "@ultrst-armada/kloks/state-graph";
|
|
1674
1674
|
var MidiManager2 = class {
|
|
1675
1675
|
midiAccess = null;
|
|
1676
1676
|
listeners = /* @__PURE__ */ new Set();
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { SkipoBase } from '@kloks/functions';
|
|
1
|
+
import { SkipoBase } from '@ultrst-armada/kloks/functions';
|
|
2
2
|
|
|
3
3
|
type StorageMode = 'local' | 'local_hosted' | 'remote';
|
|
4
4
|
type StorageProviderType = 'local_fs' | 's3' | 'backblaze_b2' | 'b2' | 'cloudflare_r2' | 'r2' | 'supabase' | 'firebase' | 'google_drive';
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { B as BaseAccountProvider, U as User, C as CreateUserData, a as AuthSession } from '../BaseAccountProvider-DduKf5HG.js';
|
|
2
2
|
export { A as AuthCredentials, R as RateLimiter, S as SessionManager, b as SignupInput, c as UserRole } from '../BaseAccountProvider-DduKf5HG.js';
|
|
3
|
-
import { SkipoBase } from '@kloks/functions';
|
|
3
|
+
import { SkipoBase } from '@ultrst-armada/kloks/functions';
|
|
4
4
|
|
|
5
5
|
declare class PasswordHasher {
|
|
6
6
|
static hash(password: string, saltRounds?: number): Promise<string>;
|
|
@@ -26,7 +26,7 @@ var PasswordHasher = class {
|
|
|
26
26
|
};
|
|
27
27
|
|
|
28
28
|
// src/auth/BaseAccountProvider.ts
|
|
29
|
-
import { SkipoBase } from "@kloks/functions";
|
|
29
|
+
import { SkipoBase } from "@ultrst-armada/kloks/functions";
|
|
30
30
|
var BaseAccountProvider = class extends SkipoBase {
|
|
31
31
|
rateLimiter = new RateLimiter();
|
|
32
32
|
sessionManager = new SessionManager();
|
|
@@ -153,7 +153,7 @@ var LocalAccountProvider = class extends BaseAccountProvider {
|
|
|
153
153
|
};
|
|
154
154
|
|
|
155
155
|
// src/auth/AdminGuard.ts
|
|
156
|
-
import { SkipoBase as SkipoBase2 } from "@kloks/functions";
|
|
156
|
+
import { SkipoBase as SkipoBase2 } from "@ultrst-armada/kloks/functions";
|
|
157
157
|
var AdminGuard = class _AdminGuard extends SkipoBase2 {
|
|
158
158
|
static async verify(authHeaderOrToken, provider) {
|
|
159
159
|
const session = await _AdminGuard.verifyUser(authHeaderOrToken, provider);
|
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createService,
|
|
3
|
+
createServiceDefinition,
|
|
4
|
+
createServiceProxy
|
|
5
|
+
} from "./chunk-IPP4VURQ.js";
|
|
6
|
+
|
|
7
|
+
// src/services/ServiceBoundary.ts
|
|
8
|
+
function createLocalService(definition) {
|
|
9
|
+
return createService(createServiceDefinition(definition));
|
|
10
|
+
}
|
|
11
|
+
function createRemoteService(definition, adapter, endpoint) {
|
|
12
|
+
return createServiceProxy(definition, adapter, endpoint);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// src/services/CompositeBackend.ts
|
|
16
|
+
var CompositeBackend = class {
|
|
17
|
+
id;
|
|
18
|
+
name;
|
|
19
|
+
version;
|
|
20
|
+
services = /* @__PURE__ */ new Map();
|
|
21
|
+
capabilitiesList = [];
|
|
22
|
+
constructor(name, version = "1.0.0") {
|
|
23
|
+
this.name = name;
|
|
24
|
+
this.id = `backend-${name}`;
|
|
25
|
+
this.version = version;
|
|
26
|
+
}
|
|
27
|
+
registerService(service) {
|
|
28
|
+
this.services.set(service.name, service);
|
|
29
|
+
return this;
|
|
30
|
+
}
|
|
31
|
+
getService(name) {
|
|
32
|
+
return this.services.get(name);
|
|
33
|
+
}
|
|
34
|
+
getServices() {
|
|
35
|
+
return Array.from(this.services.values());
|
|
36
|
+
}
|
|
37
|
+
registerCapability(capability) {
|
|
38
|
+
if (!this.capabilitiesList.includes(capability)) {
|
|
39
|
+
this.capabilitiesList.push(capability);
|
|
40
|
+
}
|
|
41
|
+
return this;
|
|
42
|
+
}
|
|
43
|
+
async start() {
|
|
44
|
+
for (const service of this.services.values()) {
|
|
45
|
+
await service.start();
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
async stop() {
|
|
49
|
+
for (const service of this.services.values()) {
|
|
50
|
+
await service.stop();
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
async call(op, input, ctx) {
|
|
54
|
+
const firstDot = op.indexOf(".");
|
|
55
|
+
if (firstDot === -1) {
|
|
56
|
+
throw new Error(`[CompositeBackend] Invalid operation '${op}'. Expected 'service.operation'.`);
|
|
57
|
+
}
|
|
58
|
+
const serviceName = op.slice(0, firstDot);
|
|
59
|
+
const operationName = op.slice(firstDot + 1);
|
|
60
|
+
const service = this.services.get(serviceName);
|
|
61
|
+
if (!service) {
|
|
62
|
+
throw new Error(`[CompositeBackend] Service '${serviceName}' not registered in '${this.name}'.`);
|
|
63
|
+
}
|
|
64
|
+
return await service.call(operationName, input, ctx);
|
|
65
|
+
}
|
|
66
|
+
getState() {
|
|
67
|
+
const aggregated = {};
|
|
68
|
+
for (const [name, service] of this.services) {
|
|
69
|
+
aggregated[name] = service.getState();
|
|
70
|
+
}
|
|
71
|
+
return Object.freeze(aggregated);
|
|
72
|
+
}
|
|
73
|
+
setState(_mutation) {
|
|
74
|
+
}
|
|
75
|
+
on(event, handler) {
|
|
76
|
+
const [serviceName, eventName] = event.split(".");
|
|
77
|
+
const service = this.services.get(serviceName);
|
|
78
|
+
if (!service) return () => {
|
|
79
|
+
};
|
|
80
|
+
return service.on(eventName, handler);
|
|
81
|
+
}
|
|
82
|
+
emit(event, payload) {
|
|
83
|
+
const [serviceName, eventName] = event.split(".");
|
|
84
|
+
const service = this.services.get(serviceName);
|
|
85
|
+
if (service) {
|
|
86
|
+
service.emit(eventName, payload);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
getDefinition() {
|
|
90
|
+
const ops = {};
|
|
91
|
+
for (const service of this.services.values()) {
|
|
92
|
+
const def = service.getDefinition();
|
|
93
|
+
for (const [opKey, opDef] of Object.entries(def.operations)) {
|
|
94
|
+
ops[`${service.name}.${opKey}`] = opDef;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return {
|
|
98
|
+
id: this.id,
|
|
99
|
+
name: this.name,
|
|
100
|
+
version: this.version,
|
|
101
|
+
operations: ops,
|
|
102
|
+
capabilities: this.capabilitiesList.map((c) => ({
|
|
103
|
+
id: c,
|
|
104
|
+
name: c,
|
|
105
|
+
protocols: ["http", "ws", "udp", "p2p"]
|
|
106
|
+
})),
|
|
107
|
+
protocols: ["http", "ws", "udp", "p2p"]
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
// src/services/EndpointGenerator.ts
|
|
113
|
+
var EndpointGenerator = class {
|
|
114
|
+
static generateEndpoints(backend, basePort = 8e3) {
|
|
115
|
+
const def = backend.getDefinition();
|
|
116
|
+
const ops = Object.keys(def.operations);
|
|
117
|
+
return {
|
|
118
|
+
httpRoutes: ops.map((op) => `/api/${op.replace(".", "/")}`),
|
|
119
|
+
wsChannels: ops.map((op) => `channel:${op}`),
|
|
120
|
+
udpEndpoints: [`udp://127.0.0.1:${basePort}`],
|
|
121
|
+
p2pBindings: ops.map((op) => `p2p://${backend.name}/${op}`)
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
static generateDescriptors(backend, host = "127.0.0.1", port = 8080) {
|
|
125
|
+
return [
|
|
126
|
+
{ protocol: "http", address: host, port, capabilities: ["requestResponse"] },
|
|
127
|
+
{ protocol: "ws", address: host, port, capabilities: ["requestResponse", "pubSub"] },
|
|
128
|
+
{ protocol: "udp", address: host, port, capabilities: ["datagram"] },
|
|
129
|
+
{ protocol: "p2p", address: `p2p://${backend.name}`, capabilities: ["peerToPeer"] }
|
|
130
|
+
];
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
// src/services/ServiceGenerators.ts
|
|
135
|
+
function createBackend(name) {
|
|
136
|
+
return new CompositeBackend(name);
|
|
137
|
+
}
|
|
138
|
+
function createCompositeService(name) {
|
|
139
|
+
return new CompositeBackend(name);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// src/services/nodeGraphPersistence/types.ts
|
|
143
|
+
var LOCAL_STORAGE_GRAPHS_PREFIX = "depm_graph_doc_";
|
|
144
|
+
|
|
145
|
+
// src/services/nodeGraphPersistence/helpers.ts
|
|
146
|
+
function validateGraphDocument(data) {
|
|
147
|
+
const errors = [];
|
|
148
|
+
if (!data || typeof data !== "object") {
|
|
149
|
+
return { valid: false, errors: ["Invalid JSON: payload must be a JSON object"] };
|
|
150
|
+
}
|
|
151
|
+
if (!data.appId) errors.push("Missing required property: appId");
|
|
152
|
+
if (!data.appName) errors.push("Missing required property: appName");
|
|
153
|
+
if (!data.graph || typeof data.graph !== "object") {
|
|
154
|
+
errors.push("Missing or invalid property: graph");
|
|
155
|
+
} else {
|
|
156
|
+
if (!Array.isArray(data.graph.nodes)) errors.push("Missing or invalid property: graph.nodes (must be array)");
|
|
157
|
+
if (!Array.isArray(data.graph.edges)) errors.push("Missing or invalid property: graph.edges (must be array)");
|
|
158
|
+
}
|
|
159
|
+
if (errors.length > 0) {
|
|
160
|
+
return { valid: false, errors };
|
|
161
|
+
}
|
|
162
|
+
return {
|
|
163
|
+
valid: true,
|
|
164
|
+
errors: [],
|
|
165
|
+
document: data
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
function exportGraphAsJsonFile(graphDoc) {
|
|
169
|
+
if (typeof window === "undefined") return;
|
|
170
|
+
const jsonString = JSON.stringify(graphDoc, null, 2);
|
|
171
|
+
const blob = new Blob([jsonString], { type: "application/json" });
|
|
172
|
+
const url = URL.createObjectURL(blob);
|
|
173
|
+
const safeName = (graphDoc.appName || "graph").toLowerCase().replace(/[^a-z0-9_-]/g, "_");
|
|
174
|
+
const filename = `${safeName}_${graphDoc.appId}.graph.json`;
|
|
175
|
+
const link = window.document.createElement("a");
|
|
176
|
+
link.href = url;
|
|
177
|
+
link.download = filename;
|
|
178
|
+
window.document.body.appendChild(link);
|
|
179
|
+
link.click();
|
|
180
|
+
window.document.body.removeChild(link);
|
|
181
|
+
URL.revokeObjectURL(url);
|
|
182
|
+
}
|
|
183
|
+
async function parseGraphJsonFile(file) {
|
|
184
|
+
try {
|
|
185
|
+
const text = await file.text();
|
|
186
|
+
const parsed = JSON.parse(text);
|
|
187
|
+
return validateGraphDocument(parsed);
|
|
188
|
+
} catch (err) {
|
|
189
|
+
return {
|
|
190
|
+
valid: false,
|
|
191
|
+
errors: [`Failed to parse JSON: ${err.message}`]
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// src/services/nodeGraphPersistence/nodeGraphPersistence.ts
|
|
197
|
+
var NodeGraphPersistenceService = class {
|
|
198
|
+
/**
|
|
199
|
+
* Generates a fully compliant v1.0.0 JSON document representing the app's complete node graph.
|
|
200
|
+
*/
|
|
201
|
+
createGraphDocument(options) {
|
|
202
|
+
const { app, nodes, edges, deployments = [], viewport, storageConnection } = options;
|
|
203
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
204
|
+
const targetProvider = storageConnection?.provider || "local_fs";
|
|
205
|
+
const targetBucketOrFolder = storageConnection?.localFolderName || storageConnection?.b2BucketName || storageConnection?.s3BucketName || storageConnection?.r2BucketName || storageConnection?.supabaseBucketName || "Vault";
|
|
206
|
+
const safeAppName = (app.name || "graph").toLowerCase().replace(/[^a-z0-9_-]/g, "_");
|
|
207
|
+
const filePath = `graphs/${safeAppName}_${app.id}.graph.json`;
|
|
208
|
+
return {
|
|
209
|
+
$schema: "https://skipo.dev/schemas/depm-node-graph.v1.json",
|
|
210
|
+
version: "1.0.0",
|
|
211
|
+
appId: app.id,
|
|
212
|
+
appName: app.name,
|
|
213
|
+
appIcon: app.icon || "Layers",
|
|
214
|
+
description: app.description,
|
|
215
|
+
publicLink: app.publicLink || `https://${safeAppName}.ecosystem.network`,
|
|
216
|
+
createdAt: app.createdAt || now,
|
|
217
|
+
updatedAt: now,
|
|
218
|
+
exportedAt: now,
|
|
219
|
+
viewport: viewport || { zoom: 1, pan: { x: 0, y: 0 } },
|
|
220
|
+
graph: {
|
|
221
|
+
nodes: nodes.filter((n) => !n.appId || n.appId === app.id),
|
|
222
|
+
edges: edges.filter((e) => !e.appId || e.appId === app.id)
|
|
223
|
+
},
|
|
224
|
+
deployments: deployments.filter(
|
|
225
|
+
(d) => nodes.some((n) => (!n.appId || n.appId === app.id) && n.deploymentId === d.id)
|
|
226
|
+
),
|
|
227
|
+
storageMetadata: {
|
|
228
|
+
provider: targetProvider,
|
|
229
|
+
targetName: targetBucketOrFolder,
|
|
230
|
+
targetPath: filePath,
|
|
231
|
+
storageConnectionId: storageConnection?.id,
|
|
232
|
+
syncedAt: now
|
|
233
|
+
}
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* Validates a parsed JSON object to verify it matches the NodeGraphDocument schema.
|
|
238
|
+
*/
|
|
239
|
+
validateGraphDocument(data) {
|
|
240
|
+
return validateGraphDocument(data);
|
|
241
|
+
}
|
|
242
|
+
/**
|
|
243
|
+
* Saves the node graph to the selected user storage option (Local Folder or Cloud Bucket)
|
|
244
|
+
* and caches it in LocalStorage.
|
|
245
|
+
*/
|
|
246
|
+
async saveGraphToStorage(options, customDirectoryHandle) {
|
|
247
|
+
const document = this.createGraphDocument(options);
|
|
248
|
+
const jsonString = JSON.stringify(document, null, 2);
|
|
249
|
+
const provider = options.storageConnection?.provider || "local_fs";
|
|
250
|
+
try {
|
|
251
|
+
if (typeof window !== "undefined" && window.localStorage) {
|
|
252
|
+
window.localStorage.setItem(`${LOCAL_STORAGE_GRAPHS_PREFIX}${document.appId}`, jsonString);
|
|
253
|
+
window.localStorage.setItem(`depm_latest_saved_graph_${document.appId}`, JSON.stringify({
|
|
254
|
+
updatedAt: document.updatedAt,
|
|
255
|
+
provider,
|
|
256
|
+
targetName: document.storageMetadata?.targetName
|
|
257
|
+
}));
|
|
258
|
+
}
|
|
259
|
+
} catch {
|
|
260
|
+
}
|
|
261
|
+
if (provider === "local_fs" && typeof window !== "undefined") {
|
|
262
|
+
try {
|
|
263
|
+
if (customDirectoryHandle) {
|
|
264
|
+
const fileName = `${(document.appName || "graph").toLowerCase().replace(/[^a-z0-9_-]/g, "_")}_${document.appId}.graph.json`;
|
|
265
|
+
const fileHandle = await customDirectoryHandle.getFileHandle(fileName, { create: true });
|
|
266
|
+
const writable = await fileHandle.createWritable();
|
|
267
|
+
await writable.write(jsonString);
|
|
268
|
+
await writable.close();
|
|
269
|
+
return {
|
|
270
|
+
success: true,
|
|
271
|
+
document,
|
|
272
|
+
message: `Saved node graph directly to local folder "${customDirectoryHandle.name || "Vault"}"`,
|
|
273
|
+
targetPath: `${customDirectoryHandle.name}/${fileName}`
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
} catch (err) {
|
|
277
|
+
console.warn("Native FS Handle save warning:", err);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
const targetPath = document.storageMetadata?.targetPath || `graphs/${document.appId}.graph.json`;
|
|
281
|
+
const targetName = document.storageMetadata?.targetName || "Vault";
|
|
282
|
+
const simulatedLatency = provider === "local_fs" ? 10 : 80;
|
|
283
|
+
await new Promise((resolve) => setTimeout(resolve, simulatedLatency));
|
|
284
|
+
return {
|
|
285
|
+
success: true,
|
|
286
|
+
document,
|
|
287
|
+
message: `Node graph saved to ${(provider || "storage").toUpperCase().replace("_", " ")} (${targetName}: ${targetPath})`,
|
|
288
|
+
targetPath
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
/**
|
|
292
|
+
* Loads a cached or previously saved node graph document for an app.
|
|
293
|
+
*/
|
|
294
|
+
loadSavedGraph(appId) {
|
|
295
|
+
try {
|
|
296
|
+
if (typeof window !== "undefined" && window.localStorage) {
|
|
297
|
+
const stored = window.localStorage.getItem(`${LOCAL_STORAGE_GRAPHS_PREFIX}${appId}`);
|
|
298
|
+
if (stored) {
|
|
299
|
+
const parsed = JSON.parse(stored);
|
|
300
|
+
const validation = this.validateGraphDocument(parsed);
|
|
301
|
+
if (validation.valid && validation.document) {
|
|
302
|
+
return validation.document;
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
} catch {
|
|
307
|
+
}
|
|
308
|
+
return null;
|
|
309
|
+
}
|
|
310
|
+
/**
|
|
311
|
+
* Triggers a browser download of the node graph as a portable JSON file.
|
|
312
|
+
*/
|
|
313
|
+
exportGraphAsJsonFile(graphDoc) {
|
|
314
|
+
exportGraphAsJsonFile(graphDoc);
|
|
315
|
+
}
|
|
316
|
+
/**
|
|
317
|
+
* Reads and parses a graph JSON file from an input file element.
|
|
318
|
+
*/
|
|
319
|
+
async parseGraphJsonFile(file) {
|
|
320
|
+
return parseGraphJsonFile(file);
|
|
321
|
+
}
|
|
322
|
+
};
|
|
323
|
+
var nodeGraphPersistence = new NodeGraphPersistenceService();
|
|
324
|
+
|
|
325
|
+
export {
|
|
326
|
+
createLocalService,
|
|
327
|
+
createRemoteService,
|
|
328
|
+
CompositeBackend,
|
|
329
|
+
EndpointGenerator,
|
|
330
|
+
createBackend,
|
|
331
|
+
createCompositeService,
|
|
332
|
+
LOCAL_STORAGE_GRAPHS_PREFIX,
|
|
333
|
+
validateGraphDocument,
|
|
334
|
+
exportGraphAsJsonFile,
|
|
335
|
+
parseGraphJsonFile,
|
|
336
|
+
NodeGraphPersistenceService,
|
|
337
|
+
nodeGraphPersistence
|
|
338
|
+
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/workspace/WorkspaceFS.ts
|
|
2
|
-
import { SkipoBase } from "@kloks/functions";
|
|
2
|
+
import { SkipoBase } from "@ultrst-armada/kloks/functions";
|
|
3
3
|
var WorkspaceFS = class extends SkipoBase {
|
|
4
4
|
primary;
|
|
5
5
|
syncEngine;
|
|
@@ -54,7 +54,7 @@ var WorkspaceFS = class extends SkipoBase {
|
|
|
54
54
|
};
|
|
55
55
|
|
|
56
56
|
// src/workspace/SyncEngine.ts
|
|
57
|
-
import { SkipoBase as SkipoBase2 } from "@kloks/functions";
|
|
57
|
+
import { SkipoBase as SkipoBase2 } from "@ultrst-armada/kloks/functions";
|
|
58
58
|
var SyncEngine = class extends SkipoBase2 {
|
|
59
59
|
primary;
|
|
60
60
|
backups;
|
|
@@ -87,7 +87,7 @@ var SyncEngine = class extends SkipoBase2 {
|
|
|
87
87
|
};
|
|
88
88
|
|
|
89
89
|
// src/workspace/WorkspaceCloneSyncEngine.ts
|
|
90
|
-
import { SkipoBase as SkipoBase3 } from "@kloks/functions";
|
|
90
|
+
import { SkipoBase as SkipoBase3 } from "@ultrst-armada/kloks/functions";
|
|
91
91
|
var WorkspaceCloneSyncEngine = class extends SkipoBase3 {
|
|
92
92
|
constructor() {
|
|
93
93
|
super({ name: "WorkspaceCloneSyncEngine" });
|