重构tracer设计

This commit is contained in:
2026-02-04 01:07:15 +08:00
parent 0ba82716dc
commit 8d5af5876f
21 changed files with 408 additions and 225 deletions

View File

@@ -1,26 +0,0 @@
import type { TracerCommand } from '../types/command';
export class Commander {
private commands: TracerCommand<unknown>[];
private constructor() {
this.commands = [];
}
private static instance: Commander | null = null;
public static getInstance(): Commander {
if (!this.instance) {
this.instance = new Commander();
}
return this.instance;
}
public command<T = never>(command: TracerCommand<T>) {
this.commands.push(command);
}
public output() {
return JSON.stringify(this.commands);
}
}

View File

@@ -0,0 +1 @@
export * from './tracer-context';

View File

@@ -0,0 +1,22 @@
import type { TracerCommand } from '../types';
const createTracerContext = () => {
const commands: TracerCommand[] = [];
const getTracerContext = () => {
const command = (command: TracerCommand) => {
commands.push(command);
};
return {
commands,
command,
};
};
return {
getTracerContext,
};
};
export const { getTracerContext } = createTracerContext();

View File

@@ -1,10 +1,20 @@
import { Commander } from './commander';
import { ArrayTracer } from './tracer';
import { getTracerContext } from './context';
import {
createArrayTracer,
createControlTracer,
createLogTracer,
} from './tracers';
const arrayTracer = ArrayTracer.define<number>({ description: 'Array Tracer' });
const logTracer = createLogTracer({ description: 'LogTracer' });
const controlTracer = createControlTracer({ description: 'ControlTracer' });
arrayTracer.preset([1, 2, 3]);
const arrayTracer = createArrayTracer<number>({
description: 'ArrayTracer',
array: [1, 2, 3],
});
const commander = Commander.getInstance();
const commands = commander.output();
console.log(commands);
arrayTracer.patch(0, 100);
controlTracer.step();
console.log(getTracerContext().commands);

View File

@@ -1,25 +0,0 @@
import type { TracerType } from "../types/tracer";
export class Registry {
private tracers: Map<TracerType, string[]>;
private constructor() {
this.tracers = new Map<TracerType, string[]>();
}
private static instance: Registry | null = null;
public static getInstance() {
if (!this.instance) {
this.instance = new Registry();
}
return this.instance;
}
public register(tracerType: TracerType, string: string) {
if (!this.tracers.has(tracerType)) {
this.tracers.set(tracerType, []);
}
this.tracers.get(tracerType)?.push(string);
}
}

View File

@@ -1,94 +0,0 @@
import { Commander } from '../commander';
import { Registry } from '../registry';
export interface ArrayTracerOption<T> {
description: string;
}
export class ArrayTracer<T> {
private tracerId: string;
private constructor() {
this.tracerId = crypto.randomUUID();
}
public static define<T>(option: ArrayTracerOption<T>): ArrayTracer<T> {
const arrayTracer = new ArrayTracer();
const registry = Registry.getInstance();
registry.register('ArrayTracer', arrayTracer.tracerId);
const commander = Commander.getInstance();
commander.command({
action: 'define',
tracer: null,
payload: {
type: 'ArrayTracer',
id: arrayTracer.tracerId,
desc: option.description,
},
});
return arrayTracer;
}
public preset(array: T[]) {
const commander = Commander.getInstance();
commander.command({
action: 'preset',
tracer: this.tracerId,
payload: array,
});
}
public extend(size: number) {
const commander = Commander.getInstance();
commander.command({
action: 'extend',
tracer: this.tracerId,
payload: { size },
});
}
public shrink(size: number) {
const commander = Commander.getInstance();
commander.command({
action: 'shrink',
tracer: this.tracerId,
payload: { size },
});
}
public pick(index: number) {
const commander = Commander.getInstance();
commander.command({
action: 'pick',
tracer: this.tracerId,
payload: { index },
});
}
public drop(index: number) {
const commander = Commander.getInstance();
commander.command({
action: 'drop',
tracer: this.tracerId,
payload: { index },
});
}
public patch(index: number, value: T) {
const commander = Commander.getInstance();
commander.command({
action: 'patch',
tracer: this.tracerId,
payload: { index, value },
});
}
public reset(index: number) {
const commander = Commander.getInstance();
commander.command({
action: 'reset',
tracer: this.tracerId,
payload: { index },
});
}
}

View File

@@ -1 +0,0 @@
export * from "./array-tracer";

View File

