Skip to content

Commit

Permalink
move to non-experimental decorators
Browse files Browse the repository at this point in the history
  • Loading branch information
lastmjs committed Jul 15, 2024
1 parent b3f9b00 commit c15fef0
Show file tree
Hide file tree
Showing 18 changed files with 4,268 additions and 257 deletions.
682 changes: 678 additions & 4 deletions examples/init/package-lock.json

Large diffs are not rendered by default.

692 changes: 687 additions & 5 deletions examples/pre_and_post_upgrade/package-lock.json

Large diffs are not rendered by default.

717 changes: 687 additions & 30 deletions examples/simple_user_accounts/package-lock.json

Large diffs are not rendered by default.

71 changes: 40 additions & 31 deletions src/lib/stable/execute_with_candid_serde.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { IDL } from '@dfinity/candid';
import { IDL, JsonValue } from '@dfinity/candid';

import { handleUncaughtError } from './error';
import { reply } from './ic_apis';
Expand All @@ -7,52 +7,61 @@ type CanisterMethodMode =
| 'query'
| 'update'
| 'init'
| 'heartbeat'
| 'inspectMessage'
| 'postUpgrade'
| 'preUpgrade';
| 'preUpgrade'
| 'inspectMessage'
| 'heartbeat';

export function executeWithCandidSerde(
export async function executeAndReplyWithCandidSerde(
mode: CanisterMethodMode,
args: any[],
callback: any,
callback: (...args: any) => any,
paramIdlTypes: IDL.Type[],
returnIdlType: IDL.Type | undefined,
manual: boolean
): void {
const decodedArgs = IDL.decode(paramIdlTypes, args[0]);

const result = getResult(decodedArgs, callback);

if (mode === 'init' || mode === 'postUpgrade') {
return;
}
): Promise<void> {
const decodedArgs = decodeArgs(mode, args, paramIdlTypes);
const unencodedResult = await getUnencodedResult(decodedArgs, callback);
encodeResultAndReply(mode, manual, unencodedResult, returnIdlType);
}

function decodeArgs(
mode: CanisterMethodMode,
args: any[],
paramIdlTypes: IDL.Type[]
): JsonValue[] {
if (
result !== undefined &&
result !== null &&
typeof result.then === 'function'
mode === 'init' ||
mode === 'postUpgrade' ||
mode === 'query' ||
mode === 'update'
) {
result
.then((result: any) => {
if (!manual) {
reply({ data: result, idlType: returnIdlType });
}
})
.catch((error: any) => {
handleUncaughtError(error);
});
return IDL.decode(paramIdlTypes, args[0]);
} else {
if (!manual) {
reply({ data: result, idlType: returnIdlType });
}
return [];
}
}

function getResult(args: any[], callback: any): any {
async function getUnencodedResult(
args: JsonValue[],
callback: (...args: any) => any
): Promise<any> {
try {
return callback(...args);
return await callback(...args);
} catch (error) {
handleUncaughtError(error);
}
}

function encodeResultAndReply(
mode: CanisterMethodMode,
manual: boolean,
unencodedResult: any,
returnIdlType: IDL.Type | undefined
): void {
if ((mode !== 'query' && mode !== 'update') || manual === true) {
return;
}

reply({ data: unencodedResult, idlType: returnIdlType });
}
54 changes: 18 additions & 36 deletions src/lib/stable/heartbeat.ts
Original file line number Diff line number Diff line change
@@ -1,44 +1,26 @@
import { handleUncaughtError } from './error';
import { executeAndReplyWithCandidSerde } from './execute_with_candid_serde';

export function heartbeat<T>(
target: object,
propertyKey: string | symbol,
descriptor: TypedPropertyDescriptor<T>
): TypedPropertyDescriptor<T> | void {
export function heartbeat<This, Args extends any[], Return>(
originalMethod: (this: This, ...args: Args) => Return,
context: ClassMethodDecoratorContext
): void {
const index = globalThis._azleCanisterMethodsIndex++;

globalThis._azleCanisterMethods.heartbeat = {
name: propertyKey as string,
name: context.name as string,
index
};

globalThis._azleCanisterMethods.callbacks[index.toString()] =
async (): Promise<void> => {
try {
await (descriptor.value as any).bind(target)();
} catch (error) {
handleUncaughtError(error);
}
};

return descriptor;
globalThis._azleCanisterMethods.callbacks[index.toString()] = (
...args: any[]
): void => {
executeAndReplyWithCandidSerde(
'heartbeat',
args,
originalMethod.bind(globalThis._azleCanisterClassInstance),
[],
undefined,
false
);
};
}

// TODO do we need this?
// TODO it would be nice if QuickJS would just panic
// TODO on all uncaught exceptions
// function executeHeartbeat(callback: any) {
// const result = callback();

// if (
// result !== undefined &&
// result !== null &&
// typeof result.then === 'function'
// ) {
// result.catch((error: any) => {
// ic.trap(error.toString());
// });
// }

// return;
// }
48 changes: 21 additions & 27 deletions src/lib/stable/init.ts
Original file line number Diff line number Diff line change
@@ -1,38 +1,32 @@
import { IDL } from '@dfinity/candid';

import { executeWithCandidSerde } from './execute_with_candid_serde';
import { executeAndReplyWithCandidSerde } from './execute_with_candid_serde';

export function init<This, Args extends any[], Return>(
paramIdlTypes: IDL.Type[]
) {
return (
originalMethod: (this: This, ...args: Args) => Return,
context: ClassMethodDecoratorContext
): void => {
const index = globalThis._azleCanisterMethodsIndex++;

export function init(paramIdls: IDL.Type[]): MethodDecorator {
return <T>(
target: object,
propertyKey: string | symbol,
descriptor: TypedPropertyDescriptor<T>
): TypedPropertyDescriptor<T> | void => {
const originalMethod = (descriptor.value as any).bind(target);
globalThis._azleCanisterMethods.init = {
name: context.name as string,
index
};

const methodCallback = (...args: any[]): void => {
executeWithCandidSerde(
globalThis._azleCanisterMethods.callbacks[index.toString()] = (
...args: any[]
): void => {
executeAndReplyWithCandidSerde(
'init',
args,
originalMethod,
paramIdls,
originalMethod.bind(globalThis._azleCanisterClassInstance),
paramIdlTypes,
undefined,
false // TODO implement manual check
false
);
};

descriptor.value = methodCallback as any;

const index = globalThis._azleCanisterMethodsIndex++;

globalThis._azleCanisterMethods.init = {
name: propertyKey as string,
index
};

globalThis._azleCanisterMethods.callbacks[index.toString()] =
methodCallback;

return descriptor;
};
}
32 changes: 17 additions & 15 deletions src/lib/stable/inspect_message.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,27 +4,29 @@
// TODO but it will break all of your other methods
// TODO so do we just leave params out?

import { handleUncaughtError } from './error';
import { executeAndReplyWithCandidSerde } from './execute_with_candid_serde';

export function inspectMessage<T>(
target: object,
propertyKey: string | symbol,
descriptor: TypedPropertyDescriptor<T>
): TypedPropertyDescriptor<T> | void {
export function inspectMessage<This, Args extends any[], Return>(
originalMethod: (this: This, ...args: Args) => Return,
context: ClassMethodDecoratorContext
): void {
const index = globalThis._azleCanisterMethodsIndex++;

globalThis._azleCanisterMethods.inspect_message = {
name: propertyKey as string,
name: context.name as string,
index
};

globalThis._azleCanisterMethods.callbacks[index.toString()] = (): void => {
try {
(descriptor.value as any).bind(target)();
} catch (error) {
handleUncaughtError(error);
}
globalThis._azleCanisterMethods.callbacks[index.toString()] = (
...args: any[]
): void => {
executeAndReplyWithCandidSerde(
'inspectMessage',
args,
originalMethod.bind(globalThis._azleCanisterClassInstance),
[],
undefined,
false
);
};

return descriptor;
}
48 changes: 21 additions & 27 deletions src/lib/stable/post_upgrade.ts
Original file line number Diff line number Diff line change
@@ -1,38 +1,32 @@
import { IDL } from '@dfinity/candid';

import { executeWithCandidSerde } from './execute_with_candid_serde';
import { executeAndReplyWithCandidSerde } from './execute_with_candid_serde';

export function postUpgrade<This, Args extends any[], Return>(
paramIdlTypes: IDL.Type[]
) {
return (
originalMethod: (this: This, ...args: Args) => Return,
context: ClassMethodDecoratorContext
): void => {
const index = globalThis._azleCanisterMethodsIndex++;

export function postUpgrade(paramIdls: IDL.Type[]): MethodDecorator {
return <T>(
target: object,
propertyKey: string | symbol,
descriptor: TypedPropertyDescriptor<T>
): TypedPropertyDescriptor<T> | void => {
const originalMethod = (descriptor.value as any).bind(target);
globalThis._azleCanisterMethods.post_upgrade = {
name: context.name as string,
index
};

const methodCallback = (...args: any[]): void => {
executeWithCandidSerde(
globalThis._azleCanisterMethods.callbacks[index.toString()] = (
...args: any[]
): void => {
executeAndReplyWithCandidSerde(
'postUpgrade',
args,
originalMethod,
paramIdls,
originalMethod.bind(globalThis._azleCanisterClassInstance),
paramIdlTypes,
undefined,
false // TODO implement manual check
false
);
};

descriptor.value = methodCallback as any;

const index = globalThis._azleCanisterMethodsIndex++;

globalThis._azleCanisterMethods.post_upgrade = {
name: propertyKey as string,
index
};

globalThis._azleCanisterMethods.callbacks[index.toString()] =
methodCallback;

return descriptor;
};
}
32 changes: 17 additions & 15 deletions src/lib/stable/pre_upgrade.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,26 @@
import { handleUncaughtError } from './error';
import { executeAndReplyWithCandidSerde } from './execute_with_candid_serde';

export function preUpgrade<T>(
target: object,
propertyKey: string | symbol,
descriptor: TypedPropertyDescriptor<T>
): TypedPropertyDescriptor<T> | void {
export function preUpgrade<This, Args extends any[], Return>(
originalMethod: (this: This, ...args: Args) => Return,
context: ClassMethodDecoratorContext
): void {
const index = globalThis._azleCanisterMethodsIndex++;

globalThis._azleCanisterMethods.pre_upgrade = {
name: propertyKey as string,
name: context.name as string,
index
};

globalThis._azleCanisterMethods.callbacks[index.toString()] = (): void => {
try {
(descriptor.value as any).bind(target)();
} catch (error) {
handleUncaughtError(error);
}
globalThis._azleCanisterMethods.callbacks[index.toString()] = (
...args: any[]
): void => {
executeAndReplyWithCandidSerde(
'preUpgrade',
args,
originalMethod.bind(globalThis._azleCanisterClassInstance),
[],
undefined,
false
);
};

return descriptor;
}
Loading

0 comments on commit c15fef0

Please sign in to comment.