Socket
Socket
Sign inDemoInstall

@starbeam/debug

Package Overview
Dependencies
Maintainers
2
Versions
49
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@starbeam/debug - npm Package Compare versions

Comparing version 0.7.4 to 0.8.0

.eslintrc.json

15

CHANGELOG.md
# @starbeam/debug
## 0.8.0
### Minor Changes
- 1a553c5: Prepare for 0.8
### Patch Changes
- Updated dependencies [1a553c5]
- @starbeam/interfaces@0.8.0
- @starbeam/core-utils@0.8.0
- @starbeam/shared@1.3.0
- @starbeam/verify@0.8.0
- @domtree/flavors@0.9.2
## 0.7.4

@@ -4,0 +19,0 @@

124

dist/index.d.ts

@@ -6,2 +6,4 @@ import * as interfaces from "@starbeam/interfaces";

import { CustomInspectFunction, InspectOptionsStylized, Style } from "util";
/// <reference types="node" />
import { UnknownFn } from "@starbeam/core-utils";
/**

@@ -14,4 +16,4 @@ * This symbol is used in APIs that accept an ID to indicate that an existing ID should be reused.

interface DescriptionStatics {
is(description: unknown): description is interfaces.Description;
from(args: interfaces.DescriptionArgs): interfaces.Description;
is: (description: unknown) => description is interfaces.Description;
from: (args: interfaces.DescriptionArgs) => interfaces.Description;
}

@@ -61,2 +63,4 @@ type Description = interfaces.Description;

declare function inspect(value: unknown, ...args: Parameters<CustomInspectFunction>): unknown;
type LoggedFunction = <T>(value: T, log?: (value: T) => void) => T;
declare const logged: LoggedFunction;
declare enum LogLevel {

@@ -73,3 +77,3 @@ Trace = 0,

constructor(logger: Logger, level: LogLevel, config: LoggerConfig);
log(arg: unknown, ...args: unknown[]): void;
log: (arg: unknown, ...args: unknown[]) => void;
get withStack(): LoggerWithStack;

@@ -164,10 +168,10 @@ }

path: string;
root?: DisplayRoot;
action?: string;
loc?: Loc;
root?: DisplayRoot | undefined;
action?: string | undefined;
loc?: Loc | undefined;
});
get action(): string | undefined;
get loc(): Loc | undefined;
get path(): string;
get root(): DisplayRoot | undefined;
get action(): string | undefined;
get loc(): Loc | undefined;
display(): string;

@@ -178,24 +182,24 @@ }

constructor(module: DescribedModulePath | DescribedPackage);
display(location?: {
loc?: Loc | undefined;
action?: string | undefined;
}, options?: interfaces.StackFrameDisplayOptions): string;
parts(location?: {
loc?: Loc;
action?: string;
loc?: Loc | undefined;
action?: string | undefined;
}, options?: interfaces.StackFrameDisplayOptions): DisplayParts;
display(location?: {
loc?: Loc;
action?: string;
}, options?: interfaces.StackFrameDisplayOptions): string;
}
interface Loc {
line: number;
column?: number;
column?: number | undefined;
}
interface DescribedPath {
// path(options?: StackFrameDisplayOptions): string;
parts(options?: interfaces.StackFrameDisplayOptions): DisplayPathParts;
parts: (options?: interfaces.StackFrameDisplayOptions) => DisplayPathParts;
}
declare class DescribedModulePath implements DescribedPath {
#private;
readonly pkg: null;
readonly type = "relative";
constructor(path: string);
get pkg(): null;
parts(options?: interfaces.StackFrameDisplayOptions): DisplayPathParts;

@@ -210,19 +214,19 @@ }

}
type ErrorWithStack = Error & {
interface ErrorWithStack extends Error {
stack: string;
};
}
interface StackStatics {
readonly EMPTY: StackProtocol;
create(this: void, internal?: number): StackProtocol;
fromStack(stack: string): StackProtocol;
from(error: ErrorWithStack): StackProtocol;
from(error: unknown): StackProtocol | null;
id(this: void, description?: string | Description | {
create: (this: void, internal?: number) => StackProtocol;
fromStack: (stack: string) => StackProtocol;
from: ((error: ErrorWithStack) => StackProtocol) & ((error: unknown) => StackProtocol | null);
id: (this: void, description?: string | Description | {
id: ReactiveId;
}): ReactiveId;
description(this: void, args: DescriptionArgs & {
fromUser?: string | DescriptionDetails | interfaces.Description;
}, internal?: number): interfaces.Description;
fromCaller(this: void, internal?: number): StackProtocol;
replaceFrames(error: unknown, fromStack: StackProtocol): void;
}) => ReactiveId;
description: (this: void, args: DescriptionArgs & {
fromUser?: string | DescriptionDetails | interfaces.Description | undefined;
}, internal?: number) => interfaces.Description;
desc: (type: interfaces.DescriptionType, fromUser?: string | DescriptionDetails | interfaces.Description | undefined, internal?: number | undefined) => interfaces.Description;
fromCaller: (this: void, internal?: number) => StackProtocol;
replaceFrames: (error: unknown, fromStack: StackProtocol) => void;
/**

@@ -237,8 +241,8 @@ * Erase an abstraction from the call stack so the test error points at the user code, rather than

* not the direct call site from user code), you can specify an additional number of frames to erase
* using the `extra` parameter.
* using the `internal` parameter.
*/
entryPoint<T>(this: void, callback: () => T, options?: {
extra?: number;
entryPoint: <T>(this: void, callback: () => T, options?: {
internal?: number;
stack?: StackProtocol;
}): T;
}) => T;
}

@@ -248,9 +252,16 @@ declare const Stack: StackStatics;