@@ -0,0 +1,102 @@
import { getTracerContext } from '../context';
import type { JsonValue } from '../types';
interface ArrayTracerCreateOptions<T extends JsonValue> {
description?: string;
array?: T[];
}
export const createArrayTracer = <T extends JsonValue>(
options: ArrayTracerCreateOptions<T>,
) => {
const { description = 'ArrayTracer', array } = options;
const tracer = crypto.randomUUID();
const { command } = getTracerContext();
command({
type: 'ArrayTracer',
tracer: tracer,
action: 'create',
params: {
description: description,
array: array,
},
});
const preset = (array: T[]) => {
command({
type: 'ArrayTracer',
tracer: tracer,
action: 'preset',
params: {
array: array,
},
});
};
const scale = (size: number) => {
command({
type: 'ArrayTracer',
tracer: tracer,
action: 'scale',
params: {
size: size,
},
});
};
const pick = (index: number) => {
command({
type: 'ArrayTracer',
tracer: tracer,
action: 'pick',
params: {
index: index,
},
});
};
const drop = (index: number) => {
command({
type: 'ArrayTracer',
tracer: tracer,
action: 'drop',
params: {
index: index,
},
});
};
const patch = (index: number, value: T) => {
command({
type: 'ArrayTracer',
tracer: tracer,
action: 'patch',
params: {
index: index,
value: value,
},
});
};
const unset = (index: number) => {
command({
type: 'ArrayTracer',
tracer: tracer,
action: 'unset',
params: {
index: index,
},
});
};
return {
// preset,
scale,
pick,
drop,
patch,
unset,
};
};

View File

@@ -0,0 +1,81 @@
import { getTracerContext } from '../context';
export const getLocation = (targetFn?: Function) => {
const stackObject: { stack: string } = { stack: '' };
Error.captureStackTrace(stackObject, targetFn ?? getLocation);
const { stack } = stackObject;
// D:\Projects\structrail-sdk\tracers.ts\src\index.ts:23:18
const location = stack.split('\n').at(1)?.trim().split(' ').at(1)?.trim();
// console.log(location);
if (!location) return null;
const matches = location?.match(/:(\d+):?(\d+)?\)?$/);
// console.log(matches);
const file = location?.replace(matches?.at(0) ?? '', '');
// console.log(file);
const line = parseInt(matches?.at(1) ?? '0');
// console.log(line);
return {
file,
line,
};
};
interface ControlTracerCreateOptions {
description?: string;
}
export const createControlTracer = (options: ControlTracerCreateOptions) => {
const { description = 'ControlTracer' } = options;
const tracer = crypto.randomUUID();
const { command } = getTracerContext();
command({
type: 'ControlTracer',
tracer: tracer,
action: 'create',
params: {
description: description,
},
});
const step = (...range: (number | [number, number])[]) => {
const { line: currentLine } = getLocation(step) ?? {};
if (!currentLine) return;
// console.log(currentLine);
const linesSet = new Set<number>([currentLine]);
range.forEach((item) => {
if (Array.isArray(item) && item.length === 2) {
const [offsetStart, offsetEnd] = item;
const lineStart = currentLine - offsetStart;
const lineEnd = currentLine - offsetEnd;
const lineMin = Math.min(lineStart, lineEnd);
const lineMax = Math.max(lineStart, lineEnd);
for (let line = lineMin; line <= lineMax; line++) {
linesSet.add(line);
}
}
if (typeof item === 'number') {
const line = item;
linesSet.add(currentLine - line);
}
});
const lines = Array.from(linesSet).sort((line1, line2) => line1 - line2);
command({
type: 'ControlTracer',
tracer: tracer,
action: 'step',
params: {
lines: lines,
},
});
};
return {
step,
};
};

View File

@@ -0,0 +1,3 @@
export * from './array-tracer';
export * from './control-tracer';
export * from './log-tracer';

View File

@@ -0,0 +1,44 @@
import { getTracerContext } from '../context';
interface LogTracerCreateOptions {
description?: string;
}
export const createLogTracer = (options: LogTracerCreateOptions) => {
const { description = 'LogTracer' } = options;
const tracer = crypto.randomUUID();
const { command } = getTracerContext();
command({
type: 'LogTracer',
tracer: tracer,
action: 'create',
params: {
description: description,
},
});
const log = (...args: unknown[]) => {
const parsed = args.map((arg) => {
if (typeof arg === 'string') {
return arg;
}
return JSON.stringify(arg);
});
const message = parsed.join(' ');
command({
type: 'LogTracer',
tracer: tracer,
action: 'log',
params: {
message: message,
},
});
};
return {
log,
};
};

View File

@@ -1,5 +0,0 @@
import type { ArrayTracerCommand } from '../command';
export type ArrayTracerAction = ArrayTracerCommand['action'];
export type TracerAction = ArrayTracerAction;

View File

