mirror of
https://github.com/HeyPuter/puter.git
synced 2026-07-08 16:21:40 +00:00
fix: import in extensions (#3276)
This commit is contained in:
Generated
-12
@@ -1514,18 +1514,6 @@
|
||||
"sisteransi": "^1.0.5"
|
||||
}
|
||||
},
|
||||
"node_modules/@clack/prompts/node_modules/is-unicode-supported": {
|
||||
"version": "1.3.0",
|
||||
"extraneous": true,
|
||||
"inBundle": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/@cloudflare/kv-asset-handler": {
|
||||
"version": "0.3.4",
|
||||
"resolved": "https://registry.npmjs.org/@cloudflare/kv-asset-handler/-/kv-asset-handler-0.3.4.tgz",
|
||||
|
||||
@@ -46,14 +46,6 @@ import {
|
||||
} from './legacyFsHelpers.js';
|
||||
import { RouteOptions } from '../../core/http/index.js';
|
||||
|
||||
/**
|
||||
* Legacy FS routes, implemented as thin shims over `FSService`.
|
||||
*
|
||||
* Each shim parses the request shape (FSNodeParam-style `{ path, uid, id }`
|
||||
* or `{ parent, name }`), invokes the service method, and returns the
|
||||
* snake_case response clients expect.
|
||||
*/
|
||||
|
||||
type RouterCache = Map<string, RequestHandler | null>;
|
||||
|
||||
const additionalRoutePaths: Record<string, string> = {};
|
||||
@@ -1631,17 +1623,6 @@ export class LegacyFSController extends PuterController {
|
||||
download.body.pipe(res);
|
||||
};
|
||||
|
||||
// Helpers for writeFile
|
||||
// -- GUI event emission -------------------------------------------
|
||||
//
|
||||
// Fire-and-forget `outer.gui.item.*` events so SocketService,
|
||||
// BroadcastService, WorkerDriver (hot-reload), and cache-invalidation
|
||||
// listeners pick up mutations made through the legacy (bare-path) routes.
|
||||
// FSController (v2-native /fs/* routes) emits these from its own handlers;
|
||||
// LegacyFSController delegates to the same FSService but needs its
|
||||
// own emissions because the service layer deliberately doesn't emit GUI
|
||||
// events (that's a controller concern).
|
||||
|
||||
async #emitGuiEvent(
|
||||
eventName:
|
||||
| 'outer.gui.item.added'
|
||||
|
||||
@@ -78,6 +78,12 @@ describe('extension.import("client") optional client access', () => {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
expect(probe()).toBe(fake);
|
||||
// The import proxy method-binds, so the result is a binding proxy over
|
||||
// `fake` rather than the raw reference (identity is intentionally not
|
||||
// preserved). What the probe pattern locks is that a registered client
|
||||
// surfaces a callable method.
|
||||
const result = probe();
|
||||
expect(result).not.toBeNull();
|
||||
expect(typeof (result as { query: unknown }).query).toBe('function');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -201,6 +201,35 @@ const makeUseFn = (): ExtensionUseFn => {
|
||||
* `'store:baz'` / `'controller:qux'` / `'driver:fred'` — returns a lazy
|
||||
* proxy to the registered instance (thrown on use-before-init).
|
||||
*/
|
||||
/**
|
||||
* Wrap a resolved layer instance so that pulling a method off the import
|
||||
* comes out *bound* to the instance. Extensions routinely grab a method as a
|
||||
* bare reference — `const { write } = extension.import('service').fs` or
|
||||
* `const w = svc.fs.write` — then call it detached; without binding, `this`
|
||||
* is `undefined` and the method's private-field access throws on the first
|
||||
* line. Getters keep the real instance as their receiver, so private-field
|
||||
* reads inside accessors still resolve. Only `get` is trapped; writes, `in`,
|
||||
* and descriptor reads fall through to the instance unchanged.
|
||||
*
|
||||
* Trade-off: each method access returns a fresh bound function, so reference
|
||||
* identity is not stable (`svc.fs.write !== svc.fs.write`). That's acceptable
|
||||
* for the import surface, where instances are grabbed once and methods called.
|
||||
*/
|
||||
const bindLayerMethods = <T>(instance: T): T => {
|
||||
if (instance === null || typeof instance !== 'object') {
|
||||
return instance;
|
||||
}
|
||||
return new Proxy(instance as object, {
|
||||
get(target, prop) {
|
||||
const value = Reflect.get(target, prop, target);
|
||||
return typeof value === 'function'
|
||||
? // eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
(value as (...a: any[]) => unknown).bind(target)
|
||||
: value;
|
||||
},
|
||||
}) as T;
|
||||
};
|
||||
|
||||
export const extension = {
|
||||
// -- Config access -----------------------------------------------
|
||||
//
|
||||
@@ -324,7 +353,7 @@ export const extension = {
|
||||
};
|
||||
return new Proxy({}, proxyProxyHandler) as object;
|
||||
}
|
||||
return proxiedObj;
|
||||
return bindLayerMethods(proxiedObj);
|
||||
},
|
||||
};
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
@@ -352,7 +381,7 @@ export const extension = {
|
||||
};
|
||||
return new Proxy({}, proxyProxyHandler) as object;
|
||||
}
|
||||
return proxiedObj;
|
||||
return bindLayerMethods(proxiedObj);
|
||||
},
|
||||
};
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
@@ -380,7 +409,7 @@ export const extension = {
|
||||
};
|
||||
return new Proxy({}, proxyProxyHandler) as object;
|
||||
}
|
||||
return proxiedObj;
|
||||
return bindLayerMethods(proxiedObj);
|
||||
},
|
||||
};
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
@@ -408,7 +437,7 @@ export const extension = {
|
||||
};
|
||||
return new Proxy({}, proxyProxyHandler) as object;
|
||||
}
|
||||
return proxiedObj;
|
||||
return bindLayerMethods(proxiedObj);
|
||||
},
|
||||
};
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
@@ -436,7 +465,7 @@ export const extension = {
|
||||
};
|
||||
return new Proxy({}, proxyProxyHandler) as object;
|
||||
}
|
||||
return proxiedObj;
|
||||
return bindLayerMethods(proxiedObj);
|
||||
},
|
||||
};
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
|
||||
Reference in New Issue
Block a user