Skip to content

Commit bf97139

Browse files
d-gubertclaude
andcommitted
refactor(apps): converge deno-runtime toward node-runtime
Reduce the platform-specific surface in deno-runtime by aligning six drifted files with their canonical node-runtime implementations, ahead of extracting a shared base runtime: - messenger/main: use node:events EventEmitter for the RPC response observer instead of EventTarget + Custom/ErrorEvent - parseArgs: use node:util instead of @std/cli (drop dead @std/cli dep) - BlockBuilder: extend apps-engine BlockBuilder instead of duplicating it (enabled by sloppy-imports) - handleUploadEvents: read uploads via node:fs/promises readFile + Buffer - logger: implement ILogger with the full accessor surface - accessors/mod: extract named WithProxy<T> consts No behavior change intended. deno task test stays green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 4025415 commit bf97139

9 files changed

Lines changed: 233 additions & 371 deletions

File tree

packages/apps/deno-runtime/deno.jsonc

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
"@rocket.chat/ui-kit": "npm:@rocket.chat/ui-kit@^0.31.22",
55
"@rocket.chat/apps-engine/": "../../../packages/apps-engine/",
66
"@rocket.chat/apps/": "../",
7-
"@std/cli": "jsr:@std/cli@^1.0.9",
87
"@std/io": "jsr:@std/io@^0.225.3",
98
"acorn": "npm:acorn@8.10.0",
109
"acorn-walk": "npm:acorn-walk@8.2.0",

packages/apps/deno-runtime/deno.lock

Lines changed: 0 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/apps/deno-runtime/handlers/app/handleUploadEvents.ts

Lines changed: 28 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,18 @@
1-
import { open } from 'node:fs/promises';
1+
import { Buffer } from 'node:buffer';
2+
import { readFile } from 'node:fs/promises';
23

34
import type { App } from '@rocket.chat/apps-engine/definition/App';
45
import { AppsEngineException } from '@rocket.chat/apps-engine/definition/exceptions/AppsEngineException';
5-
import type { IFileUploadContext } from '@rocket.chat/apps-engine/definition/uploads/IFileUploadContext'
6-
import type { IUploadDetails } from '@rocket.chat/apps-engine/definition/uploads/IUploadDetails'
7-
import { Defined, JsonRpcError } from 'jsonrpc-lite';
6+
import type { IFileUploadContext } from '@rocket.chat/apps-engine/definition/uploads/IFileUploadContext';
7+
import type { IUploadDetails } from '@rocket.chat/apps-engine/definition/uploads/IUploadDetails';
8+
import type { Defined } from 'jsonrpc-lite';
9+
import { JsonRpcError } from 'jsonrpc-lite';
810

911
import { AppObjectRegistry } from '../../AppObjectRegistry';
10-
import { assertAppAvailable, assertHandlerFunction, isPlainObject } from '../lib/assertions';
1112
import { AppAccessorsInstance } from '../../lib/accessors/mod';
1213
import { RequestContext } from '../../lib/requestContext';
1314
import { wrapAppForRequest } from '../../lib/wrapAppForRequest';
15+
import { assertAppAvailable, assertHandlerFunction, isPlainObject } from '../lib/assertions';
1416

1517
export const uploadEvents = ['executePreFileUpload'] as const;
1618

@@ -27,8 +29,11 @@ function assertString(v: unknown): asserts v is string {
2729
}
2830

