Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions components/Scope.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { type Logger } from '../utility/logging/logger.ts';
import { loggerWithTag } from '../utility/logging/harper_logger.ts';
import { EventEmitter, once } from 'node:events';
import { databaseEventsEmitter } from '../resources/databases.ts';
import { databaseEventsEmitter, table } from '../resources/databases.ts';
import { server, type Server } from '../server/Server.ts';
import { EntryHandler, type EntryHandlerEventMap, type onEntryEventHandler } from './EntryHandler.ts';
import { OptionsWatcher, OptionsWatcherEventMap } from './OptionsWatcher.ts';
Expand Down Expand Up @@ -39,6 +39,7 @@ export class Scope extends EventEmitter<ScopeEventsMap> {
#directory: string;
#appName: string;
#pluginName: string;
#origin: string;
#entryHandler?: EntryHandler;
#entryHandlers: EntryHandler[];
#logger: Logger;
Expand All @@ -57,12 +58,14 @@ export class Scope extends EventEmitter<ScopeEventsMap> {
pluginName: string,
directory: string,
configFilePath: string,
applicationScope: ApplicationScope
applicationScope: ApplicationScope,
origin: string = appName
) {
super();

this.#appName = appName;
this.#pluginName = pluginName;
this.#origin = typeof origin === 'string' ? origin : appName;
this.#directory = directory;
this.#configFilePath = configFilePath;
this.#logger = loggerWithTag(this.#appName);
Expand Down Expand Up @@ -128,6 +131,11 @@ export class Scope extends EventEmitter<ScopeEventsMap> {
return this.#configFilePath;
}

ensureTable<TableResourceType = unknown>(options: any): TableResourceType {
options.origin = this.#origin;
return table<TableResourceType>(options);
}

#handleOptionsWatcherReady(): void {
// This previously created the default entry handler immediately, but now we wait for the user to call `handleEntry`
// The issue was that since the component loader was awaiting `scope.ready()` and then calling `pluginModule.handleApplication(scope)`,
Expand Down
9 changes: 8 additions & 1 deletion components/componentLoader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -444,7 +444,14 @@ export async function loadComponent(

// New Plugin API (`handleApplication`)
if (resources.isWorker && extensionModule.handleApplication) {
const scope = new Scope(appName || 'harper', componentName, componentDirectory, configPath, applicationScope);
const scope = new Scope(
appName || 'harper',
componentName,
componentDirectory,
configPath,
applicationScope,
origin
);

onMessageByType(ITC_EVENT_TYPES.SHUTDOWN, () => scope.close());

Expand Down
49 changes: 49 additions & 0 deletions unitTests/components/componentLoader.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,55 @@ describe('ComponentLoader Status Integration', function () {
assert.match(loadedCalls[0].args[1], /loaded successfully/);
});

it('should expose ensureTable on handleApplication scope with component origin', async function () {
const componentDirName = 'scope-ensure-table-component';
const componentDir = path.join(tempDir, componentDirName);
const pluginName = 'scopeEnsureTablePlugin';
const origin = 'test-origin';
let capturedScope;

mkdirSync(componentDir);
writeFileSync(path.join(componentDir, 'harperdb-config.yaml'), `${pluginName}: {}`);

componentLoader.TRUSTED_RESOURCE_PLUGINS[pluginName] = {
handleApplication(scope) {
capturedScope = scope;
},
};

try {
await componentLoader.loadComponent(
componentDir,
{
isWorker: true,
set: sinon.stub(),
},
origin
);
} finally {
delete componentLoader.TRUSTED_RESOURCE_PLUGINS[pluginName];
}

assert.equal(typeof capturedScope?.ensureTable, 'function', 'scope.ensureTable should be exposed');

let assignedOrigin;
const options = new Proxy(
{},
{
set(target, property, value) {
if (property === 'origin') {
assignedOrigin = value;
throw new Error('stop before table call');
}
return Reflect.set(target, property, value);
},
}
);

assert.throws(() => capturedScope.ensureTable(options), /stop before table call/);
assert.equal(assignedOrigin, origin, 'scope.ensureTable should preserve the component origin');
});

// TODO: Does the plugin API have an equivalent mechanism?
it.skip('should mark component as failed when it loads no functionality', async function () {
// Create a component directory without config
Expand Down
Loading