Skip to content

Commit

Permalink
fix(developer-server): catch more error types and show more context i…
Browse files Browse the repository at this point in the history
…n own errors
  • Loading branch information
dlenroc authored Dec 3, 2023
1 parent aeea75c commit cf630fe
Show file tree
Hide file tree
Showing 9 changed files with 67 additions and 57 deletions.
38 changes: 21 additions & 17 deletions packages/developer-server/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,7 @@ npm install @dlenroc/roku-developer-server
## Usage

```typescript
import {
DeveloperServerExecutor,
installChannel,
} from '@dlenroc/roku-developer-server';
import { DeveloperServerExecutor, installChannel } from '@dlenroc/roku-developer-server';
import fs from 'node:fs';

const ctx = new DeveloperServerExecutor({
Expand All @@ -27,18 +24,25 @@ const app = fs.readFileSync('<path_to_channel>');
await installChannel(ctx, { content: app });
```

📝 Retrieving content from methods returning file paths requires an extra request.

```typescript
const path = await takeScreenshot(ctx);
const response = await ctx.execute(path);
const content = await response.arrayBuffer();
```

---

| Method | Description |
| ------------------- | ---------------------------------------------------------- |
| `convertToSquashfs` | Compress sideloaded channel using Squashfs |
| `convertToZip` | Compress sideloaded channel using Zip |
| `deleteChannel` | Delete sideloaded channel |
| `deletePackage` | Delete sideloaded channel package |
| `getPackage` | Get sideloaded channel package |
| `getProfilingData` | Get profiling data |
| `getScreenshot` | Get sideloaded channel screenshot |
| `inspectPackage` | Inspect package |
| `installChannel` | Sideload a channel from a zip file |
| `packageChannel` | Package sideloaded channel |
| `rekey` | Rekey device from existing package signed with desired key |
| Method | Description |
| ------------------- | ------------------------------------------------------------------------------ |
| `convertToSquashfs` | Compress sideloaded channel using Squashfs |
| `convertToZip` | Compress sideloaded channel using Zip |
| `deleteChannel` | Delete sideloaded channel |
| `deletePackage` | Delete sideloaded channel package |
| `inspectPackage` | Inspect channel package |
| `installChannel` | Sideload a channel from a zip file |
| `packageChannel` | Package sideloaded channel and return path to it |
| `rekey` | Rekey device from existing package signed with desired key |
| `saveProfilingData` | Saves the profiling data and returns the path to it |
| `takeScreenshot` | Takes a screenshot of the sideloaded channel and returns the path to the image |
18 changes: 9 additions & 9 deletions packages/developer-server/src/DeveloperServerError.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,22 @@
export class DeveloperServerError extends Error {
public readonly path: string;
public readonly params: Record<string, string | Blob> | undefined;
public readonly output: string;
public readonly payload?: RequestInit;
public readonly response: Response;

constructor(options: {
path: string;
params?: Record<string, string | Blob> | undefined;
output: string;
message?: string;
path: string;
payload?: RequestInit;
response: Response;
}) {
super(
options.message ||
`Failed to parse result of ${options.path} command: ${options.output}`
options.message ??
`Request to "${options.path}" failed: ${options.response.status} ${options.response.statusText}`
);

this.name = 'DeveloperServerError';
this.path = options.path;
this.params = options.params;
this.output = options.output;
this.payload = options.payload!;
this.response = options.response;
}
}
6 changes: 3 additions & 3 deletions packages/developer-server/src/commands/inspectPackage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import type { Config } from '../internal/types.d.ts';
*/
export async function inspectPackage<Context extends Executor>(
ctx: Context,
option: {
payload: {
/**
* Package to inspect.
*/
Expand All @@ -31,8 +31,8 @@ export async function inspectPackage<Context extends Executor>(
ctx,
{
mysubmit: 'Inspect',
archive: new Blob([option.content]),
passwd: option.password,
archive: new Blob([payload.content]),
passwd: payload.password,
},
config
);
Expand Down
12 changes: 7 additions & 5 deletions packages/developer-server/src/commands/installChannel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import type { Config } from '../internal/types.d.ts';
*/
export async function installChannel<Context extends Executor>(
ctx: Context,
option: {
payload: {
/**
* The content of the zip file to install.
*/
Expand All @@ -33,10 +33,12 @@ export async function installChannel<Context extends Executor>(
await executePluginInstallCommand(
ctx,
{
mysubmit: 'Install' + (option.useSquashfs ? ' with squashfs' : ''),
archive: new Blob([option.content]),
...(option.remoteDebug && { remotedebug: '1' }),
...(option.remoteDebugConnectEarly && { remotedebug_connect_early: '1' }),
mysubmit: 'Install' + (payload.useSquashfs ? ' with squashfs' : ''),
archive: new Blob([payload.content]),
...(payload.remoteDebug && { remotedebug: '1' }),
...(payload.remoteDebugConnectEarly && {
remotedebug_connect_early: '1',
}),
},
config
);
Expand Down
8 changes: 4 additions & 4 deletions packages/developer-server/src/commands/packageChannel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import type { Config } from '../internal/types.d.ts';
*/
export async function packageChannel<Context extends Executor>(
ctx: Context,
option: {
payload: {
/**
* Channel Name/Version
*/
Expand All @@ -29,9 +29,9 @@ export async function packageChannel<Context extends Executor>(
ctx,
{
mysubmit: 'Package',
pkg_time: String(option.timestamp ?? new Date().getTime()),
app_name: option.name,
passwd: option.password,
pkg_time: String(payload.timestamp ?? new Date().getTime()),
app_name: payload.name,
passwd: payload.password,
},
config
);
Expand Down
6 changes: 3 additions & 3 deletions packages/developer-server/src/commands/rekey.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import type { Config } from '../internal/types.d.ts';
*/
export async function rekey<Context extends Executor>(
ctx: Context,
option: {
payload: {
/**
* Package signed with desired key.
*/
Expand All @@ -24,8 +24,8 @@ export async function rekey<Context extends Executor>(
ctx,
{
mysubmit: 'Rekey',
archive: new Blob([option.content]),
passwd: option.password,
archive: new Blob([payload.content]),
passwd: payload.password,
},
config
);
Expand Down
27 changes: 13 additions & 14 deletions packages/developer-server/src/internal/execute.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,23 +10,22 @@ export async function execute<Context extends Executor>(
): Promise<string> {
const response = await ctx.execute(path, init);

if (response.status !== 200) {
throw new DeveloperServerError({
message: `Request failed with status ${response.status}`,
path: path,
params: {},
output: '',
});
}

const text = await response.text();
const results = parseRokuMessages(text);
if (results.errors.length > 0) {
if (!response.ok || results.errors.length) {
const message = Object.entries(results).reduce(
(acc, [key, messages]) =>
messages.length
? `${acc}\n[${key}]\n${messages.join('\n').replaceAll(/^/gm, ' ')}\n`
: acc,
''
);

throw new DeveloperServerError({
message: results.errors.join('\n'),
path: path,
params: {},
output: text,
message: `Request to "${path}" failed: ${response.status} ${response.statusText}${message}`,
path,
payload: init!,
response: new Response(text, response),
});
}

Expand Down
7 changes: 7 additions & 0 deletions packages/developer-server/src/internal/parseRokuMessages.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
const ROKU_MESSAGES_PATTERN =
/Shell\.create\('Roku\.Message'\)\.trigger\('[\w\s]+',\s+'(\w+)'\)\.trigger\('[\w\s]+',\s+'(.*?)'\)/gim;

const ROKU_ERROR_PATTERN =
/{\s*"text"\s*:\s*("[^"]+")\s*,\s*"text_type"\s*:\s*"text"\s*,\s*"type"\s*:\s*"error"/gim;

export function parseRokuMessages(text: string): RokuMessages {
const results: RokuMessages = { errors: [], infos: [], successes: [] };

Expand All @@ -18,6 +21,10 @@ export function parseRokuMessages(text: string): RokuMessages {
}
}

for (const [, message] of text.matchAll(ROKU_ERROR_PATTERN)) {
results.errors.push(JSON.parse(message!));
}

return results;
}

Expand Down
2 changes: 0 additions & 2 deletions packages/developer-server/src/internal/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,3 @@ export type Mixed<A, B> =
| (A & B)
| (Omit<A, keyof B> &
Omit<B, keyof A> & { [K in keyof (A | B)]: (A | B)[K] });

export type Nullable<T> = T | undefined | null;

0 comments on commit cf630fe

Please sign in to comment.