speckl/compiler/dist/parser.d.ts
Scott Scoble 04fa9844f4 fix(speckl): protobuf generator — List<T> in interfaces, enum prefix, transition type field, Date in interfaces (cycle 73)
Four defects found by dogfooding Speckl against the v0.3 product-layer
spec (FederatedMeetupProduct.speckdl, 74 members):

D1 — CRITICAL: List<T> fields in interface records emitted empty type
  Root cause: interface record fields called speckTypeToProto(f.type.name)
  which returns '' for List/Map/Set types (their .name is undefined).
  Event and state fields had inline List/Map dispatch, but interfaces did not.
  Fix: extracted typeExprToProto() shared function that handles all TypeExpr
  variants (primitive, ident, list, map, set). All four field-emitting code
  paths (interfaces, events, state, transitions) now use it.

D2 — MODERATE: Transition type discriminator field silently dropped
  Root cause: parser stores the type field in t.typeDiscriminator (string)
  rather than in t.fields array. Generator only emitted t.fields.
  Fix: emit typeDiscriminator as 'type' field at number 1, offset other
  fields by 1 to avoid collision.

D4 — LOW: Enum variant name collisions across enums in same proto scope
  Proto3 requires all enum values unique within enclosing scope, not just
  the enum. PENDING in Role and PENDING in OrderStatus collided.
  Fix: prefix all variants with upperSnake(enumName) — the proto3 convention.
  This also matches the handwritten proto's naming (TRANSITION_TYPE_*).

D5 — LOW: Date in interface fields did not trigger timestamp import
  The usesTimestamp check only scanned events and state, not interfaces.
  Fix: added interface member check to the timestamp import scan.

D3 (deferred): rc_ typo in spec source silently swallowed by parser.
  Parser bug, not generator bug. Filed for future cycle.

Verified: 56/56 tests pass, both FederatedMeetup.speckdl and
FederatedMeetupProduct.speckdl compile to protoc-clean output.
2026-06-29 12:12:40 -07:00

227 lines
No EOL
4.9 KiB
TypeScript

export interface SpeckNode {
type: 'speck';
name: string;
members: MemberNode[];
version?: string;
author?: string;
license?: string;
protoPackage?: string;
goPackage?: string;
eventSuffix?: string;
}
export interface ImportNode {
type: 'import';
path: string;
alias?: string;
version?: string;
hash?: string;
}
export interface InputNode {
type: 'input';
typeExpr: TypeExpr;
}
export interface OutputNode {
type: 'output';
typeExpr: TypeExpr;
}
export interface ConstraintNode {
type: 'constraint';
name?: string;
expr: string;
}
export interface VerifyNode {
type: 'verify';
name: string;
temporalExpr: string;
depth?: number;
}
export interface InterfaceField {
name: string;
type: TypeExpr;
}
export interface RpcMethod {
name: string;
requestType: string;
responseType: string;
clientStreaming?: boolean;
serverStreaming?: boolean;
}
export interface ServiceNode {
type: 'service';
name: string;
rpcs: RpcMethod[];
}
export interface InterfaceNode {
type: 'interface';
name: string;
kind: 'enum' | 'record' | 'service' | 'mixed';
methods: MethodSignature[];
fields: InterfaceField[];
}
export interface MethodSignature {
name: string;
params: {
name: string;
type: TypeExpr;
}[];
returnType: TypeExpr;
}
export interface EventNode {
type: 'event';
name: string;
fields: {
name: string;
type: TypeExpr;
}[];
}
export interface OneofNode {
type: 'oneof';
name: string;
variants: OneofVariant[];
}
export interface OneofVariant {
fieldName: string;
typeName: string;
}
export interface TransitionNode {
type: 'transition';
name: string;
fields: TransitionField[];
payloadOneof?: string;
typeDiscriminator?: string;
}
export interface TransitionField {
name: string;
type: TypeExpr;
}
export interface ProvenanceNode {
type: 'provenance';
clauses: ProvenanceClause[];
}
export interface ProvenanceClause {
type: 'regulation' | 'design_decision' | 'parent_spec' | 'external_doc';
value: string;
location?: string;
}
export interface ReviewNode {
type: 'review';
kind: 'manual' | 'auto' | 'hybrid';
}
export interface DerivesNode {
type: 'derives';
from: string;
via?: string;
}
export interface SatisfiesNode {
type: 'satisfies';
requirement: string;
clause?: string;
}
export interface AuthorNode {
type: 'author';
name: string;
email?: string;
}
export interface SourceNode {
type: 'source';
kind: 'conversation' | 'meeting' | 'document' | 'regulation' | 'architecture_review' | 'threat_model' | 'compliance_audit';
ref?: string;
}
export interface BOMNode {
type: 'bom';
compiler?: {
name: string;
version?: string;
};
solver?: {
name: string;
version?: string;
};
runtime?: {
name: string;
version?: string;
};
license?: string;
hash?: string;
}
export interface StateNode {
type: 'state';
variables: StateVar[];
messageName?: string;
}
export interface StateVar {
name: string;
typeExpr: TypeExpr;
defaultInit?: string;
}
export interface InitNode {
type: 'init';
assignments: {
name: string;
expr: string;
}[];
}
export interface ActionNode {
type: 'action';
name: string;
params: {
name: string;
type: TypeExpr;
}[];
statements: ActionStatement[];
}
export type ActionStatement = {
type: 'assign';
target: string;
expr: string;
} | {
type: 'let';
name: string;
expr: string;
} | {
type: 'require';
expr: string;
} | {
type: 'precondition';
expr: string;
} | {
type: 'postcondition';
expr: string;
} | {
type: 'emit';
event: string;
fields: {
name: string;
value: string;
}[];
} | {
type: 'return';
expr: string;
} | {
type: 'ifblock';
raw: string;
};
export type MemberNode = ImportNode | InputNode | OutputNode | ConstraintNode | VerifyNode | InterfaceNode | EventNode | OneofNode | TransitionNode | ProvenanceNode | ReviewNode | DerivesNode | SatisfiesNode | AuthorNode | SourceNode | BOMNode | StateNode | InitNode | ActionNode | ServiceNode;
export interface TypeExpr {
type: 'primitive' | 'record' | 'list' | 'set' | 'map' | 'ident';
name?: string;
fields?: {
name: string;
type: TypeExpr;
}[];
elementType?: TypeExpr;
keyType?: TypeExpr;
valueType?: TypeExpr;
}
export interface AST {
specks: SpeckNode[];
}
/**
* Parse a .speck file and return the AST
*/
export declare function parseSpeckFile(filePath: string): AST;
/**
* Parse SpeckDL content and return the AST
*/
export declare function parseSpeckContent(content: string, filePath?: string): AST;
//# sourceMappingURL=parser.d.ts.map