stone-lang 0.1.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/README.md +52 -0
- package/StoneEngine.js +879 -0
- package/StoneEngineService.js +1727 -0
- package/adapters/FileSystemAdapter.js +230 -0
- package/adapters/OutputAdapter.js +208 -0
- package/adapters/index.js +6 -0
- package/cli/CLIOutputAdapter.js +196 -0
- package/cli/DaemonClient.js +349 -0
- package/cli/JSONOutputAdapter.js +135 -0
- package/cli/ReplSession.js +567 -0
- package/cli/ViewerServer.js +590 -0
- package/cli/commands/check.js +84 -0
- package/cli/commands/daemon.js +189 -0
- package/cli/commands/kill.js +66 -0
- package/cli/commands/package.js +713 -0
- package/cli/commands/ps.js +65 -0
- package/cli/commands/run.js +537 -0
- package/cli/entry.js +169 -0
- package/cli/index.js +14 -0
- package/cli/stonec.js +358 -0
- package/cli/test-compiler.js +181 -0
- package/cli/viewer/index.html +495 -0
- package/daemon/IPCServer.js +455 -0
- package/daemon/ProcessManager.js +327 -0
- package/daemon/ProcessRunner.js +307 -0
- package/daemon/daemon.js +398 -0
- package/daemon/index.js +16 -0
- package/frontend/analysis/index.js +5 -0
- package/frontend/analysis/livenessAnalyzer.js +568 -0
- package/frontend/analysis/treeShaker.js +265 -0
- package/frontend/index.js +20 -0
- package/frontend/parsing/astBuilder.js +2196 -0
- package/frontend/parsing/index.js +7 -0
- package/frontend/parsing/sonParser.js +592 -0
- package/frontend/parsing/stoneAstTypes.js +703 -0
- package/frontend/parsing/terminal-registry.js +435 -0
- package/frontend/parsing/tokenizer.js +692 -0
- package/frontend/type-checker/OverloadedFunctionType.js +43 -0
- package/frontend/type-checker/TypeEnvironment.js +165 -0
- package/frontend/type-checker/bidirectionalInference.js +149 -0
- package/frontend/type-checker/index.js +10 -0
- package/frontend/type-checker/moduleAnalysis.js +248 -0
- package/frontend/type-checker/operatorMappings.js +35 -0
- package/frontend/type-checker/overloadResolution.js +605 -0
- package/frontend/type-checker/typeChecker.js +452 -0
- package/frontend/type-checker/typeCompatibility.js +389 -0
- package/frontend/type-checker/visitors/controlFlow.js +483 -0
- package/frontend/type-checker/visitors/functions.js +604 -0
- package/frontend/type-checker/visitors/index.js +38 -0
- package/frontend/type-checker/visitors/literals.js +341 -0
- package/frontend/type-checker/visitors/modules.js +159 -0
- package/frontend/type-checker/visitors/operators.js +109 -0
- package/frontend/type-checker/visitors/statements.js +768 -0
- package/frontend/types/index.js +5 -0
- package/frontend/types/operatorMap.js +134 -0
- package/frontend/types/types.js +2046 -0
- package/frontend/utils/errorCollector.js +244 -0
- package/frontend/utils/index.js +5 -0
- package/frontend/utils/moduleResolver.js +479 -0
- package/package.json +50 -0
- package/packages/browserCache.js +359 -0
- package/packages/fetcher.js +236 -0
- package/packages/index.js +130 -0
- package/packages/lockfile.js +271 -0
- package/packages/manifest.js +291 -0
- package/packages/packageResolver.js +356 -0
- package/packages/resolver.js +310 -0
- package/packages/semver.js +635 -0
|
@@ -0,0 +1,359 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stone Browser Package Cache
|
|
3
|
+
*
|
|
4
|
+
* Manages package caching in the browser using IndexedDB.
|
|
5
|
+
* Packages are cached until logout.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const DB_NAME = 'stone-packages';
|
|
9
|
+
const DB_VERSION = 1;
|
|
10
|
+
const STORE_NAME = 'packages';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Open the IndexedDB database
|
|
14
|
+
*/
|
|
15
|
+
function openDatabase() {
|
|
16
|
+
return new Promise((resolve, reject) => {
|
|
17
|
+
if (typeof indexedDB === 'undefined') {
|
|
18
|
+
reject(new Error('IndexedDB not available'));
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const request = indexedDB.open(DB_NAME, DB_VERSION);
|
|
23
|
+
|
|
24
|
+
request.onerror = () => reject(request.error);
|
|
25
|
+
request.onsuccess = () => resolve(request.result);
|
|
26
|
+
|
|
27
|
+
request.onupgradeneeded = (event) => {
|
|
28
|
+
const db = event.target.result;
|
|
29
|
+
|
|
30
|
+
if (!db.objectStoreNames.contains(STORE_NAME)) {
|
|
31
|
+
const store = db.createObjectStore(STORE_NAME, { keyPath: 'key' });
|
|
32
|
+
store.createIndex('name', 'name', { unique: false });
|
|
33
|
+
store.createIndex('version', 'version', { unique: false });
|
|
34
|
+
store.createIndex('timestamp', 'timestamp', { unique: false });
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Browser Package Cache
|
|
42
|
+
*/
|
|
43
|
+
export class BrowserPackageCache {
|
|
44
|
+
constructor() {
|
|
45
|
+
this.db = null;
|
|
46
|
+
this.memoryCache = new Map(); // In-memory fallback
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Initialize the cache
|
|
51
|
+
*/
|
|
52
|
+
async init() {
|
|
53
|
+
try {
|
|
54
|
+
this.db = await openDatabase();
|
|
55
|
+
} catch (e) {
|
|
56
|
+
// Fall back to memory cache
|
|
57
|
+
console.warn('IndexedDB not available, using memory cache');
|
|
58
|
+
this.db = null;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Generate cache key for a package
|
|
64
|
+
*/
|
|
65
|
+
getKey(name, version) {
|
|
66
|
+
return `${name}@${version}`;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Check if a package is cached
|
|
71
|
+
*/
|
|
72
|
+
async has(name, version) {
|
|
73
|
+
const key = this.getKey(name, version);
|
|
74
|
+
|
|
75
|
+
if (!this.db) {
|
|
76
|
+
return this.memoryCache.has(key);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return new Promise((resolve, reject) => {
|
|
80
|
+
const tx = this.db.transaction(STORE_NAME, 'readonly');
|
|
81
|
+
const store = tx.objectStore(STORE_NAME);
|
|
82
|
+
const request = store.get(key);
|
|
83
|
+
|
|
84
|
+
request.onerror = () => reject(request.error);
|
|
85
|
+
request.onsuccess = () => resolve(request.result !== undefined);
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Get a cached package
|
|
91
|
+
*/
|
|
92
|
+
async get(name, version) {
|
|
93
|
+
const key = this.getKey(name, version);
|
|
94
|
+
|
|
95
|
+
if (!this.db) {
|
|
96
|
+
return this.memoryCache.get(key) || null;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return new Promise((resolve, reject) => {
|
|
100
|
+
const tx = this.db.transaction(STORE_NAME, 'readonly');
|
|
101
|
+
const store = tx.objectStore(STORE_NAME);
|
|
102
|
+
const request = store.get(key);
|
|
103
|
+
|
|
104
|
+
request.onerror = () => reject(request.error);
|
|
105
|
+
request.onsuccess = () => resolve(request.result || null);
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Cache a package
|
|
111
|
+
*/
|
|
112
|
+
async set(name, version, data) {
|
|
113
|
+
const key = this.getKey(name, version);
|
|
114
|
+
|
|
115
|
+
const entry = {
|
|
116
|
+
key,
|
|
117
|
+
name,
|
|
118
|
+
version,
|
|
119
|
+
data,
|
|
120
|
+
timestamp: Date.now(),
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
if (!this.db) {
|
|
124
|
+
this.memoryCache.set(key, entry);
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return new Promise((resolve, reject) => {
|
|
129
|
+
const tx = this.db.transaction(STORE_NAME, 'readwrite');
|
|
130
|
+
const store = tx.objectStore(STORE_NAME);
|
|
131
|
+
const request = store.put(entry);
|
|
132
|
+
|
|
133
|
+
request.onerror = () => reject(request.error);
|
|
134
|
+
request.onsuccess = () => resolve();
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Remove a package from cache
|
|
140
|
+
*/
|
|
141
|
+
async delete(name, version) {
|
|
142
|
+
const key = this.getKey(name, version);
|
|
143
|
+
|
|
144
|
+
if (!this.db) {
|
|
145
|
+
this.memoryCache.delete(key);
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
return new Promise((resolve, reject) => {
|
|
150
|
+
const tx = this.db.transaction(STORE_NAME, 'readwrite');
|
|
151
|
+
const store = tx.objectStore(STORE_NAME);
|
|
152
|
+
const request = store.delete(key);
|
|
153
|
+
|
|
154
|
+
request.onerror = () => reject(request.error);
|
|
155
|
+
request.onsuccess = () => resolve();
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Clear all cached packages
|
|
161
|
+
*/
|
|
162
|
+
async clear() {
|
|
163
|
+
if (!this.db) {
|
|
164
|
+
this.memoryCache.clear();
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
return new Promise((resolve, reject) => {
|
|
169
|
+
const tx = this.db.transaction(STORE_NAME, 'readwrite');
|
|
170
|
+
const store = tx.objectStore(STORE_NAME);
|
|
171
|
+
const request = store.clear();
|
|
172
|
+
|
|
173
|
+
request.onerror = () => reject(request.error);
|
|
174
|
+
request.onsuccess = () => resolve();
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* List all cached packages
|
|
180
|
+
*/
|
|
181
|
+
async list() {
|
|
182
|
+
if (!this.db) {
|
|
183
|
+
return Array.from(this.memoryCache.values()).map(e => ({
|
|
184
|
+
name: e.name,
|
|
185
|
+
version: e.version,
|
|
186
|
+
timestamp: e.timestamp,
|
|
187
|
+
}));
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
return new Promise((resolve, reject) => {
|
|
191
|
+
const tx = this.db.transaction(STORE_NAME, 'readonly');
|
|
192
|
+
const store = tx.objectStore(STORE_NAME);
|
|
193
|
+
const request = store.getAll();
|
|
194
|
+
|
|
195
|
+
request.onerror = () => reject(request.error);
|
|
196
|
+
request.onsuccess = () => {
|
|
197
|
+
const entries = request.result || [];
|
|
198
|
+
resolve(entries.map(e => ({
|
|
199
|
+
name: e.name,
|
|
200
|
+
version: e.version,
|
|
201
|
+
timestamp: e.timestamp,
|
|
202
|
+
})));
|
|
203
|
+
};
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Get cache size in bytes (approximate)
|
|
209
|
+
*/
|
|
210
|
+
async getSize() {
|
|
211
|
+
const packages = await this.list();
|
|
212
|
+
let size = 0;
|
|
213
|
+
|
|
214
|
+
for (const pkg of packages) {
|
|
215
|
+
const entry = await this.get(pkg.name, pkg.version);
|
|
216
|
+
if (entry && entry.data) {
|
|
217
|
+
size += JSON.stringify(entry.data).length;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
return size;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Web Package Manager
|
|
227
|
+
*
|
|
228
|
+
* Manages package resolution and caching for web/Riva environment.
|
|
229
|
+
*/
|
|
230
|
+
export class WebPackageManager {
|
|
231
|
+
constructor(options = {}) {
|
|
232
|
+
this.cache = new BrowserPackageCache();
|
|
233
|
+
this.fetcher = options.fetcher || null;
|
|
234
|
+
|
|
235
|
+
// User's global dependencies (from user account)
|
|
236
|
+
this.globalDeps = options.globalDeps || {};
|
|
237
|
+
|
|
238
|
+
// Virtual file system for resolved packages
|
|
239
|
+
this.virtualFS = new Map();
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Initialize the package manager
|
|
244
|
+
*/
|
|
245
|
+
async init() {
|
|
246
|
+
await this.cache.init();
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* Resolve and load all dependencies for a workspace
|
|
251
|
+
*/
|
|
252
|
+
async loadDependencies(workspaceDeps = {}) {
|
|
253
|
+
// Merge global and workspace dependencies
|
|
254
|
+
const allDeps = { ...this.globalDeps, ...workspaceDeps };
|
|
255
|
+
|
|
256
|
+
const loaded = [];
|
|
257
|
+
const errors = [];
|
|
258
|
+
|
|
259
|
+
for (const [name, spec] of Object.entries(allDeps)) {
|
|
260
|
+
try {
|
|
261
|
+
await this.loadPackage(name, spec);
|
|
262
|
+
loaded.push(name);
|
|
263
|
+
} catch (e) {
|
|
264
|
+
errors.push({ name, error: e.message });
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
return { loaded, errors };
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Load a single package
|
|
273
|
+
*/
|
|
274
|
+
async loadPackage(name, spec) {
|
|
275
|
+
const version = typeof spec === 'string' ? spec : spec.version;
|
|
276
|
+
const source = typeof spec === 'object' ? spec.source : null;
|
|
277
|
+
|
|
278
|
+
// Check cache first
|
|
279
|
+
const cached = await this.cache.get(name, version);
|
|
280
|
+
if (cached) {
|
|
281
|
+
this.addToVirtualFS(name, cached.data);
|
|
282
|
+
return cached.data;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// Fetch package
|
|
286
|
+
if (!this.fetcher) {
|
|
287
|
+
throw new Error(`No fetcher configured for package: ${name}`);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
const pkgData = await this.fetcher.fetchPackage(name, version, source);
|
|
291
|
+
|
|
292
|
+
// Cache it
|
|
293
|
+
await this.cache.set(name, version, pkgData);
|
|
294
|
+
|
|
295
|
+
// Add to virtual FS
|
|
296
|
+
this.addToVirtualFS(name, pkgData);
|
|
297
|
+
|
|
298
|
+
return pkgData;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* Add package to virtual file system
|
|
303
|
+
*/
|
|
304
|
+
addToVirtualFS(name, pkgData) {
|
|
305
|
+
const basePath = `stone_modules/${name}`;
|
|
306
|
+
|
|
307
|
+
if (pkgData.files) {
|
|
308
|
+
for (const [filename, content] of pkgData.files) {
|
|
309
|
+
this.virtualFS.set(`${basePath}/${filename}`, content);
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
if (pkgData.manifest) {
|
|
314
|
+
this.virtualFS.set(`${basePath}/package.son`, JSON.stringify(pkgData.manifest));
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* Get a file from virtual FS
|
|
320
|
+
*/
|
|
321
|
+
getFile(path) {
|
|
322
|
+
return this.virtualFS.get(path) || null;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/**
|
|
326
|
+
* Check if file exists in virtual FS
|
|
327
|
+
*/
|
|
328
|
+
hasFile(path) {
|
|
329
|
+
return this.virtualFS.has(path);
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* Clear cache (call on logout)
|
|
334
|
+
*/
|
|
335
|
+
async clearCache() {
|
|
336
|
+
await this.cache.clear();
|
|
337
|
+
this.virtualFS.clear();
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
/**
|
|
341
|
+
* Set global dependencies (from user account)
|
|
342
|
+
*/
|
|
343
|
+
setGlobalDeps(deps) {
|
|
344
|
+
this.globalDeps = deps || {};
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
/**
|
|
349
|
+
* Create a web package manager
|
|
350
|
+
*/
|
|
351
|
+
export function createWebPackageManager(options = {}) {
|
|
352
|
+
return new WebPackageManager(options);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
export default {
|
|
356
|
+
BrowserPackageCache,
|
|
357
|
+
WebPackageManager,
|
|
358
|
+
createWebPackageManager,
|
|
359
|
+
};
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stone Package Fetcher
|
|
3
|
+
*
|
|
4
|
+
* Fetches packages from HTTP sources and installs them locally.
|
|
5
|
+
* Handles downloading, extracting, and verification.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { parseSON } from '../frontend/parsing/sonParser.js';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Fetch error
|
|
12
|
+
*/
|
|
13
|
+
export class FetchError extends Error {
|
|
14
|
+
constructor(message, url = null, statusCode = null) {
|
|
15
|
+
super(message);
|
|
16
|
+
this.name = 'FetchError';
|
|
17
|
+
this.url = url;
|
|
18
|
+
this.statusCode = statusCode;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Package Fetcher - Abstract base for different environments
|
|
24
|
+
*/
|
|
25
|
+
export class PackageFetcher {
|
|
26
|
+
constructor(options = {}) {
|
|
27
|
+
// Base URL for package registry (if any)
|
|
28
|
+
this.registryUrl = options.registryUrl || null;
|
|
29
|
+
|
|
30
|
+
// Cache of fetched package info
|
|
31
|
+
this.cache = new Map();
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Fetch package versions list from source
|
|
36
|
+
* @param {string} name - Package name
|
|
37
|
+
* @param {string} source - Source URL
|
|
38
|
+
* @returns {Promise<{versions: string[], source: string}>}
|
|
39
|
+
*/
|
|
40
|
+
async fetchVersions(name, source) {
|
|
41
|
+
// Try to get versions.son from the source
|
|
42
|
+
const versionsUrl = this.resolveUrl(source || this.registryUrl, name, 'versions.son');
|
|
43
|
+
|
|
44
|
+
try {
|
|
45
|
+
const content = await this.fetchText(versionsUrl);
|
|
46
|
+
const data = parseSON(content, versionsUrl);
|
|
47
|
+
|
|
48
|
+
return {
|
|
49
|
+
versions: data.versions || [],
|
|
50
|
+
source: source || this.registryUrl,
|
|
51
|
+
};
|
|
52
|
+
} catch (e) {
|
|
53
|
+
// If versions.son doesn't exist, return empty
|
|
54
|
+
return { versions: [], source };
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Fetch a specific package version
|
|
60
|
+
* @param {string} name - Package name
|
|
61
|
+
* @param {string} version - Version to fetch
|
|
62
|
+
* @param {string} source - Source URL
|
|
63
|
+
* @returns {Promise<{files: Map<string, string>, manifest: object}>}
|
|
64
|
+
*/
|
|
65
|
+
async fetchPackage(name, version, source) {
|
|
66
|
+
const packageUrl = this.resolveUrl(source, name, version);
|
|
67
|
+
|
|
68
|
+
// Fetch the package manifest first
|
|
69
|
+
const manifestUrl = `${packageUrl}/package.son`;
|
|
70
|
+
const manifestContent = await this.fetchText(manifestUrl);
|
|
71
|
+
const manifest = parseSON(manifestContent, manifestUrl);
|
|
72
|
+
|
|
73
|
+
// Fetch the file list
|
|
74
|
+
const filesUrl = `${packageUrl}/files.son`;
|
|
75
|
+
let fileList = [];
|
|
76
|
+
|
|
77
|
+
try {
|
|
78
|
+
const filesContent = await this.fetchText(filesUrl);
|
|
79
|
+
const filesData = parseSON(filesContent, filesUrl);
|
|
80
|
+
fileList = filesData.files || [];
|
|
81
|
+
} catch (e) {
|
|
82
|
+
// No files.son, try common files
|
|
83
|
+
fileList = ['main.stn'];
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Fetch each file
|
|
87
|
+
const files = new Map();
|
|
88
|
+
for (const file of fileList) {
|
|
89
|
+
const fileUrl = `${packageUrl}/${file}`;
|
|
90
|
+
try {
|
|
91
|
+
const content = await this.fetchText(fileUrl);
|
|
92
|
+
files.set(file, content);
|
|
93
|
+
} catch (e) {
|
|
94
|
+
// Skip missing files
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return { files, manifest };
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Resolve a URL for a package resource
|
|
103
|
+
*/
|
|
104
|
+
resolveUrl(base, name, resource) {
|
|
105
|
+
if (!base) {
|
|
106
|
+
throw new FetchError(`No source URL specified for package: ${name}`);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Ensure base doesn't end with /
|
|
110
|
+
base = base.replace(/\/+$/, '');
|
|
111
|
+
|
|
112
|
+
return `${base}/${name}/${resource}`;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Fetch text content from a URL (to be implemented by subclasses)
|
|
117
|
+
*/
|
|
118
|
+
async fetchText(url) {
|
|
119
|
+
throw new Error('fetchText must be implemented by subclass');
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Compute integrity hash for content
|
|
124
|
+
*/
|
|
125
|
+
async computeHash(content) {
|
|
126
|
+
// Use SubtleCrypto if available (browser/modern Node)
|
|
127
|
+
if (typeof crypto !== 'undefined' && crypto.subtle) {
|
|
128
|
+
const encoder = new TextEncoder();
|
|
129
|
+
const data = encoder.encode(content);
|
|
130
|
+
const hashBuffer = await crypto.subtle.digest('SHA-256', data);
|
|
131
|
+
const hashArray = Array.from(new Uint8Array(hashBuffer));
|
|
132
|
+
return 'sha256-' + hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Fallback: no hashing
|
|
136
|
+
return null;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Verify integrity hash
|
|
141
|
+
*/
|
|
142
|
+
async verifyIntegrity(content, expectedHash) {
|
|
143
|
+
if (!expectedHash) return true;
|
|
144
|
+
|
|
145
|
+
const actualHash = await this.computeHash(content);
|
|
146
|
+
if (!actualHash) return true; // Can't verify, skip
|
|
147
|
+
|
|
148
|
+
return actualHash === expectedHash;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Node.js Package Fetcher - Uses native fetch or http module
|
|
154
|
+
*/
|
|
155
|
+
export class NodePackageFetcher extends PackageFetcher {
|
|
156
|
+
async fetchText(url) {
|
|
157
|
+
// Use native fetch if available (Node 18+)
|
|
158
|
+
if (typeof fetch === 'function') {
|
|
159
|
+
const response = await fetch(url);
|
|
160
|
+
|
|
161
|
+
if (!response.ok) {
|
|
162
|
+
throw new FetchError(
|
|
163
|
+
`Failed to fetch ${url}: ${response.status} ${response.statusText}`,
|
|
164
|
+
url,
|
|
165
|
+
response.status
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
return await response.text();
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// Fallback to Node's http/https modules
|
|
173
|
+
return new Promise((resolve, reject) => {
|
|
174
|
+
const protocol = url.startsWith('https') ? require('https') : require('http');
|
|
175
|
+
|
|
176
|
+
protocol.get(url, (res) => {
|
|
177
|
+
if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
|
|
178
|
+
// Follow redirect
|
|
179
|
+
this.fetchText(res.headers.location).then(resolve).catch(reject);
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
if (res.statusCode !== 200) {
|
|
184
|
+
reject(new FetchError(
|
|
185
|
+
`Failed to fetch ${url}: ${res.statusCode}`,
|
|
186
|
+
url,
|
|
187
|
+
res.statusCode
|
|
188
|
+
));
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
let data = '';
|
|
193
|
+
res.on('data', chunk => data += chunk);
|
|
194
|
+
res.on('end', () => resolve(data));
|
|
195
|
+
res.on('error', reject);
|
|
196
|
+
}).on('error', reject);
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Browser Package Fetcher - Uses fetch API
|
|
203
|
+
*/
|
|
204
|
+
export class BrowserPackageFetcher extends PackageFetcher {
|
|
205
|
+
async fetchText(url) {
|
|
206
|
+
const response = await fetch(url);
|
|
207
|
+
|
|
208
|
+
if (!response.ok) {
|
|
209
|
+
throw new FetchError(
|
|
210
|
+
`Failed to fetch ${url}: ${response.status} ${response.statusText}`,
|
|
211
|
+
url,
|
|
212
|
+
response.status
|
|
213
|
+
);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
return await response.text();
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Create the appropriate fetcher for the current environment
|
|
222
|
+
*/
|
|
223
|
+
export function createFetcher(options = {}) {
|
|
224
|
+
if (typeof window !== 'undefined') {
|
|
225
|
+
return new BrowserPackageFetcher(options);
|
|
226
|
+
}
|
|
227
|
+
return new NodePackageFetcher(options);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
export default {
|
|
231
|
+
PackageFetcher,
|
|
232
|
+
NodePackageFetcher,
|
|
233
|
+
BrowserPackageFetcher,
|
|
234
|
+
FetchError,
|
|
235
|
+
createFetcher,
|
|
236
|
+
};
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stone Package System
|
|
3
|
+
*
|
|
4
|
+
* Main entry point for the Stone package management system.
|
|
5
|
+
* Exports all package-related functionality.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
// SON Parser
|
|
9
|
+
export { parseSON, stringifySON, SONParseError } from '../frontend/parsing/sonParser.js';
|
|
10
|
+
|
|
11
|
+
// Semver
|
|
12
|
+
export {
|
|
13
|
+
SemVer,
|
|
14
|
+
SemVerError,
|
|
15
|
+
VersionRange,
|
|
16
|
+
parseVersion,
|
|
17
|
+
parseRange,
|
|
18
|
+
satisfies,
|
|
19
|
+
compare,
|
|
20
|
+
gt,
|
|
21
|
+
gte,
|
|
22
|
+
lt,
|
|
23
|
+
lte,
|
|
24
|
+
eq,
|
|
25
|
+
sort,
|
|
26
|
+
rsort,
|
|
27
|
+
maxSatisfying,
|
|
28
|
+
minSatisfying,
|
|
29
|
+
valid,
|
|
30
|
+
coerce,
|
|
31
|
+
} from './semver.js';
|
|
32
|
+
|
|
33
|
+
// Package Manifest
|
|
34
|
+
export {
|
|
35
|
+
PackageManifest,
|
|
36
|
+
ManifestError,
|
|
37
|
+
parseManifest,
|
|
38
|
+
createManifest,
|
|
39
|
+
stringifyManifest,
|
|
40
|
+
} from './manifest.js';
|
|
41
|
+
|
|
42
|
+
// Dependency Resolver
|
|
43
|
+
export {
|
|
44
|
+
DependencyResolver,
|
|
45
|
+
SimpleResolver,
|
|
46
|
+
ResolutionError,
|
|
47
|
+
checkCompatibility,
|
|
48
|
+
} from './resolver.js';
|
|
49
|
+
|
|
50
|
+
// Package Fetcher
|
|
51
|
+
export {
|
|
52
|
+
PackageFetcher,
|
|
53
|
+
NodePackageFetcher,
|
|
54
|
+
BrowserPackageFetcher,
|
|
55
|
+
FetchError,
|
|
56
|
+
createFetcher,
|
|
57
|
+
} from './fetcher.js';
|
|
58
|
+
|
|
59
|
+
// Lock File
|
|
60
|
+
export {
|
|
61
|
+
LockFile,
|
|
62
|
+
LockedPackage,
|
|
63
|
+
LockFileError,
|
|
64
|
+
parseLockFile,
|
|
65
|
+
stringifyLockFile,
|
|
66
|
+
checkLockFileStatus,
|
|
67
|
+
} from './lockfile.js';
|
|
68
|
+
|
|
69
|
+
// Package Resolution
|
|
70
|
+
export {
|
|
71
|
+
PackageContext,
|
|
72
|
+
createNodePackageContext,
|
|
73
|
+
createWebPackageContext,
|
|
74
|
+
getStoneHome,
|
|
75
|
+
} from './packageResolver.js';
|
|
76
|
+
|
|
77
|
+
// Browser Cache (web only)
|
|
78
|
+
export {
|
|
79
|
+
BrowserPackageCache,
|
|
80
|
+
WebPackageManager,
|
|
81
|
+
createWebPackageManager,
|
|
82
|
+
} from './browserCache.js';
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Create a package manager instance with all components wired together
|
|
86
|
+
*/
|
|
87
|
+
export function createPackageManager(options = {}) {
|
|
88
|
+
const fetcher = options.fetcher || (typeof window !== 'undefined'
|
|
89
|
+
? new (require('./fetcher.js').BrowserPackageFetcher)(options)
|
|
90
|
+
: new (require('./fetcher.js').NodePackageFetcher)(options));
|
|
91
|
+
|
|
92
|
+
const resolver = new (require('./resolver.js').DependencyResolver)({
|
|
93
|
+
fetchVersions: (name, source) => fetcher.fetchVersions(name, source),
|
|
94
|
+
...options,
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
return {
|
|
98
|
+
fetcher,
|
|
99
|
+
resolver,
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Resolve dependencies from a manifest
|
|
103
|
+
*/
|
|
104
|
+
async resolve(manifest) {
|
|
105
|
+
return resolver.resolve(manifest);
|
|
106
|
+
},
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Fetch and install a package
|
|
110
|
+
*/
|
|
111
|
+
async fetchPackage(name, version, source) {
|
|
112
|
+
return fetcher.fetchPackage(name, version, source);
|
|
113
|
+
},
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Parse a package.son file
|
|
117
|
+
*/
|
|
118
|
+
parseManifest: require('./manifest.js').parseManifest,
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Parse a lock file
|
|
122
|
+
*/
|
|
123
|
+
parseLockFile: require('./lockfile.js').parseLockFile,
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export default {
|
|
128
|
+
// Re-export everything for convenient default import
|
|
129
|
+
createPackageManager,
|
|
130
|
+
};
|