@fungi.computer/braid 0.2.4 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4 +1,4 @@
1
- import { z } from "zod";
1
+ import * as z from "zod";
2
2
  import type { WorkflowRunMember } from "./run-authority.js";
3
3
  /** Host-captured provenance from the actual connector or approved-policy owner. */
4
4
  export declare const workflowArmedViaSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
@@ -1,4 +1,4 @@
1
- import { z } from "zod";
1
+ import * as z from "zod";
2
2
  import { decodeWorkflowRunMember } from "./run-member.js";
3
3
  const identity = z.string().min(1).max(256);
4
4
  const membershipVersion = z.lazy(() => z.number().int().nonnegative());
@@ -1,4 +1,4 @@
1
- import { z } from "zod";
1
+ import * as z from "zod";
2
2
  import type { GitWriter } from "@fungi.computer/agar/git";
3
3
  /** One credential grammar for both hosts; the owner is public, the token is not. */
4
4
  export declare const workflowGitPassPrefix = "wgp.";
@@ -1,4 +1,4 @@
1
- import { z } from "zod";
1
+ import * as z from "zod";
2
2
  import { workflowAttestationSchema } from "@fungi.computer/puffball/app-method-runtime";
3
3
  import { gitAccessScope, resolvedGitAccessSchema } from "../git-access.js";
4
4
  const id = z.string().min(1).max(256);
@@ -1,5 +1,5 @@
1
1
  import { repositoryRefsEqual, } from "@fungi.computer/agar/git";
2
- import { z } from "zod";
2
+ import * as z from "zod";
3
3
  import { canonicalJson } from "../index.js";
4
4
  import { formatWorkflowGitPassToken, workflowGitPassTokenSchema, workflowGitPassGrantSchema, workflowGitPassRevocationSchema, } from "./git-pass-contract.js";
5
5
  const invocationIdSchema = z.string().min(1).max(256);
@@ -1,4 +1,4 @@
1
- import { z } from "zod";
1
+ import * as z from "zod";
2
2
  import * as S from "effect/Schema";
3
3
  import { type ExecutionPlan, type Issue } from "../index.js";
4
4
  import type { WorkflowCatalogEntry } from "./ports.js";
@@ -1,4 +1,4 @@
1
- import { z } from "zod";
1
+ import * as z from "zod";
2
2
  import { bindingsSelectionSchema, workflowBindingSchema, } from "../git-access.js";
3
3
  import { gitSha1Schema } from "@fungi.computer/puffball/git-representation";
4
4
  import { sha256DigestSchema } from "@fungi.computer/puffball/effect";
@@ -1,6 +1,6 @@
1
1
  import * as S from "effect/Schema";
2
2
  import { type NodeEvent, type NodeAdmissionResult } from "../execution.js";
3
- import { z } from "zod";
3
+ import * as z from "zod";
4
4
  import type { WatchdogSqliteOwner } from "@fungi.computer/watchdog";
5
5
  import { type WorkflowRunAuthority } from "./run-authority.js";
6
6
  import type { ArmedWorkflowTrigger } from "./trigger-owner.js";
@@ -1584,7 +1584,7 @@ export declare function createTeamWorkflowRuns(options: {
1584
1584
  revokeReleaseInMutation(appIds: readonly string[]): void;
1585
1585
  /** The kernel's lifecycle caller owns the transaction. Suspension ends every run. */
1586
1586
  onLifecycle(status: "active" | "suspended"): void;
1587
- reserveWebhookDelivery(raw: z.input<z.ZodObject<{
1587
+ reserveWebhookDelivery(raw: z.core.input<z.ZodObject<{
1588
1588
  scope: z.ZodLazy<z.ZodObject<{
1589
1589
  installationId: z.ZodString;
1590
1590
  workflowId: z.ZodString;
@@ -1610,7 +1610,7 @@ export declare function createTeamWorkflowRuns(options: {
1610
1610
  kind: "refused";
1611
1611
  reason: "limit";
1612
1612
  };
1613
- pendingWebhookDeliveries(rawOwner: z.input<z.ZodObject<{
1613
+ pendingWebhookDeliveries(rawOwner: z.core.input<z.ZodObject<{
1614
1614
  host: z.ZodString;
1615
1615
  tenant: z.ZodString;
1616
1616
  }, z.core.$strict>>): ({
@@ -1639,7 +1639,7 @@ export declare function createTeamWorkflowRuns(options: {
1639
1639
  reservationId: string;
1640
1640
  runId?: never;
1641
1641
  })[];
1642
- nextWebhookWake(rawOwner: z.input<z.ZodObject<{
1642
+ nextWebhookWake(rawOwner: z.core.input<z.ZodObject<{
1643
1643
  host: z.ZodString;
1644
1644
  tenant: z.ZodString;
1645
1645
  }, z.core.$strict>>): number | null;
@@ -1,7 +1,7 @@
1
1
  import * as S from "effect/Schema";
2
2
  import * as Struct from "effect/Struct";
3
3
  import { nodeEventSchema, } from "../execution.js";
4
- import { z } from "zod";
4
+ import * as z from "zod";
5
5
  import { bindingsSelectionSchema, workflowBindingSchema, } from "../git-access.js";
6
6
  import { canonicalJson, contentDigest } from "../index.js";
7
7
  import { createTriggerPlans } from "./trigger-plans.js";
@@ -1,5 +1,12 @@
1
+ import * as z from "zod";
1
2
  import type { WatchdogSqliteOwner } from "@fungi.computer/watchdog";
2
3
  import { type WorkflowArmer, type WorkflowInstallationAuthority, type WorkflowTriggerPrincipal, type WorkflowTriggerTarget, type createTeamWorkflowTriggers } from "./trigger-owner.js";
4
+ /** Installation-scoped keyset read; the default and ceiling bound one reply. */
5
+ export declare const workflowScheduleListInputSchema: z.ZodObject<{
6
+ limit: z.ZodDefault<z.ZodNumber>;
7
+ after: z.ZodOptional<z.ZodString>;
8
+ }, z.core.$strict>;
9
+ export type WorkflowScheduleListInput = z.input<typeof workflowScheduleListInputSchema>;
3
10
  /** Team-owned schedule records; Almanac alone decides occurrences and transitions. */
4
11
  export declare function createWorkflowSchedules(options: {
5
12
  sql: WatchdogSqliteOwner["sql"];
@@ -22,15 +29,24 @@ export declare function createWorkflowSchedules(options: {
22
29
  kind: "armed";
23
30
  scheduleId: string;
24
31
  }>;
25
- list(principal: WorkflowTriggerPrincipal, installationId: string): Promise<{
26
- scheduleId: string;
27
- installationId: string;
28
- workflowId: string;
29
- triggerId: string;
30
- nextDueAt: number;
31
- state: "active" | "finished" | "paused" | "cancelled";
32
- lastError: string | null;
33
- }[]>;
32
+ list(principal: WorkflowTriggerPrincipal, installationId: string, input: WorkflowScheduleListInput): Promise<{
33
+ schedules: {
34
+ scheduleId: string;
35
+ installationId: string;
36
+ workflowId: string;
37
+ triggerId: string;
38
+ nextDueAt: number;
39
+ state: "active" | "finished" | "paused" | "cancelled";
40
+ lastError: string | null;
41
+ }[];
42
+ nextCursor: string | null;
43
+ }>;
44
+ /** Pause future admission only; accepted runs and the compiled plan remain owned. */
45
+ pause(principal: WorkflowTriggerPrincipal, installationId: string, scheduleId: string): Promise<{
46
+ kind: "not_found";
47
+ } | {
48
+ kind: "paused";
49
+ }>;
34
50
  disarm(principal: WorkflowTriggerPrincipal, installationId: string, scheduleId: string): Promise<{
35
51
  kind: "not_found";
36
52
  } | {
@@ -1,9 +1,14 @@
1
- import { z } from "zod";
1
+ import * as z from "zod";
2
2
  import * as Result from "effect/Result";
3
3
  import { afterAdmitted, afterRefused, isDue, nextOccurrence, occurrenceKey, paused, cancelled, } from "@fungi.computer/almanac";
4
4
  import { canonicalJson, contentDigest } from "../index.js";
5
5
  import { armedWorkflowTriggerSchema, workflowArmerKey, workflowTriggerControlledBy, workflowTriggerPrincipalMember, } from "./trigger-owner.js";
6
6
  import { storedTriggerRows } from "./stored-trigger-rows.js";
7
+ /** Installation-scoped keyset read; the default and ceiling bound one reply. */
8
+ export const workflowScheduleListInputSchema = z.strictObject({
9
+ limit: z.number().int().min(1).max(100).default(50),
10
+ after: z.string().min(1).max(256).optional(),
11
+ });
7
12
  const recordSchema = armedWorkflowTriggerSchema.extend({
8
13
  id: z.string().min(1).max(256),
9
14
  cron: z.strictObject({
@@ -26,7 +31,7 @@ export function createWorkflowSchedules(options) {
26
31
  key: "scheduleId",
27
32
  record: recordSchema,
28
33
  });
29
- const records = () => rows.all().map((record) => {
34
+ const refresh = (record) => {
30
35
  if (record.state !== "active")
31
36
  return record;
32
37
  const reason = options.triggers.refusal(bindingOf(record));
@@ -35,7 +40,8 @@ export function createWorkflowSchedules(options) {
35
40
  const next = { ...paused(record), lastError: reason, retryAt: null };
36
41
  store(next);
37
42
  return next;
38
- });
43
+ };
44
+ const records = () => rows.all().map(refresh);
39
45
  const nextWake = () => {
40
46
  const due = records()
41
47
  .filter((record) => record.state === "active")
@@ -89,19 +95,38 @@ export function createWorkflowSchedules(options) {
89
95
  await options.wake(nextWake());
90
96
  return { kind: "armed", scheduleId: id };
91
97
  },
92
- async list(principal, installationId) {
98
+ async list(principal, installationId, input) {
99
+ await options.authorize(workflowTriggerPrincipalMember(principal), installationId);
100
+ const page = workflowScheduleListInputSchema.parse(input);
101
+ const selected = rows.installationPage(installationId, page.after ?? "", page.limit + 1);
102
+ const visible = selected.slice(0, page.limit).map(refresh);
103
+ return {
104
+ schedules: visible.map((record) => ({
105
+ scheduleId: record.id,
106
+ installationId: record.installationId,
107
+ workflowId: record.workflowId,
108
+ triggerId: record.triggerId,
109
+ nextDueAt: record.nextDueAt,
110
+ state: record.state,
111
+ lastError: record.lastError,
112
+ })),
113
+ nextCursor: selected.length > page.limit ? (visible.at(-1)?.id ?? null) : null,
114
+ };
115
+ },
116
+ /** Pause future admission only; accepted runs and the compiled plan remain owned. */
117
+ async pause(principal, installationId, scheduleId) {
93
118
  await options.authorize(workflowTriggerPrincipalMember(principal), installationId);
94
- return records()
95
- .filter((record) => record.installationId === installationId)
96
- .map((record) => ({
97
- scheduleId: record.id,
98
- installationId: record.installationId,
99
- workflowId: record.workflowId,
100
- triggerId: record.triggerId,
101
- nextDueAt: record.nextDueAt,
102
- state: record.state,
103
- lastError: record.lastError,
104
- }));
119
+ const record = rows.read(scheduleId);
120
+ if (record?.installationId !== installationId ||
121
+ !workflowTriggerControlledBy(principal, record) ||
122
+ record.state === "cancelled" ||
123
+ record.state === "finished")
124
+ return { kind: "not_found" };
125
+ await options.commit(() => {
126
+ store({ ...paused(record), retryAt: null });
127
+ });
128
+ await options.wake(nextWake());
129
+ return { kind: "paused" };
105
130
  },
106
131
  async disarm(principal, installationId, scheduleId) {
107
132
  const memberId = workflowTriggerPrincipalMember(principal);
@@ -130,6 +155,10 @@ export function createWorkflowSchedules(options) {
130
155
  const result = await options.triggers.fire(binding, `${options.teamId()}/schedule/${record.id}`, {
131
156
  scheduledAt: new Date(record.nextDueAt).toISOString(),
132
157
  eventId: occurrenceKey(record),
158
+ }, {
159
+ // The existing run owner checks this in its durable-start
160
+ // transaction, after authority I/O and before accepting work.
161
+ admitBinding: () => JSON.stringify(rows.read(record.id)) === JSON.stringify(record),
133
162
  });
134
163
  next =
135
164
  result.kind === "refused"
@@ -141,7 +170,7 @@ export function createWorkflowSchedules(options) {
141
170
  const refused = afterRefused(record, options.now(), "Workflow execution temporarily unavailable");
142
171
  next = { ...refused.schedule, retryAt: refused.retryAt };
143
172
  }
144
- // A disarm or re-arm admitted during upstream I/O wins over this firing.
173
+ // A pause, disarm or re-arm during upstream I/O wins over this firing.
145
174
  sql.exec("UPDATE hub_workflow_schedules SET record=? WHERE scheduleId=? AND record=?", JSON.stringify(recordSchema.parse(next)), record.id, JSON.stringify(record));
146
175
  }
147
176
  await options.wake(nextWake());
@@ -1,4 +1,4 @@
1
- import { z } from "zod";
1
+ import * as z from "zod";
2
2
  import type { WatchdogSqliteOwner } from "@fungi.computer/watchdog";
3
3
  /** Stored trigger rows decode strictly; an undecodable row is corruption. */
4
4
  export declare function storedTriggerRows<Record extends z.ZodType>(sql: WatchdogSqliteOwner["sql"], table: Readonly<{
@@ -7,6 +7,8 @@ export declare function storedTriggerRows<Record extends z.ZodType>(sql: Watchdo
7
7
  record: Record;
8
8
  }>): {
9
9
  all: () => z.core.output<Record>[];
10
+ /** Filter and bound in SQLite, before decoding records into the owner. */
11
+ installationPage(installationId: string, after: string, limit: number): z.core.output<Record>[];
10
12
  read(id: string): z.core.output<Record> | null;
11
13
  remove(id: string): void;
12
14
  };
@@ -1,4 +1,4 @@
1
- import { z } from "zod";
1
+ import * as z from "zod";
2
2
  const rowSchema = z.strictObject({ id: z.string(), record: z.string() });
3
3
  /** Stored trigger rows decode strictly; an undecodable row is corruption. */
4
4
  export function storedTriggerRows(sql, table) {
@@ -6,6 +6,13 @@ export function storedTriggerRows(sql, table) {
6
6
  const decode = (raw) => table.record.parse(JSON.parse(rowSchema.parse(raw).record));
7
7
  return {
8
8
  all: () => sql.exec(`${select} ORDER BY ${table.key}`).toArray().map(decode),
9
+ /** Filter and bound in SQLite, before decoding records into the owner. */
10
+ installationPage(installationId, after, limit) {
11
+ return sql
12
+ .exec(`${select} WHERE json_extract(record, '$.installationId')=? AND ${table.key}>? ORDER BY ${table.key} LIMIT ?`, installationId, after, limit)
13
+ .toArray()
14
+ .map(decode);
15
+ },
9
16
  read(id) {
10
17
  const row = sql.exec(`${select} WHERE ${table.key}=?`, id).toArray()[0];
11
18
  return row === undefined ? null : decode(row);
@@ -1,4 +1,4 @@
1
- import { z } from "zod";
1
+ import * as z from "zod";
2
2
  import * as S from "effect/Schema";
3
3
  import { type GitBinding, type RepositoryRef } from "@fungi.computer/agar/git";
4
4
  import { type TriggerEvent } from "../index.js";
@@ -1,4 +1,4 @@
1
- import { z } from "zod";
1
+ import * as z from "zod";
2
2
  import { bindingsSelectionSchema, gitAccessScope, resolveGitAccess, } from "../git-access.js";
3
3
  import * as S from "effect/Schema";
4
4
  import { appIdSchema, releaseIdSchema, sha256DigestSchema, } from "@fungi.computer/puffball/effect";
@@ -1,4 +1,4 @@
1
- import { z } from "zod";
1
+ import * as z from "zod";
2
2
  import * as S from "effect/Schema";
3
3
  import { canonicalJson, executionPlanSchema, } from "../index.js";
4
4
  import { armedWorkflowTriggerSchema, } from "./trigger-owner.js";
@@ -1,4 +1,4 @@
1
- import { z } from "zod";
1
+ import * as z from "zod";
2
2
  import { canonicalJson, contentDigest } from "../index.js";
3
3
  import { appEventFactSchema, } from "@fungi.computer/puffball/app-event-facts";
4
4
  import { armedWorkflowTriggerSchema, workflowArmerKey, workflowTriggerControlledBy, workflowTriggerPrincipalMember, } from "./trigger-owner.js";
@@ -1,4 +1,4 @@
1
- import { z } from "zod";
1
+ import * as z from "zod";
2
2
  import type { WatchdogSqliteOwner } from "@fungi.computer/watchdog";
3
3
  declare const triggerRunScopeSchema: z.ZodLazy<z.ZodObject<{
4
4
  installationId: z.ZodString;
@@ -1,5 +1,5 @@
1
1
  import { defineIdFamily } from "@fungi.computer/cairn/id";
2
- import { z } from "zod";
2
+ import * as z from "zod";
3
3
  import { gitSha1Schema } from "@fungi.computer/puffball/git-representation";
4
4
  import { workflowIdSchema } from "../index.js";
5
5
  const reservationIds = defineIdFamily({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fungi.computer/braid",
3
- "version": "0.2.4",
3
+ "version": "0.3.0",
4
4
  "author": "Levi <levi@technomancy.dev>",
5
5
  "type": "module",
6
6
  "description": "Pure versioned Workflow graph validation and content-addressed compilation",
@@ -30,15 +30,15 @@
30
30
  "dependencies": {
31
31
  "@cfworker/json-schema": "4.1.1",
32
32
  "@fungi.computer/almanac": "0.2.0",
33
- "@fungi.computer/mycelium": "0.2.3",
33
+ "@fungi.computer/mycelium": "0.3.0",
34
34
  "effect": "4.0.2",
35
35
  "jsonpointer": "5.0.1",
36
36
  "mustache": "4.2.0",
37
37
  "zod": "4.4.3",
38
- "@fungi.computer/agar": "0.5.0",
38
+ "@fungi.computer/agar": "0.6.0",
39
39
  "@fungi.computer/gills": "0.2.0",
40
40
  "@fungi.computer/watchdog": "0.1.0",
41
- "@fungi.computer/puffball": "0.2.0",
41
+ "@fungi.computer/puffball": "0.3.0",
42
42
  "@fungi.computer/cairn": "0.1.0"
43
43
  },
44
44
  "devDependencies": {