2931
export default async function handleUploadEvents(request: RequestContext): Promise<Defined | JsonRpcError> {
30-
const { method: rawMethod, params } = request as { method: `app:${typeof uploadEvents[number]}`; params: [{ file?: IUploadDetails, path?: string }]};
31-
const [, method] = rawMethod.split(':') as ['app', typeof uploadEvents[number]];
32+
const { method: rawMethod, params } = request as {
33+
method: `app:${(typeof uploadEvents)[number]}`;
34+
params: [{ file?: IUploadDetails; path?: string }];
35+
};
36+
const [, method] = rawMethod.split(':') as ['app', (typeof uploadEvents)[number]];
3237

3338
try {
3439
const [{ file, path }] = params;
@@ -41,30 +46,25 @@ export default async function handleUploadEvents(request: RequestContext): Promi
4146
assertIsUpload(file);
4247
assertString(path);
4348

44-
const tempFile = await open(path, 'r');
45-
46-
try {
47-
let context: IFileUploadContext;
49+
let context: IFileUploadContext;
4850

49-
switch (method) {
50-
case 'executePreFileUpload': {
51-
context = { file, content: await tempFile.readFile() };
52-
break;
53-
}
51+
switch (method) {
52+
case 'executePreFileUpload': {
53+
const fileContents = await readFile(path);
54+
context = { file, content: Buffer.from(fileContents) };
55+
break;
5456
}
55-
56-
return await handlerFunction.call(
57-
wrapAppForRequest(app, request),
58-
context,
59-
AppAccessorsInstance.getReader(),
60-
AppAccessorsInstance.getHttp(),
61-
AppAccessorsInstance.getPersistence(),
62-
AppAccessorsInstance.getModifier(),
63-
);
64-
} finally {
65-
await tempFile.close();
6657
}
67-
} catch(e) {
58+
59+
return await handlerFunction.call(
60+
wrapAppForRequest(app, request),
61+
context,
62+
AppAccessorsInstance.getReader(),
63+
AppAccessorsInstance.getHttp(),
64+
AppAccessorsInstance.getPersistence(),
65+
AppAccessorsInstance.getModifier(),
66+
);
67+
} catch (e) {
6868
if (e?.name === AppsEngineException.name) {
6969
return new JsonRpcError(e.message, AppsEngineException.JSONRPC_ERROR_CODE, { name: e.name });
7070
}
Lines changed: 7 additions & 201 deletions
Original file line numberDiff line numberDiff line change
@@ -1,210 +1,16 @@
1-
import { v1 as uuid } from 'uuid';
2-
3-
import type {
4-
IActionsBlock,
5-
IBlock,
6-
IConditionalBlock,
7-
IConditionalBlockFilters,
8-
IContextBlock,
9-
IImageBlock,
10-
IInputBlock,
11-
ISectionBlock,
12-
} from '@rocket.chat/apps-engine/definition/uikit/blocks/Blocks';
13-
import { BlockType } from '@rocket.chat/apps-engine/definition/uikit/blocks/Blocks';
14-
import type {
15-
IBlockElement,
16-
IButtonElement,
17-
IImageElement,
18-
IInputElement,
19-
IInteractiveElement,
20-
IMultiStaticSelectElement,
21-
IOverflowMenuElement,
22-
IPlainTextInputElement,
23-
ISelectElement,
24-
IStaticSelectElement,
25-
} from '@rocket.chat/apps-engine/definition/uikit/blocks/Elements';
26-
import { BlockElementType } from '@rocket.chat/apps-engine/definition/uikit/blocks/Elements';
27-
import { TextObjectType, type ITextObject } from '@rocket.chat/apps-engine/definition/uikit/blocks/Objects';
1+
import { BlockBuilder as AppsEngineBlockBuilder } from '@rocket.chat/apps-engine/definition/uikit/blocks/BlockBuilder';
282

293
import { AppObjectRegistry } from '../../../AppObjectRegistry';
304

31-
type BlockFunctionParameter<T extends IBlock> = Omit<T, 'type'>;
32-
type ElementFunctionParameter<T extends IBlockElement> = T extends IInteractiveElement ? Omit<T, 'type' | 'actionId'> | Partial<Pick<T, 'actionId'>>
33-
: Omit<T, 'type'>;
34-
35-
type SectionBlockParam = BlockFunctionParameter<ISectionBlock>;
36-
type ImageBlockParam = BlockFunctionParameter<IImageBlock>;
37-
type ActionsBlockParam = BlockFunctionParameter<IActionsBlock>;
38-
type ContextBlockParam = BlockFunctionParameter<IContextBlock>;
39-
type InputBlockParam = BlockFunctionParameter<IInputBlock>;
40-
41-
type ButtonElementParam = ElementFunctionParameter<IButtonElement>;
42-
type ImageElementParam = ElementFunctionParameter<IImageElement>;
43-
type OverflowMenuElementParam = ElementFunctionParameter<IOverflowMenuElement>;
44-
type PlainTextInputElementParam = ElementFunctionParameter<IPlainTextInputElement>;
45-
type StaticSelectElementParam = ElementFunctionParameter<IStaticSelectElement>;
46-
type MultiStaticSelectElementParam = ElementFunctionParameter<IMultiStaticSelectElement>;
47-
485
/**
6+
* Local BlockBuilder that extends the apps-engine BlockBuilder.
7+
* It overrides the constructor to source the appId from the registry
8+
* instead of requiring it as a constructor argument.
9+
*
4910
* @deprecated please prefer the rocket.chat/ui-kit components
5011
*/
51-
export class BlockBuilder {
52-
private readonly blocks: Array<IBlock>;
53-
private readonly appId: string;
54-
12+
export class BlockBuilder extends AppsEngineBlockBuilder {
5513
constructor() {
56-
this.blocks = [];
57-
this.appId = String(AppObjectRegistry.get('id'));
58-
}
59-
60-
public addSectionBlock(block: SectionBlockParam): BlockBuilder {
61-
this.addBlock({ type: BlockType.SECTION, ...block } as ISectionBlock);
62-
63-
return this;
64-
}
65-
66-
public addImageBlock(block: ImageBlockParam): BlockBuilder {
67-
this.addBlock({ type: BlockType.IMAGE, ...block } as IImageBlock);
68-
69-
return this;
70-
}
71-
72-
public addDividerBlock(): BlockBuilder {
73-
this.addBlock({ type: BlockType.DIVIDER });
74-
75-
return this;
76-
}
77-
78-
public addActionsBlock(block: ActionsBlockParam): BlockBuilder {
79-
this.addBlock({ type: BlockType.ACTIONS, ...block } as IActionsBlock);
80-
81-
return this;
82-
}
83-
84-
public addContextBlock(block: ContextBlockParam): BlockBuilder {
85-
this.addBlock({ type: BlockType.CONTEXT, ...block } as IContextBlock);
86-
87-
return this;
88-
}
89-
90-
public addInputBlock(block: InputBlockParam): BlockBuilder {
91-
this.addBlock({ type: BlockType.INPUT, ...block } as IInputBlock);
92-
93-
return this;
94-
}
95-
96-
public addConditionalBlock(innerBlocks: BlockBuilder | Array<IBlock>, condition?: IConditionalBlockFilters): BlockBuilder {
97-
const render = innerBlocks instanceof BlockBuilder ? innerBlocks.getBlocks() : innerBlocks;
98-
99-
this.addBlock({
100-
type: BlockType.CONDITIONAL,
101-
render,
102-
when: condition,
103-
} as IConditionalBlock);
104-
105-
return this;
106-
}
107-
108-
public getBlocks() {
109-
return this.blocks;
110-
}
111-
112-
public newPlainTextObject(text: string, emoji = false): ITextObject {
113-
return {
114-
type: TextObjectType.PLAINTEXT,
115-
text,
116-
emoji,
117-
};
118-
}
119-
120-
public newMarkdownTextObject(text: string): ITextObject {
121-
return {
122-
type: TextObjectType.MARKDOWN,
123-
text,
124-
};
125-
}
126-
127-
public newButtonElement(info: ButtonElementParam): IButtonElement {
128-
return this.newInteractiveElement({
129-
type: BlockElementType.BUTTON,
130-
...info,
131-
} as IButtonElement);
132-
}
133-
134-
public newImageElement(info: ImageElementParam): IImageElement {
135-
return {
136-
type: BlockElementType.IMAGE,
137-
...info,
138-
};
139-
}
140-
141-
public newOverflowMenuElement(info: OverflowMenuElementParam): IOverflowMenuElement {
142-
return this.newInteractiveElement({
143-
type: BlockElementType.OVERFLOW_MENU,
144-
...info,
145-
} as IOverflowMenuElement);
146-
}
147-
148-
public newPlainTextInputElement(info: PlainTextInputElementParam): IPlainTextInputElement {
149-
return this.newInputElement({
150-
type: BlockElementType.PLAIN_TEXT_INPUT,
151-
...info,
152-
} as IPlainTextInputElement);
153-
}
154-
155-
public newStaticSelectElement(info: StaticSelectElementParam): IStaticSelectElement {
156-
return this.newSelectElement({
157-
type: BlockElementType.STATIC_SELECT,
158-
...info,
159-
} as IStaticSelectElement);
160-
}
161-
162-
public newMultiStaticElement(info: MultiStaticSelectElementParam): IMultiStaticSelectElement {
163-
return this.newSelectElement({
164-
type: BlockElementType.MULTI_STATIC_SELECT,
165-
...info,
166-
} as IMultiStaticSelectElement);
167-
}
168-
169-
private newInteractiveElement<T extends IInteractiveElement>(element: T): T {
170-
if (!element.actionId) {
171-
element.actionId = this.generateActionId();
172-
}
173-
174-
return element;
175-
}
176-
177-
private newInputElement<T extends IInputElement>(element: T): T {
178-
if (!element.actionId) {
179-
element.actionId = this.generateActionId();
180-
}
181-
182-
return element;
183-
}
184-
185-
private newSelectElement<T extends ISelectElement>(element: T): T {
186-
if (!element.actionId) {
187-
element.actionId = this.generateActionId();
188-
}
189-
190-
return element;
191-
}
192-
193-
private addBlock(block: IBlock): void {
194-
if (!block.blockId) {
195-
block.blockId = this.generateBlockId();
196-
}
197-
198-
block.appId = this.appId;
199-
200-
this.blocks.push(block);
201-
}
202-
203-
private generateBlockId(): string {
204-
return uuid();
205-
}
206-
207-
private generateActionId(): string {
208-
return uuid();
14+
super(String(AppObjectRegistry.get('id') ?? ''));
20915
}
21016
}

0 commit comments

Comments
 (0)