declare const entryPoint: <T>(this: void, callback: () => T, options?: {
extra?: number;
internal?: number;
stack?: StackProtocol;
}) => T;
declare function entryPointFn<F extends UnknownFn>(fn: F, options?: {
stack: Stack;
}): F;
declare function entryPoints<Funcs extends object>(funcs: Funcs, options?: {
stack: Stack;
}): Funcs;
/** This should be convertable to something like Description.EMPTY in prod builds */
declare const descriptionFrom: (this: void, args: DescriptionArgs & {
fromUser?: string | DescriptionDetails | interfaces.Description;
fromUser?: string | DescriptionDetails | interfaces.Description | undefined;
}, internal?: number) => interfaces.Description;
declare const Desc: (type: interfaces.DescriptionType, fromUser?: string | DescriptionDetails | interfaces.Description | undefined, internal?: number | undefined) => interfaces.Description;
/**

@@ -265,3 +276,3 @@ * If it isn't already removed, this should be convertable to getID in prod builds

interface ReactiveProtocolStatics {
dependencies(reactive: ReactiveProtocol): Iterable<MutableInternals>;
dependencies: (reactive: ReactiveProtocol) => Iterable<MutableInternals>;
}

@@ -277,4 +288,4 @@ interface OperationInfo<I extends ReactiveInternals> {

get at(): Timestamp;
get caller(): Stack$0;
get for(): I;
get caller(): Stack$0;
}

@@ -311,3 +322,3 @@ declare class CellConsumeOperation extends LeafOperation<MutableInternals> {

readonly history: DebugOperation[];
for(reactive: ReactiveProtocol): readonly DebugOperation[];
for: (reactive: ReactiveProtocol) => readonly DebugOperation[];
}

@@ -325,5 +336,2 @@ type DebugListener = InstanceType<typeof DebugTimeline.DebugListener>;

#private;
static create(timestamp: {
now(): Timestamp;
}, statics: ReactiveProtocolStatics): DebugTimeline;
static Flush: {

@@ -337,6 +345,6 @@ new (history: DebugOperation[]): {

new (timeline: DebugTimeline, notify: () => void, filter: DebugFilter): {
"__#28032@#timeline": DebugTimeline;
"__#28032@#offset": number;
"__#28032@#filter": DebugFilter;
"__#28032@#notify": () => void;
"__#26@#timeline": DebugTimeline;
"__#26@#offset": number;
"__#26@#filter": DebugFilter;
"__#26@#notify": () => void;
update(filter: DebugFilter): void;

@@ -347,6 +355,6 @@ flush(): DebugOperation[];

offset(this: void, listener: {
"__#28032@#timeline": DebugTimeline;
"__#28032@#offset": number;
"__#28032@#filter": DebugFilter;
"__#28032@#notify": () => void;
"__#26@#timeline": DebugTimeline;
"__#26@#offset": number;
"__#26@#filter": DebugFilter;
"__#26@#notify": () => void;
update(filter: DebugFilter): void;

@@ -356,7 +364,10 @@ flush(): DebugOperation[];

}): number;
create(timestamp: {
now: () => Timestamp;
}, statics: ReactiveProtocolStatics): DebugTimeline;
notify(this: void, listener: {
"__#28032@#timeline": DebugTimeline;
"__#28032@#offset": number;
"__#28032@#filter": DebugFilter;
"__#28032@#notify": () => void;
"__#26@#timeline": DebugTimeline;
"__#26@#offset": number;
"__#26@#filter": DebugFilter;
"__#26@#notify": () => void;
update(filter: DebugFilter): void;

@@ -367,2 +378,5 @@ flush(): DebugOperation[];

};
static create(timestamp: {
now: () => Timestamp;
}, statics: ReactiveProtocolStatics): DebugTimeline;
private constructor();

@@ -391,4 +405,4 @@ notify(): void;

declare const defaultDescription: import("@starbeam/interfaces").Description;
export { Description, REUSE_ID, DisplayStructOptions, DisplayStruct, Inspect, DEBUG, DEBUG_NAME, INSPECT, inspect, inspector, Logger, LOGGER, LogLevel, Block, Fragment, Message, Style$0 as Style, Styled, Styles, DisplayParts, describeModule, callerStack, descriptionFrom, entryPoint, idFrom, isErrorWithStack, Stack, CellConsumeOperation, CellUpdateOperation, DebugFilter, DebugListener, DebugOperation, Flush, FrameConsumeOperation, MutationLog, DebugTimeline, Tree, defaultDescription };
export { Description, REUSE_ID, DisplayStructOptions, DisplayStruct, Inspect, DEBUG, DEBUG_NAME, INSPECT, inspect, inspector, logged, Logger, LOGGER, LogLevel, Block, Fragment, Message, Style$0 as Style, Styled, Styles, DisplayParts, describeModule, callerStack, Desc, descriptionFrom, entryPoint, entryPointFn, entryPoints, idFrom, isErrorWithStack, Stack, CellConsumeOperation, CellUpdateOperation, DebugFilter, DebugListener, DebugOperation, Flush, FrameConsumeOperation, MutationLog, DebugTimeline, Tree, defaultDescription };
export type { ApiDetails, DescriptionArgs, DescriptionDetails, DescriptionParts, DescriptionType, DetailDescription, DetailsPart, MemberDescription } from "@starbeam/interfaces";
//# sourceMappingURL=index.d.ts.map

@@ -0,1 +1,3 @@

import "./src/setup.js";
import { descriptionFrom } from "./src/stack.js";

@@ -16,2 +18,3 @@

} from "./src/inspect/inspect-support.js";
export { logged } from "./src/logged.js";
export { type Logger, LOGGER, LogLevel } from "./src/logger.js";

@@ -29,4 +32,7 @@ export {

callerStack,
Desc,
descriptionFrom,
entryPoint,
entryPointFn,
entryPoints,
idFrom,

@@ -33,0 +39,0 @@ isErrorWithStack,

{
"name": "@starbeam/debug",
"version": "0.8.0",
"type": "module",
"main": "dist/index.cjs",
"version": "0.7.4",
"starbeam:type": "library",
"dependencies": {
"@starbeam/shared": "^1.2.2",
"@starbeam/verify": "^0.7.3",
"@starbeam/interfaces": "^0.7.3",
"@domtree/flavors": "^0.9.1",
"stacktracey": "^2.1.8"
},
"devDependencies": {
"chalk": "^5.0.1"
},
"types": "dist/index.d.ts",
"exports": {

@@ -24,3 +14,33 @@ ".": {

},
"types": "dist/index.d.ts"
"starbeam": {
"inline": [
"stacktracey",
"get-source",
"source-map",
"data-uri-to-buffer",
"as-table",
"printable-characters",
"chalk"
],
"type": "library:public"
},
"dependencies": {
"@domtree/flavors": "^0.9.2",
"@starbeam/core-utils": "^0.8.0",
"@starbeam/interfaces": "^0.8.0",
"@starbeam/shared": "^1.3.0",
"@starbeam/verify": "^0.8.0",
"buffer": "^6.0.3",
"stacktracey": "^2.1.8"
},
"devDependencies": {
"@starbeam-dev/build-support": "1.0.0",
"@types/node": "^18.7.23",
"chalk": "^5.0.1"
},
"scripts": {
"test:lint": "eslint",
"test:specs": "vitest --run",
"test:types": "tsc -b"
}
}

@@ -5,38 +5,38 @@ import type { anydom } from "@domtree/flavors";

export interface DebugTree {
app(description: interfaces.Description): App;
route(description: interfaces.Description): Route;
component(description: interfaces.Description): Component;
snapshot(): interfaces.RootNode;
app: (description: interfaces.Description) => App;
route: (description: interfaces.Description) => Route;
component: (description: interfaces.Description) => Component;
snapshot: () => interfaces.RootNode;
}
export interface App {
route(description: interfaces.Description): Route;
component(description: interfaces.Description): Component;
route: (description: interfaces.Description) => Route;
component: (description: interfaces.Description) => Component;
}
export interface Route {
component(description: interfaces.Description): Component;
component: (description: interfaces.Description) => Component;
}
export interface Component {
component(description: interfaces.Description): Component;
lifecycle(timing: "layout" | "idle"): Lifecycle;
ref(description: interfaces.Description): Ref;
modifier(timing: "layout" | "idle"): Modifier;
resource(reactive: interfaces.ReactiveProtocol): interfaces.Unsubscribe;
domResource(
component: (description: interfaces.Description) => Component;
lifecycle: (timing: "layout" | "idle") => Lifecycle;
ref: (description: interfaces.Description) => Ref;
modifier: (timing: "layout" | "idle") => Modifier;
resource: (reactive: interfaces.ReactiveProtocol) => interfaces.Unsubscribe;
domResource: (
timing: "layout" | "idle",
reactive: interfaces.ReactiveProtocol
): interfaces.Unsubscribe;
) => interfaces.Unsubscribe;
}
export interface Lifecycle {
setup(
setup: (
timing: "layout" | "idle",
protocol: interfaces.ReactiveProtocol
): interfaces.Unsubscribe;
) => interfaces.Unsubscribe;
}
export interface Ref {
element(element: anydom.Element | null): interfaces.Unsubscribe;
element: (element: anydom.Element | null) => interfaces.Unsubscribe;
}

@@ -63,6 +63,8 @@

}
route(_description: interfaces.Description): Route {
component(_description: interfaces.Description): Component {
throw new Error("Method not implemented.");
}
component(_description: interfaces.Description): Component {
route(_description: interfaces.Description): Route {
throw new Error("Method not implemented.");

@@ -73,4 +75,4 @@ }

class DebugTreeImpl implements DebugTree {
#app: AppNode | null = null;
#root: RootNode = new RootNode();
#app: AppNode | null = null;

@@ -84,9 +86,10 @@ app(description: interfaces.Description): App {

route(_description: interfaces.Description): Route {
component(_description: interfaces.Description): Component {
throw new Error("Method not implemented.");
}
component(_description: interfaces.Description): Component {
route(_description: interfaces.Description): Route {
throw new Error("Method not implemented.");
}
snapshot(): interfaces.RootNode {

@@ -97,7 +100,7 @@ throw new Error("Method not implemented.");

// eslint-disable-next-line unused-imports/no-unused-vars, @typescript-eslint/no-unused-vars
// eslint-disable-next-line unused-imports/no-unused-vars
class ComponentImpl implements Component {
#children: interfaces.ComponentNode[] = [];
#delegate: ChildDelegate;
#parts: interfaces.ComponentNode[] = [];
#children: interfaces.ComponentNode[] = [];

@@ -111,6 +114,9 @@ constructor(delegate: ChildDelegate) {

}
lifecycle(_timing: "layout" | "idle"): Lifecycle {
domResource(
_timing: "layout" | "idle",
_reactive: interfaces.ReactiveProtocol
): interfaces.Unsubscribe {
throw new Error("Method not implemented.");
}
ref(_description: interfaces.Description): Ref {
lifecycle(_timing: "layout" | "idle"): Lifecycle {
throw new Error("Method not implemented.");

@@ -121,9 +127,6 @@ }

}
resource(_reactive: interfaces.ReactiveProtocol): interfaces.Unsubscribe {
ref(_description: interfaces.Description): Ref {
throw new Error("Method not implemented.");
}
domResource(
_timing: "layout" | "idle",
_reactive: interfaces.ReactiveProtocol
): interfaces.Unsubscribe {
resource(_reactive: interfaces.ReactiveProtocol): interfaces.Unsubscribe {
throw new Error("Method not implemented.");

@@ -134,3 +137,3 @@ }

interface ChildDelegate {
addChild(child: ChildNode): interfaces.Unsubscribe;
addChild: (child: ChildNode) => interfaces.Unsubscribe;
}

@@ -140,3 +143,3 @@

} else {
const NOOP_UNSUBSCRIBE = () => {
const NOOP_UNSUBSCRIBE = (): void => {
/* noop */

