// Generated by bynkc — do not edit by hand.
// bynk runtime — shared by every emitted module.

export type Result<T, E> =
  | { readonly tag: "Ok"; readonly value: T }
  | { readonly tag: "Err"; readonly error: E };

export const Ok = <T>(value: T): Result<T, never> => ({ tag: "Ok", value });
export const Err = <E>(error: E): Result<never, E> => ({ tag: "Err", error });

export type Option<T> =
  | { readonly tag: "Some"; readonly value: T }
  | { readonly tag: "None" };

export const Some = <T>(value: T): Option<T> => ({ tag: "Some", value });
export const None: Option<never> = { tag: "None" };

export interface ValidationError {
  readonly field: string;
  readonly message: string;
  readonly value: unknown;
}

export interface DurableObjectStorage {
  get<T>(key: string): Promise<T | undefined>;
  put(key: string, value: unknown): Promise<void>;
  delete(key: string): Promise<boolean>;
  list<T>(options?: { prefix?: string }): Promise<Map<string, T>>;
}

export interface DurableObjectState {
  readonly storage: DurableObjectStorage;
  readonly id: { readonly name: string };
}

export class InMemoryStorage implements DurableObjectStorage {
  private data = new Map<string, unknown>();

  async get<T>(key: string): Promise<T | undefined> {
    return this.data.get(key) as T | undefined;
  }

  async put(key: string, value: unknown): Promise<void> {
    this.data.set(key, value);
  }

  async delete(key: string): Promise<boolean> {
    return this.data.delete(key);
  }

  async list<T>(options?: { prefix?: string }): Promise<Map<string, T>> {
    const prefix = options?.prefix ?? "";
    const out = new Map<string, T>();
    for (const [k, v] of this.data) {
      if (k.startsWith(prefix)) out.set(k, v as T);
    }
    return out;
  }
}

export function makeTestState(name: string): DurableObjectState {
  return {
    storage: new InMemoryStorage(),
    id: { name },
  };
}

// v0.8: cross-Worker boundary protocol — JSON wire format and error types.

export type JsonValue =
  | null
  | boolean
  | number
  | string
  | JsonValue[]
  | { [k: string]: JsonValue };

export type BoundaryError =
  | { readonly kind: "MalformedJson"; readonly details: string }
  | {
      readonly kind: "StructuralMismatch";
      readonly path: string;
      readonly expected: string;
      readonly actual: string;
    }
  | {
      readonly kind: "RefinementViolation";
      readonly path: string;
      readonly violation: ValidationError;
    }
  | { readonly kind: "Transport"; readonly status: number; readonly details: string };

export function boundaryError(error: BoundaryError): Error {
  const e = new Error(`BoundaryError: ${error.kind}`);
  (e as any).boundaryError = error;
  return e;
}

export interface ServiceBinding {
  fetch(request: Request): Promise<Response>;
}

export async function callService<T, E>(
  binding: ServiceBinding,
  servicePath: string,
  argsJson: JsonValue,
  deserialiseResult: (json: JsonValue) => Result<Result<T, E>, BoundaryError>,
): Promise<Result<T, E>> {
  const request = new Request(`http://internal/_bynk/call/${servicePath}`, {
    method: "POST",
    headers: { "content-type": "application/json" },
    body: JSON.stringify(argsJson),
  });
  const response = await binding.fetch(request);
  if (!response.ok) {
    throw boundaryError({
      kind: "Transport",
      status: response.status,
      details: await response.text(),
    });
  }
  let responseJson: JsonValue;
  try {
    responseJson = (await response.json()) as JsonValue;
  } catch (e) {
    throw boundaryError({ kind: "MalformedJson", details: String(e) });
  }
  const result = deserialiseResult(responseJson);
  if (result.tag === "Err") {
    throw boundaryError(result.error);
  }
  return result.value;
}

// v0.9: HttpResult — the built-in HTTP-result sum.

export type HttpResult<T> =
  | { readonly tag: "Ok"; readonly value: T }
  | { readonly tag: "Created"; readonly value: T }
  | { readonly tag: "NoContent" }
  | { readonly tag: "BadRequest"; readonly message: string }
  | { readonly tag: "Unauthorized" }
  | { readonly tag: "Forbidden" }
  | { readonly tag: "NotFound" }
  | { readonly tag: "Conflict"; readonly message: string }
  | { readonly tag: "UnprocessableEntity"; readonly message: string }
  | { readonly tag: "ServerError"; readonly message: string };

export const HttpResult = {
  Ok: <T>(value: T): HttpResult<T> => ({ tag: "Ok", value }),
  Created: <T>(value: T): HttpResult<T> => ({ tag: "Created", value }),
  NoContent: { tag: "NoContent" } as HttpResult<never>,
  BadRequest: (message: string): HttpResult<never> => ({ tag: "BadRequest", message }),
  Unauthorized: { tag: "Unauthorized" } as HttpResult<never>,
  Forbidden: { tag: "Forbidden" } as HttpResult<never>,
  NotFound: { tag: "NotFound" } as HttpResult<never>,
  Conflict: (message: string): HttpResult<never> => ({ tag: "Conflict", message }),
  UnprocessableEntity: (message: string): HttpResult<never> => ({
    tag: "UnprocessableEntity",
    message,
  }),
  ServerError: (message: string): HttpResult<never> => ({ tag: "ServerError", message }),
};

// Match a path pattern (e.g., "/orders/:id") against a request path.
// Returns the captured parameter map, or null on no match.
export function matchPath(
  pattern: string,
  path: string,
): { params: Record<string, string> } | null {
  const patternSegments = pattern.split("/").filter(Boolean);
  const pathSegments = path.split("/").filter(Boolean);
  if (patternSegments.length !== pathSegments.length) return null;
  const params: Record<string, string> = {};
  for (let i = 0; i < patternSegments.length; i++) {
    const p = patternSegments[i];
    if (p.startsWith(":")) {
      params[p.slice(1)] = decodeURIComponent(pathSegments[i]);
    } else if (p !== pathSegments[i]) {
      return null;
    }
  }
  return { params };
}

// Serialise an HttpResult<T> to a Response. The variant determines the
// HTTP status code; the body (if any) is JSON-encoded.
export function httpResultToResponse<T>(
  result: HttpResult<T>,
  serialiseValue: (v: T) => JsonValue,
): Response {
  switch (result.tag) {
    case "Ok":
      return new Response(JSON.stringify(serialiseValue(result.value)), {
        status: 200,
        headers: { "content-type": "application/json" },
      });
    case "Created":
      return new Response(JSON.stringify(serialiseValue(result.value)), {
        status: 201,
        headers: { "content-type": "application/json" },
      });
    case "NoContent":
      return new Response(null, { status: 204 });
    case "BadRequest":
      return new Response(JSON.stringify({ error: result.message }), {
        status: 400,
        headers: { "content-type": "application/json" },
      });
    case "Unauthorized":
      return new Response(null, { status: 401 });
    case "Forbidden":
      return new Response(null, { status: 403 });
    case "NotFound":
      return new Response(null, { status: 404 });
    case "Conflict":
      return new Response(JSON.stringify({ error: result.message }), {
        status: 409,
        headers: { "content-type": "application/json" },
      });
    case "UnprocessableEntity":
      return new Response(JSON.stringify({ error: result.message }), {
        status: 422,
        headers: { "content-type": "application/json" },
      });
    case "ServerError":
      return new Response(JSON.stringify({ error: result.message }), {
        status: 500,
        headers: { "content-type": "application/json" },
      });
  }
}