@@ -0,0 +1,65 @@
import type { BaseTracerCommand, JsonValue } from './common';
type BaseArrayTracerCommand = BaseTracerCommand & {
type: 'ArrayTracer';
};
type ArrayTracerCreateCommand = BaseArrayTracerCommand & {
action: 'create';
params: {
description: string;
array?: JsonValue[];
};
};
type ArrayTracerPresetCommand = BaseArrayTracerCommand & {
action: 'preset';
params: {
array: JsonValue[];
};
};
type ArrayTracerScaleCommand = BaseArrayTracerCommand & {
action: 'scale';
params: {
size: number;
};
};
type ArrayTracerPickCommand = BaseArrayTracerCommand & {
action: 'pick';
params: {
index: number;
};
};
type ArrayTracerDropCommand = BaseArrayTracerCommand & {
action: 'drop';
params: {
index: number;
};
};
type ArrayTracerPatchCommand = BaseArrayTracerCommand & {
action: 'patch';
params: {
index: number;
value: JsonValue;
};
};
type ArrayTracerUnsetCommand = BaseArrayTracerCommand & {
action: 'unset';
params: {
index: number;
};
};
export type ArrayTracerCommand =
| ArrayTracerCreateCommand
| ArrayTracerPresetCommand
| ArrayTracerScaleCommand
| ArrayTracerPickCommand
| ArrayTracerDropCommand
| ArrayTracerPatchCommand
| ArrayTracerUnsetCommand;

View File

@@ -0,0 +1,8 @@
import type { ArrayTracerCommand } from './array-tracer';
import type { ControlTracerCommand } from './control-tracer';
import type { LogTracerCommand } from './log-tracer';
export type TracerCommand =
| ArrayTracerCommand
| LogTracerCommand
| ControlTracerCommand;

View File

@@ -1,57 +0,0 @@
export type ArrayTracerDefineCommand = {
action: 'define';
tracer: null;
payload: { type: 'ArrayTracer'; id: string; desc?: string };
};
export type ArrayTracerPresetCommand<T = unknown> = {
action: 'preset';
tracer: string;
payload: T[];
};
export type ArrayTracerExtendCommand = {
action: 'extend';
tracer: string;
payload: { size: number };
};
export type ArrayTracerShrinkCommand = {
action: 'shrink';
tracer: string;
payload: { size: number };
};
export type ArrayTracerPickCommand = {
action: 'pick';
tracer: string;
payload: { index: number };
};
export type ArrayTracerDropCommand = {
action: 'drop';
tracer: string;
payload: { index: number };
};
export type ArrayTracerPatchCommand<T = unknown> = {
action: 'patch';
tracer: string;
payload: { index: number; value: T };
};
export type ArrayTracerResetCommand = {
action: 'reset';
tracer: string;
payload: { index: number };
};
export type ArrayTracerCommand<T = never> =
| ArrayTracerDefineCommand
| ArrayTracerPresetCommand<T>
| ArrayTracerExtendCommand
| ArrayTracerShrinkCommand
| ArrayTracerPickCommand
| ArrayTracerDropCommand
| ArrayTracerPatchCommand<T>
| ArrayTracerResetCommand;

View File

@@ -1,5 +0,0 @@
import type { ArrayTracerCommand } from './array-tracer-command';
export * from './array-tracer-command';
export type TracerCommand<T = never> = ArrayTracerCommand<T>;

View File

@@ -0,0 +1,16 @@
export type JsonValue =
| string
| number
| boolean
| null
| JsonValue[]
| { [key: string]: JsonValue };
export type TracerType = 'ArrayTracer' | 'LogTracer' | 'ControlTracer';
export type TracerId = ReturnType<typeof crypto.randomUUID>;
export type BaseTracerCommand = {
type: TracerType;
tracer: TracerId;
};

View File

@@ -0,0 +1,23 @@
import type { BaseTracerCommand } from './common';
type BaseControlTracerCommand = BaseTracerCommand & {
type: 'ControlTracer';
};
type ControlTracerCreateCommand = BaseControlTracerCommand & {
action: 'create';
params: {
description: string;
};
};
type ControlTracerStepCommand = BaseControlTracerCommand & {
action: 'step';
params: {
lines: number[];
};
};
export type ControlTracerCommand =
| ControlTracerCreateCommand
| ControlTracerStepCommand;

View File

@@ -0,0 +1,5 @@
export * from './array-tracer';
export * from './command';
export * from './common';
export * from './control-tracer';
export * from './log-tracer';

View File

@@ -0,0 +1,21 @@
import type { BaseTracerCommand } from "./common";
type BaseLogTracerCommand = BaseTracerCommand & {
type: 'LogTracer';
};
type LogTracerCreateCommand = BaseLogTracerCommand & {
action: 'create';
params: {
description: string;
};
};
type LogTracerLogCommand = BaseLogTracerCommand & {
action: 'log';
params: {
message: string;
};
};
export type LogTracerCommand = LogTracerCreateCommand | LogTracerLogCommand;

View File

@@ -1,5 +0,0 @@
import type { ArrayTracerDefineCommand } from '../command';
type ArrayTracer = ArrayTracerDefineCommand['payload']['type'];
export type TracerType = ArrayTracer;