Skip to content
Merged
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
4 changes: 4 additions & 0 deletions packages/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,10 @@ export interface ICreatable<C extends AnyBrandedCell<any>> {
*/
export interface IResolvable<T, C extends AnyBrandedCell<T>> {
resolveAsCell(): C;
getArgumentCell<S extends JSONSchema = JSONSchema>(
schema?: S,
): Cell<Schema<S>> | undefined;
getArgumentCell<U>(): Cell<U> | undefined;
}

/**
Expand Down
6 changes: 3 additions & 3 deletions packages/background-charm-service/cast-admin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { CharmManager, compileRecipe } from "@commontools/charm";
import { Runtime } from "@commontools/runner";
import { StorageManager } from "@commontools/runner/storage/cache.deno";
import { type DID } from "@commontools/identity";
import { createAdminSession } from "@commontools/identity";
import { createSessionFromDid } from "@commontools/identity";
import {
BG_CELL_CAUSE,
BG_SYSTEM_SPACE_ID,
Expand Down Expand Up @@ -87,9 +87,9 @@ async function castRecipe() {
console.log("Casting recipe...");

// Create session and charm manager (matching main.ts pattern)
const session = await createAdminSession({
const session = await createSessionFromDid({
identity,
name: "recipe-caster",
spaceName: "recipe-caster",
space: spaceId as DID,
});

Expand Down
6 changes: 3 additions & 3 deletions packages/background-charm-service/src/worker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {
import { StorageManager } from "@commontools/runner/storage/cache.deno";

import {
createAdminSession,
createSessionFromDid,
type DID,
Identity,
Session,
Expand Down Expand Up @@ -93,9 +93,9 @@ async function initialize(

// Initialize session
spaceId = did as DID;
currentSession = await createAdminSession({
currentSession = await createSessionFromDid({
identity,
name: "~background-service-worker",
spaceName: "~background-service-worker",
space: spaceId,
});

Expand Down
2 changes: 1 addition & 1 deletion packages/charm/src/manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@ export class CharmManager {
}

getSpaceName(): string {
return this.session.name;
return this.session.spaceName;
}

async synced(): Promise<void> {
Expand Down
14 changes: 2 additions & 12 deletions packages/charm/src/ops/charms-controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { StorageManager } from "@commontools/runner/storage/cache";
import { CharmManager } from "../index.ts";
import { CharmController } from "./charm-controller.ts";
import { compileProgram } from "./utils.ts";
import { ANYONE, Identity } from "@commontools/identity";
import { createSession, Identity } from "@commontools/identity";

export interface CreateCharmOptions {
input?: object;
Expand Down Expand Up @@ -114,17 +114,7 @@ export class CharmsController<T = unknown> {
identity: Identity;
spaceName: string;
}): Promise<CharmsController> {
const account = spaceName.startsWith("~")
? identity
: await Identity.fromPassphrase(ANYONE);
const user = await account.derive(spaceName);
const session = {
private: account.did() === identity.did(),
name: spaceName,
space: user.did(),
as: user,
};

const session = await createSession({ identity, spaceName });
const runtime = new Runtime({
apiUrl: new URL(apiUrl),
storageManager: StorageManager.open({
Expand Down
15 changes: 3 additions & 12 deletions packages/cli/lib/charm.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ANYONE, Identity, Session } from "@commontools/identity";
import { createSession, Session } from "@commontools/identity";
import { ensureDir } from "@std/fs";
import { loadIdentity } from "./identity.ts";
import {
Expand Down Expand Up @@ -35,17 +35,8 @@ async function makeSession(config: SpaceConfig): Promise<Session> {
if (config.space.startsWith("did:key")) {
throw new Error("DID key spaces not yet supported.");
}
const root = await loadIdentity(config.identity);
const account = config.space.startsWith("~")
? root
: await Identity.fromPassphrase(ANYONE);
const user = await account.derive(config.space);
return {
private: account.did() === root.did(),
name: config.space,
space: user.did(),
as: user,
};
const identity = await loadIdentity(config.identity);
return createSession({ identity, spaceName: config.space });
}

export async function loadManager(config: SpaceConfig): Promise<CharmManager> {
Expand Down
2 changes: 1 addition & 1 deletion packages/identity/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export { KeyStore } from "./key-store.ts";
export * from "./interface.ts";
export {
ANYONE,
createAdminSession,
createSession,
createSessionFromDid,
type Session,
} from "./session.ts";
36 changes: 20 additions & 16 deletions packages/identity/src/session.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,37 +3,41 @@ import { type DID } from "./interface.ts";
export const ANYONE = "common user";

export type Session = {
private: boolean;
name: string;
isPrivate: boolean;
spaceName: string;
space: DID;
as: Identity;
};

// Create a session where `Identity` is used directly and not derived.
export const createAdminSession = async (
{ identity, space, name }: {
export const createSessionFromDid = (
{ identity, space, spaceName }: {
identity: Identity;
space: DID;
name: string;
spaceName: string;
},
) =>
await ({
private: name.startsWith("~"),
name,
): Promise<Session> => {
const isPrivate = spaceName.startsWith("~");
return Promise.resolve({
isPrivate,
spaceName,
space,
as: identity,
});
};

// Create a session where `Identity` is used to derive a space key.
export const createSession = async (
{ identity, name }: { identity: Identity; name: string },
) => {
const space = await identity.derive(name);
{ identity, spaceName }: { identity: Identity; spaceName: string },
): Promise<Session> => {
const isPrivate = spaceName.startsWith("~");
const account = isPrivate ? identity : await Identity.fromPassphrase(ANYONE);

const user = await account.derive(spaceName);
return {
private: name.startsWith("~"),
name,
space: space.did(),
as: space,
isPrivate,
spaceName,
space: user.did(),
as: user,
};
};
4 changes: 2 additions & 2 deletions packages/runner/integration/array_push.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ async function runTest() {
const space_thingy = await account.derive(SPACE_NAME);
const space_thingy_space = space_thingy.did();
const session = {
private: false,
name: SPACE_NAME,
isPrivate: false,
spaceName: SPACE_NAME,
space: space_thingy_space,
as: space_thingy,
} as Session;
Expand Down
4 changes: 2 additions & 2 deletions packages/runner/integration/derive_array_leak.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,8 @@ async function runTest() {
const space_thingy = await account.derive(SPACE_NAME);
const space_thingy_space = space_thingy.did();
const session = {
private: false,
name: SPACE_NAME,
isPrivate: false,
spaceName: SPACE_NAME,
space: space_thingy_space,
as: space_thingy,
} as Session;
Expand Down
13 changes: 13 additions & 0 deletions packages/runner/src/cell.ts
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,7 @@ const cellMethods = new Set<keyof ICell<unknown>>([
"setRaw",
"getSourceCell",
"setSourceCell",
"getArgumentCell",
"freeze",
"isFrozen",
"setSchema",
Expand Down Expand Up @@ -916,6 +917,18 @@ export class CellImpl<T> implements ICell<T>, IStreamable<T> {
);
}

getArgumentCell<U>(schema?: JSONSchema): Cell<U> | undefined {
const sourceCell = this.getSourceCell();
if (!sourceCell) return undefined;
// Kick off sync, since when used in a pattern, this wasn't automatically
// subscribed to yet. So we might still get a conflict on first write, but will
// get the correct version on retry.
sourceCell.sync();
// TODO(seefeld): Ideally we intersect this schema with the actual argument
// schema, so that get isn't for any.
return sourceCell.key("argument").asSchema<U>(schema);
}

freeze(reason: string): void {
this.readOnlyReason = reason;
}
Expand Down
59 changes: 58 additions & 1 deletion packages/runner/test/cell.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { isCell } from "../src/cell.ts";
import { LINK_V1_TAG } from "../src/sigil-types.ts";
import { isCellResult } from "../src/query-result-proxy.ts";
import { toCell } from "../src/back-to-cell.ts";
import { ID, JSONSchema } from "../src/builder/types.ts";
import { ID, JSONSchema, type Recipe } from "../src/builder/types.ts";
import { popFrame, pushFrame } from "../src/builder/recipe.ts";
import { Runtime } from "../src/runtime.ts";
import { txToReactivityLog } from "../src/scheduler.ts";
Expand Down Expand Up @@ -225,6 +225,63 @@ describe("Cell", () => {
expect(retrievedSource?.get()).toEqual({ foo: 456 });
});

it("should update recipe output when argument is changed via getArgumentCell", async () => {
// Create a simple doubling recipe
const doubleRecipe: Recipe = {
argumentSchema: {
type: "object",
properties: { input: { type: "number" } },
required: ["input"],
},
resultSchema: {
type: "object",
properties: { output: { type: "number" } },
},
result: { output: { $alias: { path: ["internal", "doubled"] } } },
nodes: [
{
module: {
type: "javascript",
implementation: (args: { input: number }) => (args.input * 2),
},
inputs: { input: { $alias: { path: ["argument", "input"] } } },
outputs: { $alias: { path: ["internal", "doubled"] } },
},
],
};

// Instantiate the recipe with initial argument
const resultCell = runtime.getCell(space, "doubling recipe instance");
runtime.setup(undefined, doubleRecipe, { input: 5 }, resultCell);
runtime.start(resultCell);
await runtime.idle();

// Verify initial output
expect(resultCell.getAsQueryResult().output).toEqual(10);

// Get the argument cell and update it
const argumentCell = resultCell.getArgumentCell<{ input: number }>();
expect(argumentCell).toBeDefined();
expect(argumentCell?.get()).toEqual({ input: 5 });

// Update the argument via the argument cell
const updateTx = runtime.edit();
argumentCell!.withTx(updateTx).set({ input: 7 });
updateTx.commit();
await runtime.idle();

// Verify the output has changed
expect(resultCell.getAsQueryResult()).toEqual({ output: 14 });

// Update again to verify reactivity
const updateTx2 = runtime.edit();
argumentCell!.withTx(updateTx2).set({ input: 100 });
updateTx2.commit();
await runtime.idle();

expect(resultCell.getAsQueryResult()).toEqual({ output: 200 });
});

it("should translate circular references into links", () => {
const c = runtime.getCell(
space,
Expand Down
2 changes: 1 addition & 1 deletion packages/seeder/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ setLLMUrl(apiUrl);
const identity = await Identity.fromPassphrase("common user");
const session = await createSession({
identity,
name,
spaceName: name,
});

const runtime = new Runtime({
Expand Down
23 changes: 2 additions & 21 deletions packages/shell/src/lib/runtime.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ANYONE, Identity, Session } from "@commontools/identity";
import { createSession, Identity } from "@commontools/identity";
import {
Runtime,
RuntimeTelemetry,
Expand All @@ -18,25 +18,6 @@ const logger = getLogger("shell.telemetry", {
level: "debug",
});

async function createSession(
root: Identity,
spaceName: string,
): Promise<Session> {
const account = spaceName.startsWith("~")
? root
: await Identity.fromPassphrase(ANYONE);

const user = await account.derive(spaceName);
const session = {
private: account.did() === root.did(),
name: spaceName,
space: user.did(),
as: user,
};

return session;
}

// RuntimeInternals bundles all of the lifetimes
// of resources bound to an identity,host,space triplet,
// containing runtime, inspector, and charm references.
Expand Down Expand Up @@ -103,7 +84,7 @@ export class RuntimeInternals extends EventTarget {
apiUrl: URL;
},
): Promise<RuntimeInternals> {
const session = await createSession(identity, spaceName);
const session = await createSession({ identity, spaceName });

// We're hoisting CharmManager so that
// we can create it after the runtime, but still reference
Expand Down
6 changes: 3 additions & 3 deletions scripts/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import {
} from "@commontools/runner";
import { StorageManager } from "@commontools/runner/storage/cache";
import {
createAdminSession,
createSessionFromDid,
type DID,
Identity,
type Session,
Expand Down Expand Up @@ -85,10 +85,10 @@ async function main() {

const space: DID = spaceDID as DID ?? identity.did();

const session = await createAdminSession({
const session = await createSessionFromDid({
identity,
space,
name: spaceName ?? "unknown",
spaceName: spaceName ?? "unknown",
}) satisfies Session;

// TODO(seefeld): It only wants the space, so maybe we simplify the above and just space the space did?
Expand Down
Loading
Loading