@@ -143,0 +146,0 @@ };

import type * as interfaces from "@starbeam/interfaces";
import { getID } from "@starbeam/shared";
import { exhaustive } from "@starbeam/verify";
import { exhaustive, expected, isEqual, verify } from "@starbeam/verify";
import chalk from "chalk";

@@ -8,3 +8,2 @@

import { DisplayParts } from "../module.js";
// eslint-disable-next-line import/no-cycle
import { type Stack, callerStack } from "../stack.js";

@@ -29,4 +28,4 @@

export interface DescriptionStatics {
is(description: unknown): description is interfaces.Description;
from(args: interfaces.DescriptionArgs): interfaces.Description;
is: (description: unknown) => description is interfaces.Description;
from: (args: interfaces.DescriptionArgs) => interfaces.Description;
}

@@ -38,40 +37,3 @@

{
static is(description: unknown): description is interfaces.Description {
return !!(description && description instanceof DebugDescription);
}
static from(args: interfaces.DescriptionArgs): interfaces.Description {
if (DebugDescription.is(args)) {
return args;
} else if (DebugDescription.is(args.fromUser)) {
return args.fromUser;
}
return new DebugDescription(
args.id ?? getID(),
args.type,
args.api,
args.fromUser,
args.internal,
args.stack
);
}
/**
* The type is a high-level categorization from the perspective of Starbeam. It is used in
* developer tools to decide how to render the description.
*/
readonly #type: interfaces.DescriptionType;
readonly #id: interfaces.ReactiveId;
get #idName(): string {
if (typeof this.#id === "string" || typeof this.#id === "number") {
return String(this.#id);
} else {
return this.#id.map(String).join("/");
}
}
/**
* The {@linkcode DescriptionArgs.api} is the user-facing, public API entry point that the developer used to

@@ -81,3 +43,3 @@ * construct the thing that this description is describing. For example, the `useSetup` hook in

*/
readonly #api: string | interfaces.ApiDetails;
readonly #api: string | interfaces.ApiDetails | undefined;
/**

@@ -91,5 +53,7 @@ * Optional additional details, provided by the end user. The `DescriptionDetails` may represent a

readonly #id: interfaces.ReactiveId;
#internal:
| {
reason?: string;
reason?: string | undefined;
userFacing: Description;

@@ -101,8 +65,37 @@ }

/**
* The type is a high-level categorization from the perspective of Starbeam. It is used in
* developer tools to decide how to render the description.
*/
readonly #type: interfaces.DescriptionType;
static from(args: interfaces.DescriptionArgs): interfaces.Description {
if (DebugDescription.is(args)) {
return args;
} else if (DebugDescription.is(args.fromUser)) {
return args.fromUser;
}
return new DebugDescription(
args.id ?? getID(),
args.type,
args.api,
args.fromUser,
args.internal,
args.stack
) as Description;
}
static is(description: unknown): description is interfaces.Description {
return !!(description && description instanceof DebugDescription);
}
constructor(
id: interfaces.ReactiveId,
type: interfaces.DescriptionType,
api: string | interfaces.ApiDetails,
api: string | interfaces.ApiDetails | undefined,
details: interfaces.DescriptionDetails | undefined,
internal: { reason?: string; userFacing: Description } | undefined,
internal:
| { reason?: string | undefined; userFacing: Description }
| undefined,
stack: Stack | undefined

@@ -118,131 +111,30 @@ ) {

[Symbol.for("nodejs.util.inspect.custom")](): object {
return DisplayStruct("Description", {
id: this.#id,
type: this.#type,
api: this.#api,
details: this.#details,
internal: this.#internal,
stack: this.#stack,
});
get api(): string | interfaces.ApiDetails | undefined {
return this.#api ?? this.#parent?.api;
}
#newId(id: interfaces.ReactiveId | REUSE_ID): interfaces.ReactiveId {
return id === REUSE_ID ? this.#id : id;
}
#extendId(id: interfaces.ReactiveId): interfaces.ReactiveId {
if (Array.isArray(this.#id)) {
return [...this.#id, id];
get #apiPart(): interfaces.ApiDetails | undefined {
if (typeof this.#api === "string") {
return {
name: this.#api,
};
} else {
return [this.#id, id];
return this.#api;
}
}
get id(): interfaces.ReactiveId {
return this.#id;
}
get type(): interfaces.DescriptionType {
return this.#type;
}
get api(): string | interfaces.ApiDetails {
return this.#api;
}
get userFacing(): interfaces.Description {
if (this.#internal) {
return this.#internal.userFacing;
get #detailsPart(): interfaces.DetailsPart {
if (typeof this.#details === "string") {
return { type: "value", name: this.#details };
} else if (this.#details === undefined) {
return { type: "anonymous" };
} else {
return this;
return this.#details;
}
}
get isAnonymous(): boolean {
return this.#details === undefined;
get frame(): interfaces.StackFrame | undefined {
return this.#stack?.caller;
}
withId(id?: interfaces.ReactiveId): interfaces.Description {
if (id === undefined) {
return this;
}
return new DebugDescription(
id,
this.#type,
this.#api,
this.#details,
this.#internal,
this.#stack
);
}
withStack(
stack: Stack,
id: interfaces.ReactiveId | REUSE_ID
): interfaces.Description {
return new DebugDescription(
this.#newId(id),
this.#type,
this.#api,
this.#details,
this.#internal,
stack
);
}
get #parent(): interfaces.Description | void {
if (typeof this.#details === "object") {
return this.#details.parent;
}
}
asImplementation(options?: { reason: string }): interfaces.Description {
return new DebugDescription(
this.#id,
this.#type,
this.#api,
this.#details,
{
reason: options?.reason,
userFacing: this.#parent ?? this,
},
this.#stack
);
}
implementation(
id: interfaces.ReactiveId,
details?: {
reason: string;
userFacing: interfaces.Description;
stack?: Stack;
}
) {
return new DebugDescription(
this.#extendId(id),
this.#type,
this.#api,
this.#details,
{
reason: details?.reason,
userFacing: details?.userFacing ?? this.userFacing,
},
details?.stack ?? callerStack()
);
}
get fullName(): string {
if (this.#details !== undefined) {
if (typeof this.#details === "string") {
return this.#details;
} else {
return `${this.#details.parent.fullName}${this.fromUser}`;
}
} else {
return `{${this.#idName}:anonymous ${this.type}}`;
}
}
get fromUser(): string {

@@ -289,51 +181,85 @@ if (this.#details) {

method(
this: DebugDescription,
id: interfaces.ReactiveId | REUSE_ID,
name: string,
args?: interfaces.DescriptionArgument[]
): interfaces.Description {
return new DebugDescription(
this.#newId(id),
"formula",
this.#api,
{
type: "method",
parent: this,
name,
args,
},
undefined,
this.#stack
);
get fullName(): string {
if (this.#details !== undefined) {
if (typeof this.#details === "string") {
return this.#details;
} else {
return `${this.#details.parent.fullName}${this.fromUser}`;
}
} else {
return `{${this.#idName}:anonymous ${this.type}}`;
}
}
index(this: DebugDescription, index: number): interfaces.Description {
return new DebugDescription(
this.#extendId(index),
"formula",
this.#api,
{
type: "member",
parent: this,
kind: "index",
name: index,
},
undefined,
this.#stack
);
get id(): interfaces.ReactiveId {
return this.#id;
}
property(this: DebugDescription, name: string): interfaces.Description {
get #idName(): string {
if (typeof this.#id === "string" || typeof this.#id === "number") {
return String(this.#id);
} else {
return this.#id.map(String).join("/");
}
}
get isAnonymous(): boolean {
return this.#details === undefined;
}
get #parent(): interfaces.Description | undefined {
if (typeof this.#details === "object") {
return this.#details.parent;
}
}
get parts(): interfaces.DescriptionParts {
return {
type: this.#type,
id: this.#id,
api: this.#apiPart,
details: this.#detailsPart,
userFacing: this.#userFacingDesc,
frame: this.#stack?.caller,
stack: this.#stack?.stack,
internal: this.#internal,
};
}
get type(): interfaces.DescriptionType {
return this.#type;
}
get userFacing(): interfaces.Description {
if (this.#internal) {
return this.#internal.userFacing;
} else {
return this as interfaces.Description;
}
}
get #userFacingDesc(): interfaces.Description {
return (this.#internal?.userFacing ?? this) as interfaces.Description;
}
[Symbol.for("nodejs.util.inspect.custom")](): object {
return DisplayStruct("Description", {
id: this.#id,
type: this.#type,
api: this.#api,
details: this.#details,
internal: this.#internal,
stack: this.#stack,
});
}
asImplementation(options?: { reason: string }): interfaces.Description {
return new DebugDescription(
this.#extendId(name),
"formula",
this.#id,
this.#type,
this.#api,
this.#details,
{
type: "member",
parent: this,
kind: "property",
name,
reason: options?.reason,
userFacing: this.#parent ?? this,
},
undefined,
this.#stack

@@ -343,2 +269,25 @@ );

#caller(options?: interfaces.DescriptionDescribeOptions): string {
const caller = this.#stack?.caller;
if (caller !== undefined) {
return caller.display(options);
} else {
return "<unknown>";
}
}
describe(options: interfaces.DescriptionDescribeOptions = {}): string {
const name = this.#name(options);
if (this.#internal) {
const desc = this.#internal.reason
? chalk.dim(`[${this.#internal.reason}]`)
: chalk.dim("[internals]");
return `${name} ${desc}`;
} else {
return name;
}
}
detail(

@@ -380,2 +329,57 @@ this: DebugDescription,

#extendId(id: interfaces.ReactiveId | REUSE_ID): interfaces.ReactiveId {
if (id === REUSE_ID) {
return this.#id;
} else if (Array.isArray(this.#id)) {
return [...this.#id, id];
} else {
return [this.#id, id];
}
}
implementation(
id: interfaces.ReactiveId | symbol,
details?: {
reason?: string;
userFacing?: interfaces.Description;
stack?: Stack;
}
): DebugDescription {
if (typeof id === "symbol") {
verify(
id,
isEqual(REUSE_ID),
expected("symbol passed to implementation()").toBe("REUSE_ID")
);
}
return new DebugDescription(
this.#extendId(id),
this.#type,
this.#api,
this.#details,
{
reason: details?.reason,
userFacing: details?.userFacing ?? this.userFacing,
},
details?.stack ?? callerStack()
);
}
index(this: DebugDescription, index: number): interfaces.Description {
return new DebugDescription(
this.#extendId(index),
"formula",
this.#api,
{
type: "member",
parent: this,
kind: "index",
name: index,
},
undefined,
this.#stack
);
}
/**

@@ -408,47 +412,34 @@ * A key represents a string that the user wrote in their source code. In contrast, `detail`,

get parts(): interfaces.DescriptionParts {
return {
type: this.#type,
api: this.#apiPart,
details: this.#detailsPart,
userFacing: this.#internal ? this.#internal.userFacing : this,
frame: this.#stack?.caller,
stack: this.#stack?.stack,
internal: this.#internal,
};
}
get #apiPart(): interfaces.ApiDetails {
if (typeof this.#api === "string") {
return {
name: this.#api,
};
} else {
return this.#api;
method(
this: DebugDescription,
id: interfaces.ReactiveId | symbol,
name: string,
args?: interfaces.DescriptionArgument[] | undefined
): interfaces.Description {
if (typeof id === "symbol") {
verify(
id,
isEqual(REUSE_ID),
expected
.as("symbol passed to desc.method()")
.toBe("REUSE_ID")
.butGot((s) => String(s))
);
}
}
get #detailsPart(): interfaces.DetailsPart {
if (typeof this.#details === "string") {
return { type: "value", name: this.#details };
} else if (this.#details === undefined) {
return { type: "anonymous" };
} else {
return this.#details;
}
return new DebugDescription(
this.#newId(id),
"formula",
this.#api,
{
type: "method",
parent: this,
name,
args,
},
undefined,
this.#stack
);
}
describe(options: interfaces.DescriptionDescribeOptions = {}): string {
const name = this.#name(options);
if (this.#internal) {
const desc = this.#internal.reason
? chalk.dim(`[${this.#internal.reason}]`)
: chalk.dim("[internals]");
return `${name} ${desc}`;
} else {
return name;
}
}
#name(options: interfaces.DescriptionDescribeOptions): string {

@@ -462,14 +453,60 @@ if ((this.isAnonymous || options.source) ?? false) {

#caller(options?: interfaces.DescriptionDescribeOptions): string {
const caller = this.#stack?.caller;
#newId(id: interfaces.ReactiveId | REUSE_ID): interfaces.ReactiveId {
return id === REUSE_ID ? this.#id : id;
}
if (caller !== undefined) {
return caller.display(options);
} else {
return "<unknown>";
property(this: DebugDescription, name: string): interfaces.Description {
return new DebugDescription(
this.#extendId(name),
"formula",
this.#api,
{
type: "member",
parent: this,
kind: "property",
name,
},
undefined,
this.#stack
);
}
withId(id?: interfaces.ReactiveId): interfaces.Description {
if (id === undefined) {
return this as interfaces.Description;
}
return new DebugDescription(
id,
this.#type,
this.#api,
this.#details,
this.#internal,
this.#stack
) as interfaces.Description;
}
get frame(): interfaces.StackFrame | undefined {
return this.#stack?.caller;
withStack(
stack: Stack,
id: interfaces.ReactiveId | symbol
): interfaces.Description {
if (typeof id === "symbol") {
verify(
id,
isEqual(REUSE_ID),
expected
.as("symbol passed to withStack")
.toBe("REUSE_ID")
.butGot((s) => String(s))
);
}
return new DebugDescription(
this.#newId(id as REUSE_ID),
this.#type,
this.#api,
this.#details,
this.#internal,
stack
) as interfaces.Description;
}

@@ -481,22 +518,18 @@ }

class ProdDescription implements interfaces.Description {
static PROD = new ProdDescription();
readonly api = "";
static is(description: unknown): description is interfaces.Description {
return !!(description && description instanceof ProdDescription);
}
readonly frame: interfaces.StackFrame = {
starbeamCaller: undefined,
parts: () => new DisplayParts({ path: "" }),
link: () => "",
display: () => "",
};
static from(): interfaces.Description {
return ProdDescription.PROD;
}
readonly fromUser = undefined;
readonly fullName = "";
readonly id: interfaces.ReactiveId = "@starbeam:erased";
readonly fullName = "";
readonly type = "erased";
readonly api = "";
readonly fromUser = undefined;
readonly internal = undefined;
readonly stack = undefined;
readonly userFacing = this;
readonly parts: interfaces.DescriptionParts = {
api: { name: "erased" },
id: "erased",
details: { type: "anonymous" },

@@ -508,33 +541,47 @@ userFacing: this,

};
readonly frame: interfaces.StackFrame = {
parts: () => new DisplayParts({ path: "" }),
link: () => "",
display: () => "",
};
method(): interfaces.Description {
readonly stack = undefined;
readonly type = "erased";
readonly userFacing = this;
static PROD = new ProdDescription();
static from(): interfaces.Description {
return ProdDescription.PROD;
}
static is(description: unknown): description is interfaces.Description {
return !!(description && description instanceof ProdDescription);
}
asImplementation(): interfaces.Description {
return this;
}
index(): interfaces.Description {
describe(): string {
return "";
}
detail(): interfaces.Description {
return this;
}
property(): interfaces.Description {
implementation(): interfaces.Description {
return this;
}
key(): interfaces.Description {
index(): interfaces.Description {
return this;
}
detail(): interfaces.Description {
key(): interfaces.Description {
return this;
}
asImplementation(): interfaces.Description {
method(): interfaces.Description {
return this;
}
implementation(): interfaces.Description {
property(): interfaces.Description {
return this;

@@ -550,6 +597,2 @@ }

}
describe(): string {
return "";
}
}

@@ -556,0 +599,0 @@

@@ -5,2 +5,3 @@ import type { Stack } from "./stack.js";

let STACK: Stack | null = null;
const CALLER_FRAME = 1;

@@ -11,3 +12,3 @@ export function entryPoint<T>(callback: () => T): T {

try {
STACK = callerStack(1);
STACK = callerStack(CALLER_FRAME);
return callback();

@@ -14,0 +15,0 @@ } finally {

@@ -50,3 +50,3 @@ import { isObject } from "@starbeam/verify";

struct(fields: Fields, options?: DisplayStructOptions) {
struct(fields: Fields, options?: DisplayStructOptions): object {
return DisplayStruct(this.#name, fields, options);

@@ -74,3 +74,3 @@ }

return {
define: (inspector: (instance: I, debug: Debug) => unknown) => {
define: (callback: (instance: I, debug: Debug) => unknown) => {
Class.prototype[INSPECT] = function (

@@ -81,3 +81,3 @@ this: I,

) {
return inspector(this, Debug.create(name ?? Class.name, options));
return callback(this, Debug.create(name ?? Class.name, options));
};

@@ -84,0 +84,0 @@ },

@@ -21,7 +21,7 @@ export enum LogLevel {

log(arg: unknown, ...args: unknown[]): void {
log = (arg: unknown, ...args: unknown[]): void => {
if (this.#level >= this.#config.minimum) {
this.#logger.send(this.#level, { args: [arg, ...args] });
}
}
};

@@ -28,0 +28,0 @@ get withStack(): LoggerWithStack {

@@ -40,3 +40,3 @@ export class Style {

css(style: `${string}:${string}`): this {
const [property, value] = style.split(":");
const [property, value] = style.split(":") as [string, string];
this.#styles.add(property.trim(), value.trim());

@@ -134,7 +134,7 @@ return this;

class Buffer {
static styled() {
static styled(): Buffer {
return new Buffer(false);
}
static plain() {
static plain(): Buffer {
return new Buffer(true);

@@ -152,3 +152,3 @@ }

add(content: string, style?: string) {
add(content: string, style?: string): void {
if (style && !this.#plain) {

@@ -170,3 +170,3 @@ this.#current += `%c${content}`;

break() {
break(): void {
this.#message.push(this.#current);

@@ -173,0 +173,0 @@ this.#current = "";

@@ -0,4 +1,7 @@

import { isPresentString } from "@starbeam/core-utils";
import type * as interfaces from "@starbeam/interfaces";
import { hasType } from "@starbeam/verify";
import { inspector } from "./inspect/inspect-support.js";
export function describeModule(module: string): DescribedModule {

@@ -28,7 +31,18 @@ return new DescribedModule(parse(module));

export class DisplayParts implements interfaces.DisplayParts {
readonly #action: string | undefined;
readonly #loc: Loc | undefined;
readonly #path: string;
readonly #root: DisplayRoot | undefined;
readonly #action: string | undefined;
readonly #loc: Loc | undefined;
static {
inspector(this, "DisplayParts").define((parts, debug) =>
debug.struct({
path: parts.#path,
root: parts.#root,
action: parts.#action,
loc: parts.#loc,
})
);
}
constructor({

@@ -41,5 +55,5 @@ path,

path: string;
root?: DisplayRoot;
action?: string;
loc?: Loc;
root?: DisplayRoot | undefined;
action?: string | undefined;
loc?: Loc | undefined;
}) {

@@ -52,10 +66,2 @@ this.#path = path;

get path(): string {
return this.#path;
}
get root(): DisplayRoot | undefined {
return this.#root;
}
get action(): string | undefined {

@@ -65,7 +71,3 @@ return this.#action;

get loc(): Loc | undefined {
return this.#loc;
}
get #displayPath() {
get #displayPath(): string {
if (this.#root?.name) {

@@ -78,2 +80,14 @@ return `[${this.#root.name}]/${this.#path}`;

get loc(): Loc | undefined {
return this.#loc;
}
get path(): string {
return this.#path;
}
get root(): DisplayRoot | undefined {
return this.#root;
}
display(): string {

@@ -99,4 +113,11 @@ if (this.#loc && this.#action) {

display(
location?: { loc?: Loc | undefined; action?: string | undefined },
options: interfaces.StackFrameDisplayOptions = {}
): string {
return this.parts(location, options).display();
}
parts(
location?: { loc?: Loc; action?: string },
location?: { loc?: Loc | undefined; action?: string | undefined },
options: interfaces.StackFrameDisplayOptions = {}

@@ -113,3 +134,3 @@ ): DisplayParts {

const hasLoc = loc !== undefined;
const hasAction = action !== undefined && action.trim().length !== 0;
const hasAction = isPresentString(action);

@@ -126,9 +147,2 @@ if (hasLoc && hasAction) {

}
display(
location?: { loc?: Loc; action?: string },
options: interfaces.StackFrameDisplayOptions = {}
): string {
return this.parts(location, options).display();
}
}

@@ -138,6 +152,6 @@

line: number;
column?: number;
column?: number | undefined;
}
function formatLoc(loc: Loc) {
function formatLoc(loc: Loc): string {
if (loc.column === undefined) {

@@ -152,8 +166,9 @@ return `${loc.line}`;

// path(options?: StackFrameDisplayOptions): string;
parts(options?: interfaces.StackFrameDisplayOptions): DisplayPathParts;
parts: (options?: interfaces.StackFrameDisplayOptions) => DisplayPathParts;
}
class DescribedModulePath implements DescribedPath {
readonly #path: string;
readonly pkg = null;
readonly type = "relative";
readonly #path: string;

@@ -164,7 +179,3 @@ constructor(path: string) {

get pkg() {
return null;
}
[Symbol.for("nodejs.util.inspect.custom")]() {
[Symbol.for("nodejs.util.inspect.custom")](): string {
return `DescribedModulePath(${this.#path})`;

@@ -179,6 +190,6 @@ }

class DescribedPackage implements DescribedPath {
readonly type = "package";
readonly #scope: string | undefined;
readonly #name: string;
readonly #path: string;
readonly #scope: string | undefined;
readonly type = "package";

@@ -191,20 +202,2 @@ constructor(scope: string, name: string, path: string) {

[Symbol.for("nodejs.util.inspect.custom")]() {
const path = this.#path ? ` at ${this.#path}` : "";
if (this.#scope) {
return `DescribedPackage(${this.#scope}/${this.#name}${path})`;
} else {
return `DescribedPackage(${this.#name}${path})`;
}
}
get pkg() {
return normalize(this.#scope, this.#name);
}
parts(): DisplayPathParts {
return new DisplayPathParts({ path: this.#fullPath });
}
get #fullPath(): string {

@@ -225,2 +218,20 @@ const parts: string[] = [];

}
get pkg(): string {
return normalize(this.#scope, this.#name);
}
[Symbol.for("nodejs.util.inspect.custom")](): string {
const path = this.#path ? ` at ${this.#path}` : "";
if (this.#scope) {
return `DescribedPackage(${this.#scope}/${this.#name}${path})`;
} else {
return `DescribedPackage(${this.#name}${path})`;
}
}
parts(): DisplayPathParts {
return new DisplayPathParts({ path: this.#fullPath });
}
}

@@ -242,3 +253,7 @@

const { scope, name, path } = groups;
const { scope, name, path } = groups as {
scope: string;
name: string;
path: string;
};

@@ -271,4 +286,4 @@ return new DescribedPackage(scope, name, path);

const path = full.slice(root.length);
const relative = path.startsWith("/") ? path.slice(1) : path;
return new DisplayPathParts({ root: { prefix: root }, path: relative });
const rel = path.startsWith("/") ? path.slice("/".length) : path;
return new DisplayPathParts({ root: { prefix: root }, path: rel });
} else if (roots) {

@@ -278,6 +293,6 @@ for (const [key, value] of Object.entries(roots)) {

const path = full.slice(value.length);
const relative = path.startsWith("/") ? path.slice(1) : path;
const r = path.startsWith("/") ? path.slice("/".length) : path;
return new DisplayPathParts({
root: { name: key, prefix: value },
path: relative,
path: r,
});

@@ -284,0 +299,0 @@ }

/// <reference types="node" />
import { type UnknownFn, getFirst } from "@starbeam/core-utils";
import type {

@@ -10,5 +11,3 @@ DescriptionArgs,

StackFrameDisplayOptions,
// eslint-disable-next-line import/no-duplicates
} from "@starbeam/interfaces";
// eslint-disable-next-line import/no-duplicates
import type * as interfaces from "@starbeam/interfaces";

@@ -19,7 +18,9 @@ import { getID } from "@starbeam/shared";

// eslint-disable-next-line import/no-cycle
import { Description } from "./description/impl.js";
import { inspector } from "./inspect/inspect-support.js";
import { describeModule } from "./module.js";
type ErrorWithStack = Error & { stack: string };
interface ErrorWithStack extends Error {
stack: string;
}

@@ -29,25 +30,35 @@ export interface StackStatics {

create(this: void, internal?: number): StackProtocol;
fromStack(stack: string): StackProtocol;
create: (this: void, internal?: number) => StackProtocol;
fromStack: (stack: string) => StackProtocol;
from(error: ErrorWithStack): StackProtocol;
from(error: unknown): StackProtocol | null;
from: ((error: ErrorWithStack) => StackProtocol) &
((error: unknown) => StackProtocol | null);
id(
id: (
this: void,
description?: string | Description | { id: ReactiveId }
): ReactiveId;
) => ReactiveId;
description(
description: (
this: void,
args: DescriptionArgs & {
fromUser?: string | DescriptionDetails | interfaces.Description;
fromUser?:
| string
| DescriptionDetails
| interfaces.Description
| undefined;
},
internal?: number
): interfaces.Description;
) => interfaces.Description;
fromCaller(this: void, internal?: number): StackProtocol;
desc: (
type: interfaces.DescriptionType,
fromUser?: string | DescriptionDetails | interfaces.Description | undefined,
internal?: number | undefined
) => interfaces.Description;
replaceFrames(error: unknown, fromStack: StackProtocol): void;
fromCaller: (this: void, internal?: number) => StackProtocol;
replaceFrames: (error: unknown, fromStack: StackProtocol) => void;
/**

@@ -62,9 +73,9 @@ * Erase an abstraction from the call stack so the test error points at the user code, rather than

* not the direct call site from user code), you can specify an additional number of frames to erase
* using the `extra` parameter.
* using the `internal` parameter.
*/
entryPoint<T>(
entryPoint: <T>(
this: void,
callback: () => T,
options?: { extra?: number; stack?: StackProtocol }
): T;
options?: { internal?: number; stack?: StackProtocol }
) => T;
}

@@ -74,19 +85,24 @@

const MISSING = -1;
const START = 0;
if (import.meta.env.DEV) {
Error.stackTraceLimit = Infinity;
Error.stackTraceLimit = 1000;
class ParsedStack {
static empty() {
static empty(): ParsedStack {
return new ParsedStack("", "", "", []);
}
static parse({ stack }: { stack: string }) {
static parse({ stack }: { stack: string }): ParsedStack {
const parsed = new StackTracey(stack);
const frames = parsed.items;
if (frames.length === 0) {
const [firstFrame] = frames;
if (firstFrame === undefined) {
return new ParsedStack(stack, stack, "", []);
}
const first = frames[0].beforeParse;
const first = firstFrame.beforeParse;
const lines = stack.split("\n");

@@ -96,3 +112,3 @@

if (offset === -1) {
if (offset === MISSING) {
throw Error(

@@ -104,3 +120,3 @@ `An assumption was incorrect: A line that came from StackTracey cannot be found in the original trace.\n\n== Stack ==\n\n${stack}\n\n== Line ==\n\n${first}`

// the header is all of the lines before the offset
const header = lines.slice(0, offset).join("\n");
const header = lines.slice(START, offset).join("\n");
const rest = lines.slice(offset).join("\n");

@@ -116,2 +132,12 @@

static {
inspector(this, "ParsedStack").define((stack, debug) =>
debug.struct({
header: stack.#header,
frames: stack.#frames.map((f) => f.parts().display()),
rest: stack.#rest,
})
);
}
readonly #source: string;

@@ -134,11 +160,2 @@ readonly #header: string;

replaceFrames(stack: ParsedStack) {
return new ParsedStack(
this.#source,
this.#header,
stack.#rest,
stack.#frames
);
}
get header(): string {

@@ -155,6 +172,15 @@ return this.#header;

*/
get stack() {
get stack(): string {
return `${this.#header}\n${this.#rest}`;
}
replaceFrames(stack: ParsedStack): ParsedStack {
return new ParsedStack(
this.#source,
this.#header,
stack.#rest,
stack.#frames
);
}
slice(n: number): ParsedStack {

@@ -171,7 +197,17 @@ const rest = this.#rest.split("\n").slice(n).join("\n");

const REPLACED_ERRORS = new WeakSet<ErrorWithStack>();
const INITIAL_INTERNAL_FRAMES = 0;
const INITIAL_ENTRY_POINT_FRAMES = 1;
const CALLER = 1;
const ABSTRACTION_CALLER = 2;
class DebugStack implements StackProtocol {
static create(this: void, internal = 0): DebugStack {
if ("captureStackTrace" in Error) {
static EMPTY = new DebugStack(ParsedStack.empty());
static create(this: void, internal = INITIAL_INTERNAL_FRAMES): DebugStack {
const ErrorClass = Error;
if ("captureStackTrace" in ErrorClass) {
const err = {} as { stack: string };
Error.captureStackTrace(err, DebugStack.create);
ErrorClass.captureStackTrace(err, DebugStack.create);

@@ -184,3 +220,3 @@ return DebugStack.fromStack(err.stack).slice(internal);

return DebugStack.fromStack(verified(stack, hasType("string"))).slice(
internal + 1
internal + CALLER
);

@@ -190,5 +226,8 @@ }

static fromCaller(this: void, internal = 0): DebugStack {
static fromCaller(
this: void,
internal = INITIAL_INTERNAL_FRAMES
): DebugStack {
// Remove *this* `fromCaller` frame from the stack *and* the caller's frame
return DebugStack.create(internal + 2);
return DebugStack.create(internal + ABSTRACTION_CALLER);
}

@@ -206,6 +245,6 @@

*/
static replaceFrames(error: unknown, fromStack: DebugStack): void {
static replaceFrames(error: unknown, fromStack: Stack): void {
if (isErrorWithStack(error)) {
const errorStack = DebugStack.from(error);
errorStack.replaceFrames(fromStack);
errorStack.withReplacedFrames(fromStack as DebugStack);
error.stack = errorStack.stack;

@@ -225,13 +264,26 @@ }

static EMPTY = new DebugStack(ParsedStack.empty());
static description(
args: DescriptionArgs & {
fromUser?: string | DescriptionDetails | interfaces.Description;
fromUser?:
| string
| DescriptionDetails
| interfaces.Description
| undefined;
},
internal = 0
internal = INITIAL_INTERNAL_FRAMES
): interfaces.Description {
const stack = DebugStack.fromCaller(internal + 1);
const stack = DebugStack.fromCaller(internal + CALLER);
const fromUser = args.fromUser;
const api: string | interfaces.ApiDetails | undefined = args.api ?? {};
if (typeof api !== "string" && api.package === undefined) {
const starbeam = stack.caller?.starbeamCaller;
if (starbeam) {
api.package = starbeam.package;
api.name = starbeam.name;
}
}
if (fromUser === undefined || typeof fromUser === "string") {

@@ -246,2 +298,20 @@ return Description.from({ ...args, stack });

static desc(
type: interfaces.DescriptionType,
fromUser?:
| string
| DescriptionDetails
| interfaces.Description
| undefined,
internal = INITIAL_INTERNAL_FRAMES
): interfaces.Description {
return DebugStack.description(
{
type,
fromUser,
},
internal + CALLER
);
}
static id(

@@ -258,4 +328,6 @@ this: void,

static callerFrame(internal = 0): StackFrame | undefined {
return DebugStack.fromCaller(internal + 1).caller;
static callerFrame(
internal = INITIAL_INTERNAL_FRAMES
): StackFrame | undefined {
return DebugStack.fromCaller(internal + CALLER).caller;
}

@@ -266,5 +338,5 @@

{
extra = 0,
stack = DebugStack.create(1 + extra),
}: { extra?: number; stack?: StackProtocol } = {}
internal = INITIAL_ENTRY_POINT_FRAMES,
stack = DebugStack.create(CALLER + internal),
}: { internal?: number; stack?: StackProtocol } = {}
): T {

@@ -274,9 +346,8 @@ try {

} catch (e) {
if (isErrorWithStack(e)) {
if (isErrorWithStack(e) && !REPLACED_ERRORS.has(e)) {
const errorStack = DebugStack.from(e);
if (errorStack) {
errorStack.replaceFrames(stack as DebugStack);
e.stack = errorStack.stack;
}
const updated = errorStack.withReplacedFrames(stack as DebugStack);
e.stack = updated.stack;
REPLACED_ERRORS.add(e);
}

@@ -298,3 +369,3 @@ throw e;

get caller(): StackFrame | undefined {
return this.#parsed.entries[0];
return getFirst(this.#parsed.entries);
}

@@ -317,3 +388,3 @@

*/
replaceFrames(stack: DebugStack) {
withReplacedFrames(stack: DebugStack): DebugStack {
return new DebugStack(this.#parsed.replaceFrames(stack.#parsed));

@@ -323,3 +394,3 @@ }

slice(n: number): DebugStack {
if (n === 0) {
if (n === START) {
return this;

@@ -339,2 +410,8 @@ } else {

static {
inspector(this, "StackFrame").define((frame, debug) =>
debug.struct({ original: frame.parts().display() })
);
}
#stack: StackTracey;

@@ -354,10 +431,18 @@ #frame: StackTracey.Entry;

#reify(): StackTracey.Entry {
let reified = this.#reified;
get starbeamCaller():
| { package: string; name?: string | undefined }
| undefined {
const frame = this.#reify();
if (!reified) {
this.#reified = reified = this.#stack.withSource(this.#frame);
const pkg = /(@starbeam\/[/]+)/.exec(frame.file) as
| (RegExpExecArray & [string, string])
| undefined;
if (pkg) {
return {
// eslint-disable-next-line @typescript-eslint/no-magic-numbers
package: pkg[1],
name: frame.callee === "" ? undefined : frame.callee,
};
}
return reified;
}

@@ -369,3 +454,3 @@

get loc(): { line: number; column?: number } | undefined {
get loc(): { line: number; column?: number | undefined } | undefined {
const entry = this.#reify();

@@ -384,2 +469,12 @@

#reify(): StackTracey.Entry {
let reified = this.#reified;
if (!reified) {
this.#reified = reified = this.#stack.withSource(this.#frame);
}
return reified;
}
link(options?: StackFrameDisplayOptions): string {

@@ -437,3 +532,7 @@ if (options?.complete) {

args: DescriptionArgs & {
fromUser?: string | DescriptionDetails | interfaces.Description;
fromUser?:
| string
| DescriptionDetails
| interfaces.Description
| undefined;
}

@@ -444,2 +543,17 @@ ): interfaces.Description {

static desc(
type: interfaces.DescriptionType,
fromUser?:
| string
| DescriptionDetails
| interfaces.Description
| undefined
): interfaces.Description {
return ProdStack.description({
type,
stack: ProdStack.EMPTY,
fromUser,
});
}
static id(): ReactiveId {

@@ -472,4 +586,30 @@ return getID();

export function entryPointFn<F extends UnknownFn>(
fn: F,
options?: { stack: Stack }
): F {
return ((...args: unknown[]) =>
entryPoint(() => fn(...args), { internal: 1, ...options })) as F;
}
export function entryPoints<Funcs extends object>(
funcs: Funcs,
options?: { stack: Stack }
): Funcs {
const result = Object.create(null) as Record<string, UnknownFn>;
for (const [key, fn] of Object.entries(funcs) as [string, UnknownFn][]) {
if (typeof fn === "function") {
result[key] = entryPointFn(fn, options);
} else {
result[key] = fn;
}
}
return result as Funcs;
}
/** This should be convertable to something like Description.EMPTY in prod builds */
export const descriptionFrom = PickedStack.description;
export const Desc = PickedStack.desc;

@@ -481,3 +621,4 @@ /**

export const callerStack = PickedStack.fromCaller;
export const callerStack: (this: void, internal?: number) => StackProtocol =
PickedStack.fromCaller;

@@ -484,0 +625,0 @@ export function isErrorWithStack(error: unknown): error is ErrorWithStack {

@@ -15,3 +15,3 @@ import type {

interface ReactiveProtocolStatics {
dependencies(reactive: ReactiveProtocol): Iterable<MutableInternals>;
dependencies: (reactive: ReactiveProtocol) => Iterable<MutableInternals>;
}

@@ -45,9 +45,9 @@

get caller(): Stack {
return this.#data.caller;
}
get for(): I {
return this.#data.for;
}
get caller(): Stack {
return this.#data.caller;
}
}

@@ -69,5 +69,5 @@

export class FrameConsumeOperation extends LeafOperation<CompositeInternals> {
readonly type = "frame:consume";
readonly #diff: Diff<MutableInternals>;
readonly #frame: Frame;
readonly type = "frame:consume";

@@ -96,3 +96,3 @@ constructor(data: FrameConsumeInfo) {

#description: string;
#children: Set<DebugOperation> = new Set();
#children = new Set<DebugOperation>();
#parent: MutationLog | null;

@@ -123,3 +123,3 @@

readonly history: DebugOperation[];
for(reactive: ReactiveProtocol): readonly DebugOperation[];
for: (reactive: ReactiveProtocol) => readonly DebugOperation[];
}

@@ -165,9 +165,11 @@

const INITIAL_OFFSET = 0;
export class DebugTimeline {
static create(
timestamp: { now(): Timestamp },
statics: ReactiveProtocolStatics
): DebugTimeline {
return new DebugTimeline(timestamp, statics);
}
#timestamp: { now: () => Timestamp };
#statics: ReactiveProtocolStatics;
#trimOffset = INITIAL_OFFSET;
#operationList: DebugOperation[] = [];
#currentMutation: MutationLog | null = null;
#listeners = new Set<DebugListener>();

@@ -184,12 +186,4 @@ static Flush = class Flush {

static DebugListener = class DebugListener {
static offset(this: void, listener: DebugListener): number {
return listener.#offset;
}
static notify(this: void, listener: DebugListener): void {
listener.#notify();
}
#timeline: DebugTimeline;
#offset = 0;
#offset = INITIAL_OFFSET;
#filter: DebugFilter;

@@ -208,2 +202,17 @@ #notify: () => void;

static offset(this: void, listener: DebugListener): number {
return listener.#offset;
}
static create(
timestamp: { now: () => Timestamp },
statics: ReactiveProtocolStatics
): DebugTimeline {
return new DebugTimeline(timestamp, statics);
}
static notify(this: void, listener: DebugListener): void {
listener.#notify();
}
update(filter: DebugFilter): void {

@@ -229,11 +238,11 @@ this.#filter = filter;

#timestamp: { now(): Timestamp };
#statics: ReactiveProtocolStatics;
#trimOffset = 0;
#operationList: DebugOperation[] = [];
#currentMutation: MutationLog | null = null;
#listeners: Set<DebugListener> = new Set();
static create(
timestamp: { now: () => Timestamp },
statics: ReactiveProtocolStatics
): DebugTimeline {
return new DebugTimeline(timestamp, statics);
}
private constructor(
timestamp: { now(): Timestamp },
timestamp: { now: () => Timestamp },
statics: ReactiveProtocolStatics

@@ -245,2 +254,6 @@ ) {

get #end(): number {
return this.#trimOffset + this.#operationList.length;
}
notify(): void {

@@ -250,6 +263,2 @@ this.#listeners.forEach(DebugTimeline.DebugListener.notify);

get #end() {
return this.#trimOffset + this.#operationList.length;
}
attach(

@@ -283,3 +292,3 @@ notify: () => void,

#prune() {
#prune(): void {
const minOffset = Math.min(

@@ -294,3 +303,3 @@ ...[...this.#listeners].map(DebugTimeline.DebugListener.offset)

#add(operation: DebugOperation) {
#add(operation: DebugOperation): void {
if (this.#currentMutation) {

@@ -303,2 +312,12 @@ this.#currentMutation.add(operation);

#consumeCell(cell: MutableInternals, caller: Stack): void {
this.#add(
new CellConsumeOperation({
at: this.#timestamp.now(),
for: cell,
caller,
})
);
}
consumeCell(internals: MutableInternals, caller: Stack): void {

@@ -316,12 +335,2 @@ this.#consumeCell(internals, caller);

#consumeCell(cell: MutableInternals, caller: Stack) {
this.#add(
new CellConsumeOperation({
at: this.#timestamp.now(),
for: cell,
caller,
})
);
}
updateCell(cell: MutableInternals, caller: Stack): void {

@@ -337,3 +346,7 @@ this.#add(

#consumeFrame(frame: Frame, diff: Diff<MutableInternals>, caller: Stack) {
#consumeFrame(
frame: Frame,
diff: Diff<MutableInternals>,
caller: Stack
): void {
this.#add(

@@ -340,0 +353,0 @@ new FrameConsumeOperation({

@@ -0,1 +1,3 @@

import { getLastIndex } from "@starbeam/core-utils";
const VBAR = "│";

@@ -40,5 +42,7 @@ const NEXT = "├";

): string {
const lastIndex = getLastIndex(children);
return children
.map((child, index) => {
const isLast = index === children.length - 1;
const isLast = index === lastIndex;
return formatNode(child, { depth, isLast });

@@ -49,2 +53,4 @@ })

const ONE_DEEPER = 1;
function formatParent(

@@ -56,3 +62,5 @@ [label, ...children]: ParentNode,

return `${title}\n${formatChildren(children, { depth: depth + 1 })}`;
return `${title}\n${formatChildren(children, {
depth: depth + ONE_DEEPER,
})}`;
}

@@ -67,7 +75,7 @@

function indent(depth: number) {
function indent(depth: number): string {
return `${VBAR} `.repeat(depth);
}
function prefix({ depth, isLast }: { depth: number; isLast: boolean }) {
function prefix({ depth, isLast }: { depth: number; isLast: boolean }): string {
if (isLast) {

@@ -74,0 +82,0 @@ return `${indent(depth)}${LAST}`;

@@ -28,3 +28,3 @@ import { inspector } from "@starbeam/debug";

describe.skipIf(() => import.meta.env.PROD)("inspect utilities", () => {
describe.skipIf(() => !!import.meta.env.PROD)("inspect utilities", () => {
test("an installed DisplayStruct is used by util.inspect", async () => {

@@ -31,0 +31,0 @@ const util = await import("node:util");

@@ -7,16 +7,9 @@ {

"starbeam:type": "tests",
"scripts": {
"test:lint": "eslint",
"test:types": "tsc -b"
},
"dependencies": {
"@starbeam/debug": "workspace:^"
},
"publishConfig": {
"main": "dist/index.cjs",
"types": "dist/index.d.ts",
"exports": {
".": {
"types": "./dist/index.d.ts",
"import": "./dist/index.js",
"default": "./dist/index.cjs"
}
}
}
}
import { callerStack } from "@starbeam/debug";
import type { Stack } from "@starbeam/interfaces";
import { describe, expect, test } from "vitest";

@@ -8,3 +9,3 @@

function aFunction() {
function aFunction(): Stack {
return anOuterFunction();

@@ -20,8 +21,10 @@ }

function anOuterFunction() {
function anOuterFunction(): Stack {
return callerStack();
}
function callerStackInArgs(desc = callerStack().caller?.display()) {
function callerStackInArgs(
desc = callerStack().caller?.display()
): string | undefined {
return desc;
}

@@ -57,3 +57,3 @@ import { Tree } from "@starbeam/debug";

// if the first line is entirely whitespace, remove it
if (lines[0].trim() === "") {
if (lines[0]?.trim() === "") {
lines.shift();

@@ -63,3 +63,3 @@ }

// if the last line is entirely whitespace, remove it
if (lines[lines.length - 1].trim() === "") {
if (lines[lines.length - 1]?.trim() === "") {
lines.pop();

@@ -66,0 +66,0 @@ }

{
"extends": "../../../.config/tsconfig/tsconfig.-package.json",
"extends": "../../../../.config/tsconfig/tsconfig.shared.json",
"compilerOptions": {
"outDir": "../../../dist/packages",
"composite": true,
"declaration": true,
"declarationDir": "../../../../dist/types",
"declarationMap": true,
"declarationDir": "../../../dist/types",
"declaration": true,
"composite": true,
"types": ["../../env"]
}
"outDir": "../../../../dist/packages",
"types": ["../../../env"]
},
"exclude": ["dist/**/*"]
}
{
"extends": "../../.config/tsconfig/tsconfig.-package.json",
"extends": "../../../.config/tsconfig/tsconfig.shared.json",
"compilerOptions": {
"outDir": "../../dist/packages",
"composite": true,
"declaration": true,
"composite": true,
"declarationDir": "../../../dist/types",
"declarationMap": true,
"declarationDir": "../../dist/types",
"types": ["../env"]
}
"outDir": "../../../dist/packages",
"types": ["../../env"]
},
"exclude": ["dist/**/*"]
}

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc