Scaffold monorepo dev setup
This commit is contained in:
commit
d2a09e095a
47 changed files with 1033 additions and 0 deletions
11
packages/config/package.json
Normal file
11
packages/config/package.json
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"name": "@islandflow/config",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"exports": {
|
||||
".": "./src/index.ts"
|
||||
},
|
||||
"dependencies": {
|
||||
"zod": "^3.23.8"
|
||||
}
|
||||
}
|
||||
34
packages/config/src/env.ts
Normal file
34
packages/config/src/env.ts
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
import { z } from "zod";
|
||||
|
||||
export class EnvError extends Error {
|
||||
readonly issues: z.ZodIssue[];
|
||||
|
||||
constructor(message: string, issues: z.ZodIssue[]) {
|
||||
super(message);
|
||||
this.name = "EnvError";
|
||||
this.issues = issues;
|
||||
}
|
||||
}
|
||||
|
||||
const formatIssues = (issues: z.ZodIssue[]): string => {
|
||||
return issues
|
||||
.map((issue) => {
|
||||
const path = issue.path.length > 0 ? issue.path.join(".") : "<root>";
|
||||
return `${path}: ${issue.message}`;
|
||||
})
|
||||
.join("; ");
|
||||
};
|
||||
|
||||
export const readEnv = <T extends z.ZodTypeAny>(
|
||||
schema: T,
|
||||
env: Record<string, string | undefined> = Bun.env
|
||||
): z.infer<T> => {
|
||||
const result = schema.safeParse(env);
|
||||
|
||||
if (!result.success) {
|
||||
const details = formatIssues(result.error.issues);
|
||||
throw new EnvError(`Invalid environment: ${details}`, result.error.issues);
|
||||
}
|
||||
|
||||
return result.data;
|
||||
};
|
||||
1
packages/config/src/index.ts
Normal file
1
packages/config/src/index.ts
Normal file
|
|
@ -0,0 +1 @@
|
|||
export * from "./env";
|
||||
7
packages/config/tsconfig.json
Normal file
7
packages/config/tsconfig.json
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"types": []
|
||||
},
|
||||
"include": ["src/**/*.ts"]
|
||||
}
|
||||
8
packages/observability/package.json
Normal file
8
packages/observability/package.json
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"name": "@islandflow/observability",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"exports": {
|
||||
".": "./src/index.ts"
|
||||
}
|
||||
}
|
||||
2
packages/observability/src/index.ts
Normal file
2
packages/observability/src/index.ts
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
export * from "./logger";
|
||||
export * from "./metrics";
|
||||
54
packages/observability/src/logger.ts
Normal file
54
packages/observability/src/logger.ts
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
export type LogLevel = "debug" | "info" | "warn" | "error";
|
||||
|
||||
export type LogContext = Record<string, unknown>;
|
||||
|
||||
export type LogRecord = LogContext & {
|
||||
level: LogLevel;
|
||||
service: string;
|
||||
msg: string;
|
||||
ts: string;
|
||||
};
|
||||
|
||||
export type LoggerFn = (msg: string, context?: LogContext) => void;
|
||||
|
||||
export type Logger = {
|
||||
debug: LoggerFn;
|
||||
info: LoggerFn;
|
||||
warn: LoggerFn;
|
||||
error: LoggerFn;
|
||||
};
|
||||
|
||||
export type LoggerOptions = {
|
||||
service: string;
|
||||
now?: () => string;
|
||||
sink?: (record: LogRecord) => void;
|
||||
};
|
||||
|
||||
const defaultSink = (record: LogRecord) => {
|
||||
console.log(JSON.stringify(record));
|
||||
};
|
||||
|
||||
export const createLogger = ({
|
||||
service,
|
||||
now = () => new Date().toISOString(),
|
||||
sink = defaultSink
|
||||
}: LoggerOptions): Logger => {
|
||||
const write = (level: LogLevel, msg: string, context?: LogContext) => {
|
||||
const record: LogRecord = {
|
||||
level,
|
||||
service,
|
||||
msg,
|
||||
ts: now(),
|
||||
...(context ?? {})
|
||||
};
|
||||
|
||||
sink(record);
|
||||
};
|
||||
|
||||
return {
|
||||
debug: (msg, context) => write("debug", msg, context),
|
||||
info: (msg, context) => write("info", msg, context),
|
||||
warn: (msg, context) => write("warn", msg, context),
|
||||
error: (msg, context) => write("error", msg, context)
|
||||
};
|
||||
};
|
||||
51
packages/observability/src/metrics.ts
Normal file
51
packages/observability/src/metrics.ts
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
export type MetricType = "counter" | "gauge" | "timing";
|
||||
|
||||
export type MetricTags = Record<string, string>;
|
||||
|
||||
export type MetricRecord = {
|
||||
name: string;
|
||||
type: MetricType;
|
||||
value: number;
|
||||
ts: number;
|
||||
service?: string;
|
||||
tags?: MetricTags;
|
||||
};
|
||||
|
||||
export type MetricsEmitter = (record: MetricRecord) => void;
|
||||
|
||||
export type MetricsOptions = {
|
||||
service?: string;
|
||||
emit?: MetricsEmitter;
|
||||
now?: () => number;
|
||||
};
|
||||
|
||||
export type Metrics = {
|
||||
count: (name: string, value?: number, tags?: MetricTags) => void;
|
||||
gauge: (name: string, value: number, tags?: MetricTags) => void;
|
||||
timing: (name: string, value: number, tags?: MetricTags) => void;
|
||||
};
|
||||
|
||||
const noopEmit: MetricsEmitter = () => {};
|
||||
|
||||
export const createMetrics = ({
|
||||
service,
|
||||
emit = noopEmit,
|
||||
now = () => Date.now()
|
||||
}: MetricsOptions = {}): Metrics => {
|
||||
const write = (type: MetricType, name: string, value: number, tags?: MetricTags) => {
|
||||
emit({
|
||||
name,
|
||||
type,
|
||||
value,
|
||||
tags,
|
||||
service,
|
||||
ts: now()
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
count: (name, value = 1, tags) => write("counter", name, value, tags),
|
||||
gauge: (name, value, tags) => write("gauge", name, value, tags),
|
||||
timing: (name, value, tags) => write("timing", name, value, tags)
|
||||
};
|
||||
};
|
||||
7
packages/observability/tsconfig.json
Normal file
7
packages/observability/tsconfig.json
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"types": []
|
||||
},
|
||||
"include": ["src/**/*.ts"]
|
||||
}
|
||||
11
packages/types/package.json
Normal file
11
packages/types/package.json
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
{
|
||||
"name": "@islandflow/types",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"exports": {
|
||||
".": "./src/index.ts"
|
||||
},
|
||||
"dependencies": {
|
||||
"zod": "^3.23.8"
|
||||
}
|
||||
}
|
||||
105
packages/types/src/events.ts
Normal file
105
packages/types/src/events.ts
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
import { z } from "zod";
|
||||
|
||||
export const EventMetaSchema = z.object({
|
||||
source_ts: z.number().int().nonnegative(),
|
||||
ingest_ts: z.number().int().nonnegative(),
|
||||
seq: z.number().int().nonnegative(),
|
||||
trace_id: z.string().min(1)
|
||||
});
|
||||
|
||||
export type EventMeta = z.infer<typeof EventMetaSchema>;
|
||||
|
||||
export const OptionPrintSchema = EventMetaSchema.merge(
|
||||
z.object({
|
||||
ts: z.number().int().nonnegative(),
|
||||
option_contract_id: z.string().min(1),
|
||||
price: z.number().nonnegative(),
|
||||
size: z.number().int().positive(),
|
||||
exchange: z.string().min(1),
|
||||
conditions: z.array(z.string().min(1)).optional()
|
||||
})
|
||||
);
|
||||
|
||||
export type OptionPrint = z.infer<typeof OptionPrintSchema>;
|
||||
|
||||
export const OptionNBBOSchema = EventMetaSchema.merge(
|
||||
z.object({
|
||||
ts: z.number().int().nonnegative(),
|
||||
option_contract_id: z.string().min(1),
|
||||
bid: z.number().nonnegative(),
|
||||
ask: z.number().nonnegative(),
|
||||
bidSize: z.number().int().nonnegative(),
|
||||
askSize: z.number().int().nonnegative()
|
||||
})
|
||||
);
|
||||
|
||||
export type OptionNBBO = z.infer<typeof OptionNBBOSchema>;
|
||||
|
||||
export const EquityPrintSchema = EventMetaSchema.merge(
|
||||
z.object({
|
||||
ts: z.number().int().nonnegative(),
|
||||
underlying_id: z.string().min(1),
|
||||
price: z.number().nonnegative(),
|
||||
size: z.number().int().positive(),
|
||||
exchange: z.string().min(1),
|
||||
offExchangeFlag: z.boolean()
|
||||
})
|
||||
);
|
||||
|
||||
export type EquityPrint = z.infer<typeof EquityPrintSchema>;
|
||||
|
||||
export const EquityQuoteSchema = EventMetaSchema.merge(
|
||||
z.object({
|
||||
ts: z.number().int().nonnegative(),
|
||||
underlying_id: z.string().min(1),
|
||||
bid: z.number().nonnegative(),
|
||||
ask: z.number().nonnegative()
|
||||
})
|
||||
);
|
||||
|
||||
export type EquityQuote = z.infer<typeof EquityQuoteSchema>;
|
||||
|
||||
export const FlowPacketSchema = EventMetaSchema.merge(
|
||||
z.object({
|
||||
id: z.string().min(1),
|
||||
members: z.array(z.string().min(1)),
|
||||
features: z.record(z.union([z.string(), z.number(), z.boolean()])),
|
||||
join_quality: z.record(z.number())
|
||||
})
|
||||
);
|
||||
|
||||
export type FlowPacket = z.infer<typeof FlowPacketSchema>;
|
||||
|
||||
export const ClassifierHitSchema = z.object({
|
||||
classifier_id: z.string().min(1),
|
||||
confidence: z.number().min(0).max(1),
|
||||
direction: z.string().min(1),
|
||||
explanations: z.array(z.string().min(1))
|
||||
});
|
||||
|
||||
export type ClassifierHit = z.infer<typeof ClassifierHitSchema>;
|
||||
|
||||
export const ClassifierHitEventSchema = EventMetaSchema.merge(ClassifierHitSchema);
|
||||
|
||||
export type ClassifierHitEvent = z.infer<typeof ClassifierHitEventSchema>;
|
||||
|
||||
export const AlertEventSchema = EventMetaSchema.merge(
|
||||
z.object({
|
||||
score: z.number(),
|
||||
severity: z.string().min(1),
|
||||
hits: z.array(ClassifierHitSchema),
|
||||
evidence_refs: z.array(z.string().min(1))
|
||||
})
|
||||
);
|
||||
|
||||
export type AlertEvent = z.infer<typeof AlertEventSchema>;
|
||||
|
||||
export const InferredDarkEventSchema = EventMetaSchema.merge(
|
||||
z.object({
|
||||
type: z.string().min(1),
|
||||
confidence: z.number().min(0).max(1),
|
||||
evidence_refs: z.array(z.string().min(1))
|
||||
})
|
||||
);
|
||||
|
||||
export type InferredDarkEvent = z.infer<typeof InferredDarkEventSchema>;
|
||||
1
packages/types/src/index.ts
Normal file
1
packages/types/src/index.ts
Normal file
|
|
@ -0,0 +1 @@
|
|||
export * from "./events";
|
||||
7
packages/types/tsconfig.json
Normal file
7
packages/types/tsconfig.json
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"compilerOptions": {
|
||||
"types": []
|
||||
},
|
||||
"include": ["src/**/*.ts"]
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue