@intentius/chant-k8s-client 0.31.0 → 0.33.1

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.
package/README.md ADDED
@@ -0,0 +1,24 @@
1
+ # @intentius/chant-k8s-client
2
+
3
+ The typed Kubernetes API client behind [chant](https://www.npmjs.com/package/@intentius/chant)'s live-cluster surfaces: `chant lifecycle diff --live`, `chant lifecycle plan`, `chant kube`, the `kubectlApply`/`waitForReady` Op activities, and [behold](https://github.com/INTENTIUS/behold)'s overlay.
4
+
5
+ You normally don't install this directly — the k8s lexicon declares it as an **optional dependency**:
6
+
7
+ ```sh
8
+ npm install @intentius/chant-lexicon-k8s
9
+ ```
10
+
11
+ Without it installed, chant's build/synthesis path is completely unaffected (this package is never importable from it — enforced by test); live observation reports honest "not observed" holes instead of failing.
12
+
13
+ ## What it does
14
+
15
+ - **Any kind the cluster serves** — the operation surface is generated from the same OpenAPI/CRD pass that produces the lexicon's resource types (they cannot skew), then confirmed against the cluster's own API discovery. CRDs included; no hand-maintained kind map.
16
+ - **Server-side apply as `chant:<stack>`** — a stable field-manager identity per stack, with 409 conflicts surfaced as typed errors naming the competing manager and contested field paths. Force is per-call only, never a default.
17
+ - **managedFields primitives** — per-manager field-set parsing that powers chant's derived (not hand-maintained) property-level drift.
18
+ - **Cluster binding honored** — `k8s.profiles.<env>.context` from `chant.config.ts` is enforced; a mismatch with the ambient context is a refusal, not a read of the wrong cluster.
19
+ - Transport and auth are rented from [`@kubernetes/client-node`](https://github.com/kubernetes-client/javascript) (kubeconfig, exec credential plugins, token refresh).
20
+
21
+ ## Documentation
22
+
23
+ - [The API client](https://intentius.io/chant/lexicons/k8s/api-client/) — coverage, concurrency, credentials, and why this is a separate package
24
+ - [`chant kube`](https://intentius.io/chant/lexicons/k8s/kube/) — the terminal surface over this client
package/dist/client.d.ts CHANGED
@@ -64,18 +64,53 @@ export interface ReadOptions {
64
64
  }
65
65
  /** Options for {@link K8sClient.apply}. */
66
66
  export interface ApplyOptions {
67
- /** Field manager recorded on the objects this apply owns. Default `chant`. */
67
+ /**
68
+ * Field manager recorded on the fields this apply owns. Defaults to the bare
69
+ * `chant`; the k8s lexicon passes the stack-qualified `chant:<stack>` derived
70
+ * by {@link import("./field-manager.js").fieldManagerFor} (chant #1075).
71
+ */
68
72
  fieldManager?: string;
69
73
  /**
70
74
  * Take ownership of fields another manager owns instead of failing with a
71
- * 409. Default false — chant #1075 is where the conflict surface proper
72
- * lives; here a conflict simply arrives as a typed {@link K8sApiError}.
75
+ * {@link import("./conflict.js").FieldManagerConflictError}. **Default false,
76
+ * and nothing in chant sets it for you** transferring ownership of a live
77
+ * field is a decision, not a retry (chant #1075).
73
78
  */
74
79
  force?: boolean;
75
80
  /** Server-side dry run — validates and returns the result, persists nothing. */
76
81
  dryRun?: boolean;
77
82
  signal?: AbortSignal;
78
83
  }
84
+ /** Options for {@link K8sClient.delete}. */
85
+ export interface DeleteOptions {
86
+ /** `Foreground`, `Background` or `Orphan`. Omitted leaves the server's default. */
87
+ propagationPolicy?: "Foreground" | "Background" | "Orphan";
88
+ /** Server-side dry run — validates, deletes nothing. */
89
+ dryRun?: boolean;
90
+ signal?: AbortSignal;
91
+ }
92
+ /** Options for {@link K8sClient.list}. */
93
+ export interface ListOptions {
94
+ /** Restrict to one namespace. Omitted lists across all of them. */
95
+ namespace?: string;
96
+ /** A label selector, e.g. `app.kubernetes.io/managed-by=chant`. */
97
+ labelSelector?: string;
98
+ signal?: AbortSignal;
99
+ }
100
+ /** Options for {@link K8sClient.readLog} (chant #1079). */
101
+ export interface ReadLogOptions {
102
+ /** Container name. Required by the API server when a Pod has more than one. */
103
+ container?: string;
104
+ /** Read the previous (crashed/restarted) container instance's log. */
105
+ previous?: boolean;
106
+ /** Only the last N lines. */
107
+ tailLines?: number;
108
+ /** Only entries from the last N seconds. */
109
+ sinceSeconds?: number;
110
+ /** Prefix each line with its RFC3339 timestamp. */
111
+ timestamps?: boolean;
112
+ signal?: AbortSignal;
113
+ }
79
114
  /** The client surface the k8s lexicon consumes. */
80
115
  export interface K8sClient {
81
116
  /** Where this client is pointed and what authorized it. */
@@ -92,13 +127,22 @@ export interface K8sClient {
92
127
  read(ref: ObjectRef, options?: ReadOptions): Promise<K8sObject>;
93
128
  /** GET one object, returning undefined instead of throwing on a 404. */
94
129
  readIfPresent(ref: ObjectRef, options?: ReadOptions): Promise<K8sObject | undefined>;
95
- /** LIST a kind, optionally namespaced. Follows `continue` tokens. */
96
- list(selector: ResourceSelector, options?: {
97
- namespace?: string;
98
- signal?: AbortSignal;
99
- }): Promise<K8sObject[]>;
130
+ /** LIST a kind, optionally namespaced and label-filtered. Follows `continue` tokens. */
131
+ list(selector: ResourceSelector, options?: ListOptions): Promise<K8sObject[]>;
132
+ /**
133
+ * GET a Pod's `/log` subresource — plain text, not JSON, which is why this
134
+ * is its own method rather than a `read` variant. A snapshot only: the
135
+ * server's log endpoint supports `follow` as a chunked stream, but this
136
+ * client's transport seam (`ResponseContextLike.body.text()`) reads a
137
+ * response to completion rather than exposing it as a stream, so `--follow`
138
+ * is out of reach without widening that seam — chant #1079 leaves it there
139
+ * deliberately rather than half-implementing it.
140
+ */
141
+ readLog(ref: ObjectRef, options?: ReadLogOptions): Promise<string>;
100
142
  /** Server-side apply one object. Creates it when absent. */
101
143
  apply(object: K8sObject, options?: ApplyOptions): Promise<K8sObject>;
144
+ /** DELETE one object. Throws {@link K8sApiError} with `notFound` when absent. */
145
+ delete(ref: ObjectRef, options?: DeleteOptions): Promise<void>;
102
146
  /** Run `fn` over `items` with this client's concurrency ceiling. */
103
147
  concurrently<T, R>(items: readonly T[], fn: (item: T, index: number) => Promise<R>): Promise<R[]>;
104
148
  /** The API resource lists discovery has been asked for so far, for tests and diagnostics. */
@@ -1 +1 @@
1
- {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AAYH,OAAO,KAAK,EACV,eAAe,EACf,gBAAgB,EAChB,gBAAgB,EAChB,SAAS,EACT,SAAS,EAET,gBAAgB,EAEjB,MAAM,SAAS,CAAC;AAEjB,KAAK,UAAU,GAAG,cAAc,yBAAyB,CAAC,CAAC;AAI3D;;;;;;;GAOG;AACH,wBAAsB,cAAc,IAAI,OAAO,CAAC,UAAU,CAAC,CAQ1D;AAED,yEAAyE;AACzE,wBAAsB,oBAAoB,IAAI,OAAO,CAAC,OAAO,CAAC,CAO7D;AAED;;;;;;;;;GASG;AACH,wBAAsB,kBAAkB,CACtC,OAAO,GAAE,IAAI,CAAC,gBAAgB,EAAE,YAAY,GAAG,gBAAgB,CAAM,GACpE,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAW7B;AAED,wCAAwC;AACxC,MAAM,WAAW,WAAW;IAC1B,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAED,2CAA2C;AAC3C,MAAM,WAAW,YAAY;IAC3B,8EAA8E;IAC9E,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;;OAIG;IACH,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,gFAAgF;IAChF,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAED,mDAAmD;AACnD,MAAM,WAAW,SAAS;IACxB,2DAA2D;IAC3D,QAAQ,CAAC,UAAU,EAAE,gBAAgB,CAAC;IACtC,kDAAkD;IAClD,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC;;;;OAIG;IACH,OAAO,CAAC,QAAQ,EAAE,gBAAgB,EAAE,MAAM,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,eAAe,GAAG,SAAS,CAAC,CAAC;IAChG,8EAA8E;IAC9E,IAAI,CAAC,GAAG,EAAE,SAAS,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC;IAChE,wEAAwE;IACxE,aAAa,CAAC,GAAG,EAAE,SAAS,EAAE,OAAO,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,SAAS,GAAG,SAAS,CAAC,CAAC;IACrF,qEAAqE;IACrE,IAAI,CAAC,QAAQ,EAAE,gBAAgB,EAAE,OAAO,CAAC,EAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,WAAW,CAAA;KAAE,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC,CAAC;IAC/G,4DAA4D;IAC5D,KAAK,CAAC,MAAM,EAAE,SAAS,EAAE,OAAO,CAAC,EAAE,YAAY,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC;IACrE,oEAAoE;IACpE,YAAY,CAAC,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,SAAS,CAAC,EAAE,EAAE,EAAE,EAAE,CAAC,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,EAAE,CAAC,CAAC;IAClG,6FAA6F;IAC7F,kBAAkB,IAAI,MAAM,EAAE,CAAC;CAChC;AAcD;;;;GAIG;AACH,wBAAsB,eAAe,CAAC,OAAO,GAAE,gBAAqB,GAAG,OAAO,CAAC,SAAS,CAAC,CA8WxF;AAED,qDAAqD;AACrD,wBAAgB,cAAc,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAEzD;AAED,yDAAyD;AACzD,wBAAgB,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,CAGpF;AAED,wDAAwD;AACxD,wBAAgB,YAAY,CAAC,QAAQ,EAAE,gBAAgB,GAAG,MAAM,CAM/D;AAED,iEAAiE;AACjE,wBAAgB,OAAO,CAAC,GAAG,EAAE,SAAS,GAAG,MAAM,CAE9C"}
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,103 @@
1
+ /**
2
+ * The server-side-apply conflict surface — chant #1075.
3
+ *
4
+ * When an apply touches a field another manager owns, the API server refuses
5
+ * with a 409 and — unlike almost every other Kubernetes failure — tells you
6
+ * precisely what went wrong: which fields, and who owns each one. It arrives as
7
+ * a `Status` whose `details.causes` is a list of `FieldManagerConflict` entries:
8
+ *
9
+ * ```json
10
+ * { "reason": "Conflict", "code": 409,
11
+ * "message": "Apply failed with 2 conflicts: conflicts with \"kubectl\" ...",
12
+ * "details": { "causes": [
13
+ * { "type": "FieldManagerConflict", "message": "conflict with \"kubectl\"",
14
+ * "field": ".spec.replicas" } ] } }
15
+ * ```
16
+ *
17
+ * chant #1074 already carried that through as a typed `K8sApiError` with
18
+ * `conflict === true`. What was missing is the presentation: a 409 whose
19
+ * message is one run-on line is not something an operator can act on, and the
20
+ * thing they must not do — force it because the error suggested nothing else —
21
+ * is exactly what a bad message invites.
22
+ *
23
+ * So this error names the competing manager, lists the contested paths under
24
+ * it, says what forcing would mean, and stops there. **chant never forces a
25
+ * conflict on its own.** Taking a field from another manager is a decision
26
+ * about who owns production, and a tool that makes it silently is the reason
27
+ * "it works on my cluster" happens. The opt-in exists (`force`), it is never
28
+ * the default, and nothing in chant turns it on for you.
29
+ */
30
+ import { K8sApiError, type K8sStatus } from "./errors.js";
31
+ /** One contested field: a path, and the manager that owns it. */
32
+ export interface FieldConflict {
33
+ /** The manager that currently owns the field, e.g. `kubectl`, `helm`. */
34
+ manager: string;
35
+ /**
36
+ * The field path, in the same syntax `./managed-fields.ts` renders — e.g.
37
+ * `.spec.replicas`, `.spec.template.spec.containers[name="web"].image`.
38
+ */
39
+ field: string;
40
+ /** The apiVersion the owning entry was recorded at, when the server said. */
41
+ apiVersion?: string;
42
+ }
43
+ /**
44
+ * A server-side apply was refused because another field manager owns fields
45
+ * this apply would set.
46
+ *
47
+ * Extends {@link K8sApiError} so every existing `instanceof K8sApiError` check
48
+ * (and the `conflict` predicate) keeps working — this is a presentation of the
49
+ * 409, not a replacement for it.
50
+ */
51
+ export declare class FieldManagerConflictError extends K8sApiError {
52
+ /** Every contested field, in the order the server reported them. */
53
+ readonly conflicts: FieldConflict[];
54
+ /** The field manager chant applied as, and which was refused. */
55
+ readonly fieldManager: string;
56
+ constructor(statusCode: number, apiMessage: string, conflicts: FieldConflict[], fieldManager: string, target?: string, status?: K8sStatus);
57
+ /** Contested paths grouped by the manager that owns them, managers sorted. */
58
+ get byManager(): Record<string, string[]>;
59
+ /** The competing managers, sorted. */
60
+ get managers(): string[];
61
+ /** The contested paths, sorted and deduplicated. */
62
+ get fields(): string[];
63
+ }
64
+ /** Inputs {@link renderConflictReport} needs; broken out so tests can render directly. */
65
+ export interface ConflictReport {
66
+ conflicts: FieldConflict[];
67
+ fieldManager: string;
68
+ target?: string;
69
+ /** The server's own message, used verbatim when nothing could be parsed. */
70
+ apiMessage?: string;
71
+ }
72
+ /**
73
+ * The operator-facing rendering. Three things, in this order: what is
74
+ * contested and who holds it, what chant applied as, and what the two ways out
75
+ * actually mean. No recommendation between them — that is the point.
76
+ */
77
+ export declare function renderConflictReport(report: ConflictReport): string;
78
+ /**
79
+ * Pull the field causes out of a 409 `Status`.
80
+ *
81
+ * `details.causes` is the machine-readable form and is preferred. Not every
82
+ * server fills it in — an aggregated API server or an older release puts the
83
+ * same information only in the prose `message` — so the message is parsed as a
84
+ * fallback rather than the list being reported as empty.
85
+ */
86
+ export declare function parseFieldConflicts(status: K8sStatus | undefined, message?: string): FieldConflict[];
87
+ /**
88
+ * Parse the prose form, which the server builds as one `conflicts with "x"`
89
+ * clause per manager followed by that manager's fields as a `-` list:
90
+ *
91
+ * ```
92
+ * Apply failed with 2 conflicts: conflicts with "kubectl" using apps/v1:
93
+ * - .spec.replicas
94
+ * - .spec.template.spec.containers[name="web"].image
95
+ * ```
96
+ */
97
+ export declare function parseConflictMessage(message: string): FieldConflict[];
98
+ /**
99
+ * Turn a 409 from an apply into the presented error. Any other failure is
100
+ * returned unchanged — this is a narrowing, not a catch-all.
101
+ */
102
+ export declare function asFieldManagerConflict(error: unknown, fieldManager: string): unknown;
103
+ //# sourceMappingURL=conflict.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"conflict.d.ts","sourceRoot":"","sources":["../src/conflict.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,EAAE,WAAW,EAAE,KAAK,SAAS,EAAE,MAAM,UAAU,CAAC;AAEvD,iEAAiE;AACjE,MAAM,WAAW,aAAa;IAC5B,yEAAyE;IACzE,OAAO,EAAE,MAAM,CAAC;IAChB;;;OAGG;IACH,KAAK,EAAE,MAAM,CAAC;IACd,6EAA6E;IAC7E,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;;;;;GAOG;AACH,qBAAa,yBAA0B,SAAQ,WAAW;IACxD,oEAAoE;IACpE,SAAgB,SAAS,EAAE,aAAa,EAAE,CAAC;IAC3C,iEAAiE;IACjE,SAAgB,YAAY,EAAE,MAAM,CAAC;gBAGnC,UAAU,EAAE,MAAM,EAClB,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,aAAa,EAAE,EAC1B,YAAY,EAAE,MAAM,EACpB,MAAM,CAAC,EAAE,MAAM,EACf,MAAM,CAAC,EAAE,SAAS;IAUpB,8EAA8E;IAC9E,IAAI,SAAS,IAAI,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAUxC;IAED,sCAAsC;IACtC,IAAI,QAAQ,IAAI,MAAM,EAAE,CAEvB;IAED,oDAAoD;IACpD,IAAI,MAAM,IAAI,MAAM,EAAE,CAErB;CACF;AAED,0FAA0F;AAC1F,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,aAAa,EAAE,CAAC;IAC3B,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,4EAA4E;IAC5E,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,cAAc,GAAG,MAAM,CAwCnE;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,SAAS,GAAG,SAAS,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,aAAa,EAAE,CAIpG;AAoCD;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,aAAa,EAAE,CA0BrE;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,GAAG,OAAO,CAWpF"}
package/dist/errors.d.ts CHANGED
@@ -39,7 +39,11 @@ export declare class K8sApiError extends Error {
39
39
  get forbidden(): boolean;
40
40
  /** No usable credentials for this cluster. */
41
41
  get unauthorized(): boolean;
42
- /** Server-side-apply field-ownership conflict (chant #1075 surfaces these properly). */
42
+ /**
43
+ * Server-side-apply field-ownership conflict. `./conflict.ts`'s
44
+ * {@link import("./conflict.js").FieldManagerConflictError} is the presented
45
+ * form (chant #1075); this predicate still answers for both.
46
+ */
43
47
  get conflict(): boolean;
44
48
  /**
45
49
  * Build from a raw response body, which is a `Status` on every well-behaved
@@ -80,6 +84,15 @@ export declare class ExecCredentialNotAllowedError extends Error {
80
84
  readonly allowed: readonly string[];
81
85
  constructor(command: string, allowed: readonly string[]);
82
86
  }
87
+ /**
88
+ * chant's own field-manager identity is unusable (chant #1075) — almost always
89
+ * because `ownership.stack` is too long or carries whitespace. Raised where the
90
+ * name is derived, before any request, so the config key can be named instead
91
+ * of the failure arriving as a 400 from a cluster.
92
+ */
93
+ export declare class FieldManagerError extends Error {
94
+ constructor(message: string);
95
+ }
83
96
  /** The kubeconfig could not be read, or names no usable cluster/context. */
84
97
  export declare class KubeConfigError extends Error {
85
98
  constructor(message: string);
@@ -1 +1 @@
1
- {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,+EAA+E;AAC/E,MAAM,WAAW,SAAS;IACxB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,iFAAiF;IACjF,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED;;;GAGG;AACH,qBAAa,WAAY,SAAQ,KAAK;aAElB,UAAU,EAAE,MAAM;aAClB,MAAM,EAAE,MAAM,GAAG,SAAS;aAC1B,UAAU,EAAE,MAAM;IAClC,oEAAoE;aACpD,MAAM,CAAC,EAAE,MAAM;aACf,MAAM,CAAC,EAAE,SAAS;gBALlB,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,MAAM,GAAG,SAAS,EAC1B,UAAU,EAAE,MAAM;IAClC,oEAAoE;IACpD,MAAM,CAAC,EAAE,MAAM,YAAA,EACf,MAAM,CAAC,EAAE,SAAS,YAAA;IASpC,0EAA0E;IAC1E,IAAI,QAAQ,IAAI,OAAO,CAEtB;IAED,4EAA4E;IAC5E,IAAI,SAAS,IAAI,OAAO,CAEvB;IAED,8CAA8C;IAC9C,IAAI,YAAY,IAAI,OAAO,CAE1B;IAED,wFAAwF;IACxF,IAAI,QAAQ,IAAI,OAAO,CAEtB;IAED;;;OAGG;IACH,MAAM,CAAC,YAAY,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,WAAW;CAapF;AAED;;;;GAIG;AACH,qBAAa,iBAAkB,SAAQ,KAAK;aAGxB,MAAM,CAAC,EAAE,MAAM;gBAD/B,OAAO,EAAE,MAAM,EACC,MAAM,CAAC,EAAE,MAAM,YAAA,EAC/B,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE;CAMhC;AAED;;;;;GAKG;AACH,qBAAa,yBAA0B,SAAQ,KAAK;gBACtC,KAAK,CAAC,EAAE,OAAO;CAS5B;AAED;;;;;;;GAOG;AACH,qBAAa,6BAA8B,SAAQ,KAAK;aAEpC,OAAO,EAAE,MAAM;aACf,OAAO,EAAE,SAAS,MAAM,EAAE;gBAD1B,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,SAAS,MAAM,EAAE;CAU7C;AAED,4EAA4E;AAC5E,qBAAa,eAAgB,SAAQ,KAAK;gBAC5B,OAAO,EAAE,MAAM;CAI5B;AAED;;;;GAIG;AACH,qBAAa,oBAAqB,SAAQ,KAAK;aAE3B,YAAY,EAAE,MAAM;gBAApB,YAAY,EAAE,MAAM,EACpC,OAAO,CAAC,EAAE,MAAM;CAKnB"}
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,+EAA+E;AAC/E,MAAM,WAAW,SAAS;IACxB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,iFAAiF;IACjF,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED;;;GAGG;AACH,qBAAa,WAAY,SAAQ,KAAK;aAElB,UAAU,EAAE,MAAM;aAClB,MAAM,EAAE,MAAM,GAAG,SAAS;aAC1B,UAAU,EAAE,MAAM;IAClC,oEAAoE;aACpD,MAAM,CAAC,EAAE,MAAM;aACf,MAAM,CAAC,EAAE,SAAS;gBALlB,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,MAAM,GAAG,SAAS,EAC1B,UAAU,EAAE,MAAM;IAClC,oEAAoE;IACpD,MAAM,CAAC,EAAE,MAAM,YAAA,EACf,MAAM,CAAC,EAAE,SAAS,YAAA;IASpC,0EAA0E;IAC1E,IAAI,QAAQ,IAAI,OAAO,CAEtB;IAED,4EAA4E;IAC5E,IAAI,SAAS,IAAI,OAAO,CAEvB;IAED,8CAA8C;IAC9C,IAAI,YAAY,IAAI,OAAO,CAE1B;IAED;;;;OAIG;IACH,IAAI,QAAQ,IAAI,OAAO,CAEtB;IAED;;;OAGG;IACH,MAAM,CAAC,YAAY,CAAC,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,WAAW;CAapF;AAED;;;;GAIG;AACH,qBAAa,iBAAkB,SAAQ,KAAK;aAGxB,MAAM,CAAC,EAAE,MAAM;gBAD/B,OAAO,EAAE,MAAM,EACC,MAAM,CAAC,EAAE,MAAM,YAAA,EAC/B,OAAO,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE;CAMhC;AAED;;;;;GAKG;AACH,qBAAa,yBAA0B,SAAQ,KAAK;gBACtC,KAAK,CAAC,EAAE,OAAO;CAS5B;AAED;;;;;;;GAOG;AACH,qBAAa,6BAA8B,SAAQ,KAAK;aAEpC,OAAO,EAAE,MAAM;aACf,OAAO,EAAE,SAAS,MAAM,EAAE;gBAD1B,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,SAAS,MAAM,EAAE;CAU7C;AAED;;;;;GAKG;AACH,qBAAa,iBAAkB,SAAQ,KAAK;gBAC9B,OAAO,EAAE,MAAM;CAI5B;AAED,4EAA4E;AAC5E,qBAAa,eAAgB,SAAQ,KAAK;gBAC5B,OAAO,EAAE,MAAM;CAI5B;AAED;;;;GAIG;AACH,qBAAa,oBAAqB,SAAQ,KAAK;aAE3B,YAAY,EAAE,MAAM;gBAApB,YAAY,EAAE,MAAM,EACpC,OAAO,CAAC,EAAE,MAAM;CAKnB"}
@@ -0,0 +1,62 @@
1
+ /**
2
+ * chant's field-manager identity — chant #1075.
3
+ *
4
+ * Server-side apply records, per field, the name of the manager that last
5
+ * wrote it. That name is chant's identity in the cluster, so it has to be
6
+ * *stable* (the same stack applying twice must be the same manager, or the
7
+ * second apply conflicts with the first) and *distinguishing* (two chant
8
+ * stacks sharing a cluster must not silently co-own each other's fields).
9
+ *
10
+ * The scheme is `chant` alone, or `chant:<stack>` when the project sets
11
+ * `ownership.stack` — the same identity the label-based ownership marker
12
+ * already carries (`packages/core/src/ownership.ts`). Ownership-by-label
13
+ * answers a binary whole-object question; the field manager is the sub-object
14
+ * version of the same fact, supplied by the API server rather than stamped by
15
+ * chant. One identity, two granularities.
16
+ *
17
+ * **`ownership.env` is deliberately not part of it.** Two environments of one
18
+ * stack only ever touch the same object if they share a namespace and a name,
19
+ * and at that point they are fighting over it. An env-qualified manager would
20
+ * let them each own a different half of that object without either noticing;
21
+ * a stack-qualified one makes the second apply conflict, which is the correct
22
+ * outcome and the whole point of the conflict surface.
23
+ *
24
+ * This module is plain string logic with no dependency on chant core, so the
25
+ * lexicon (which reads `ownership` from project config) and the client (which
26
+ * writes the query parameter) agree on one derivation rather than two.
27
+ */
28
+ /** The unqualified manager, used when a project sets no ownership stack. */
29
+ export declare const CHANT_FIELD_MANAGER = "chant";
30
+ /** Separator between the `chant` prefix and the stack identity. */
31
+ export declare const FIELD_MANAGER_SEPARATOR = ":";
32
+ /**
33
+ * The API server's own ceiling on `fieldManager`
34
+ * (`k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager`). Exceeding it is a
35
+ * 400 on every apply, so it is checked here — where the name is derived and
36
+ * the offending config key can be named — rather than discovered in a cluster.
37
+ */
38
+ export declare const FIELD_MANAGER_MAX_LENGTH = 128;
39
+ /** The stack identity a field manager is derived from. */
40
+ export interface FieldManagerIdentity {
41
+ /** `ownership.stack` from project config, when set. */
42
+ stack?: string;
43
+ }
44
+ /**
45
+ * Derive the field manager for a stack. `undefined`/no stack yields the bare
46
+ * `chant`; a stack yields `chant:<stack>`.
47
+ */
48
+ export declare function fieldManagerFor(identity?: FieldManagerIdentity): string;
49
+ /**
50
+ * Reject a field manager the API server would reject, naming the config key
51
+ * responsible. A silent truncation would be worse than a failure: it would
52
+ * merge two stacks' identities into one and make their applies fight.
53
+ */
54
+ export declare function assertValidFieldManager(manager: string, stack?: string): void;
55
+ /** True when `manager` is a chant field manager, qualified or not. */
56
+ export declare function isChantFieldManager(manager: string | undefined): boolean;
57
+ /**
58
+ * The stack a chant field manager names, or undefined for the unqualified
59
+ * `chant` and for any manager that is not chant's at all.
60
+ */
61
+ export declare function chantStackOf(manager: string | undefined): string | undefined;
62
+ //# sourceMappingURL=field-manager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"field-manager.d.ts","sourceRoot":"","sources":["../src/field-manager.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAIH,4EAA4E;AAC5E,eAAO,MAAM,mBAAmB,UAAU,CAAC;AAE3C,mEAAmE;AACnE,eAAO,MAAM,uBAAuB,MAAM,CAAC;AAE3C;;;;;GAKG;AACH,eAAO,MAAM,wBAAwB,MAAM,CAAC;AAE5C,0DAA0D;AAC1D,MAAM,WAAW,oBAAoB;IACnC,uDAAuD;IACvD,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,QAAQ,CAAC,EAAE,oBAAoB,GAAG,MAAM,CAMvE;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAwB7E;AAED,sEAAsE;AACtE,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAMxE;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAI5E"}
package/dist/index.d.ts CHANGED
@@ -12,9 +12,15 @@
12
12
  * it stays that way.
13
13
  */
14
14
  export { createK8sClient, readAmbientContext, loadClientNode, isK8sClientAvailable, apiVersionPath, splitApiVersion, selectorText, refText, } from "./client.js";
15
- export type { K8sClient, ReadOptions, ApplyOptions } from "./client.js";
16
- export { K8sApiError, K8sTransportError, K8sClientUnavailableError, ExecCredentialNotAllowedError, KubeConfigError, UnknownResourceError, } from "./errors.js";
15
+ export type { K8sClient, ReadOptions, ApplyOptions, DeleteOptions, ListOptions, ReadLogOptions } from "./client.js";
16
+ export { K8sApiError, K8sTransportError, K8sClientUnavailableError, ExecCredentialNotAllowedError, FieldManagerError, KubeConfigError, UnknownResourceError, } from "./errors.js";
17
17
  export type { K8sStatus } from "./errors.js";
18
+ export { CHANT_FIELD_MANAGER, FIELD_MANAGER_SEPARATOR, FIELD_MANAGER_MAX_LENGTH, fieldManagerFor, assertValidFieldManager, isChantFieldManager, chantStackOf, } from "./field-manager.js";
19
+ export type { FieldManagerIdentity } from "./field-manager.js";
20
+ export { FieldManagerConflictError, asFieldManagerConflict, parseFieldConflicts, parseConflictMessage, renderConflictReport, } from "./conflict.js";
21
+ export type { FieldConflict, ConflictReport } from "./conflict.js";
22
+ export { managedFieldsOf, fieldSetsOf, managersOf, fieldsOwnedBy, chantOwnedFields, fieldOwners, fieldPathsOf, renderSegment, } from "./managed-fields.js";
23
+ export type { ManagedFieldsEntry, ManagerFieldSet } from "./managed-fields.js";
18
24
  export { DEFAULT_EXEC_ALLOWLIST, assertExecCredentialAllowed, credentialPathOf, execConfigOf, execCommandName, } from "./credentials.js";
19
25
  export type { ExecConfig, KubeConfigUser } from "./credentials.js";
20
26
  export { mapConcurrent, DEFAULT_CONCURRENCY } from "./concurrency.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EACL,eAAe,EACf,kBAAkB,EAClB,cAAc,EACd,oBAAoB,EACpB,cAAc,EACd,eAAe,EACf,YAAY,EACZ,OAAO,GACR,MAAM,UAAU,CAAC;AAClB,YAAY,EAAE,SAAS,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAErE,OAAO,EACL,WAAW,EACX,iBAAiB,EACjB,yBAAyB,EACzB,6BAA6B,EAC7B,eAAe,EACf,oBAAoB,GACrB,MAAM,UAAU,CAAC;AAClB,YAAY,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAE1C,OAAO,EACL,sBAAsB,EACtB,2BAA2B,EAC3B,gBAAgB,EAChB,YAAY,EACZ,eAAe,GAChB,MAAM,eAAe,CAAC;AACvB,YAAY,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAEhE,OAAO,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AAEnE,YAAY,EACV,eAAe,EACf,gBAAgB,EAChB,cAAc,EACd,gBAAgB,EAChB,SAAS,EACT,SAAS,EACT,kBAAkB,EAClB,YAAY,EACZ,gBAAgB,EAChB,mBAAmB,GACpB,MAAM,SAAS,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EACL,eAAe,EACf,kBAAkB,EAClB,cAAc,EACd,oBAAoB,EACpB,cAAc,EACd,eAAe,EACf,YAAY,EACZ,OAAO,GACR,MAAM,UAAU,CAAC;AAClB,YAAY,EAAE,SAAS,EAAE,WAAW,EAAE,YAAY,EAAE,aAAa,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAEjH,OAAO,EACL,WAAW,EACX,iBAAiB,EACjB,yBAAyB,EACzB,6BAA6B,EAC7B,iBAAiB,EACjB,eAAe,EACf,oBAAoB,GACrB,MAAM,UAAU,CAAC;AAClB,YAAY,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAG1C,OAAO,EACL,mBAAmB,EACnB,uBAAuB,EACvB,wBAAwB,EACxB,eAAe,EACf,uBAAuB,EACvB,mBAAmB,EACnB,YAAY,GACb,MAAM,iBAAiB,CAAC;AACzB,YAAY,EAAE,oBAAoB,EAAE,MAAM,iBAAiB,CAAC;AAE5D,OAAO,EACL,yBAAyB,EACzB,sBAAsB,EACtB,mBAAmB,EACnB,oBAAoB,EACpB,oBAAoB,GACrB,MAAM,YAAY,CAAC;AACpB,YAAY,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAGhE,OAAO,EACL,eAAe,EACf,WAAW,EACX,UAAU,EACV,aAAa,EACb,gBAAgB,EAChB,WAAW,EACX,YAAY,EACZ,aAAa,GACd,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAE5E,OAAO,EACL,sBAAsB,EACtB,2BAA2B,EAC3B,gBAAgB,EAChB,YAAY,EACZ,eAAe,GAChB,MAAM,eAAe,CAAC;AACvB,YAAY,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAEhE,OAAO,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AAEnE,YAAY,EACV,eAAe,EACf,gBAAgB,EAChB,cAAc,EACd,gBAAgB,EAChB,SAAS,EACT,SAAS,EACT,kBAAkB,EAClB,YAAY,EACZ,gBAAgB,EAChB,mBAAmB,GACpB,MAAM,SAAS,CAAC"}
@@ -0,0 +1,110 @@
1
+ /**
2
+ * Reading `metadata.managedFields` — chant #1075, consumed by #1076.
3
+ *
4
+ * The API server records, per object, one entry per manager that has written
5
+ * to it, and inside each entry a *set* of the field paths that manager owns.
6
+ * That set is encoded as `fieldsV1`, a nested object whose keys carry a
7
+ * one-or-two-character prefix rather than being plain field names:
8
+ *
9
+ * ```json
10
+ * { "f:spec": { "f:template": { "f:spec": {
11
+ * "f:containers": { "k:{\"name\":\"web\"}": { ".": {}, "f:image": {} } } } } } }
12
+ * ```
13
+ *
14
+ * | prefix | means | rendered as |
15
+ * |--------|------------------------------------------|-----------------|
16
+ * | `f:` | a field of a map | `.image` |
17
+ * | `k:` | a list item, addressed by its key fields | `[name="web"]` |
18
+ * | `v:` | a set item, addressed by its value | `[="blue"]` |
19
+ * | `i:` | a list item, addressed by its index | `[0]` |
20
+ * | `.` | the containing element itself | (the prefix) |
21
+ *
22
+ * The rendering above is not invented here: it is what
23
+ * `sigs.k8s.io/structured-merge-diff`'s `fieldpath.Path.String()` produces,
24
+ * which is the same syntax the API server uses for the `field` of a conflict
25
+ * cause (`./conflict.ts`). Both halves of #1076's question — *which fields
26
+ * does chant own* and *which fields is something else fighting over* — have to
27
+ * be comparable as strings, so there is exactly one renderer.
28
+ *
29
+ * This module reads. It does not decide what a difference means; that is
30
+ * #1076's job. What it owes #1076 is the primitive: an object in, per-manager
31
+ * field sets out.
32
+ */
33
+ import type { K8sObject } from "./types.js";
34
+ /** One `metadata.managedFields` entry, as the API server writes it. */
35
+ export interface ManagedFieldsEntry {
36
+ manager?: string;
37
+ /** `Apply` for a server-side apply, `Update` for anything else. */
38
+ operation?: string;
39
+ apiVersion?: string;
40
+ fieldsType?: string;
41
+ fieldsV1?: Record<string, unknown>;
42
+ /** Set when the entry describes a subresource write, e.g. `status`. */
43
+ subresource?: string;
44
+ time?: string;
45
+ }
46
+ /** One manager's ownership of one object, with `fieldsV1` decoded. */
47
+ export interface ManagerFieldSet {
48
+ /** The manager's name, e.g. `chant:web`, `kubectl-client-side-apply`. */
49
+ manager: string;
50
+ /** `Apply` (server-side apply) or `Update` (everything else). */
51
+ operation: string;
52
+ /** The apiVersion the entry was recorded at. */
53
+ apiVersion?: string;
54
+ /** Set when this entry covers a subresource (`status`, `scale`). */
55
+ subresource?: string;
56
+ /** When the write happened, as the server recorded it. */
57
+ time?: string;
58
+ /** Field paths this entry owns, rendered and sorted. */
59
+ fields: string[];
60
+ }
61
+ /** `metadata.managedFields`, or an empty list when the object carries none. */
62
+ export declare function managedFieldsOf(object: K8sObject | undefined): ManagedFieldsEntry[];
63
+ /**
64
+ * Decode every `managedFields` entry into a manager and its owned paths.
65
+ *
66
+ * Entries are kept separate rather than merged by manager name: a manager can
67
+ * legitimately hold two entries for one object — one for the main resource and
68
+ * one for a subresource, or two at different apiVersions — and collapsing them
69
+ * would lose the distinction #1076 needs when deciding whether a `status`
70
+ * write is chant's business (it is not).
71
+ */
72
+ export declare function fieldSetsOf(object: K8sObject | undefined): ManagerFieldSet[];
73
+ /** Every manager named on the object, in `managedFields` order, deduplicated. */
74
+ export declare function managersOf(object: K8sObject | undefined): string[];
75
+ /**
76
+ * The paths owned by managers matching `manager` — a literal name, or a
77
+ * predicate for the fuzzier questions (#1076 asks "which fields does *any*
78
+ * chant manager own", since a stack rename changes the name).
79
+ *
80
+ * Subresource entries are excluded by default: a controller writing `status`
81
+ * is not competing for the spec chant declared.
82
+ */
83
+ export declare function fieldsOwnedBy(object: K8sObject | undefined, manager: string | ((manager: string) => boolean), options?: {
84
+ includeSubresources?: boolean;
85
+ }): string[];
86
+ /** The paths owned by any chant field manager — {@link isChantFieldManager}. */
87
+ export declare function chantOwnedFields(object: K8sObject | undefined, options?: {
88
+ includeSubresources?: boolean;
89
+ }): string[];
90
+ /**
91
+ * path → the managers that own it. A path with two owners is not an error:
92
+ * server-side apply lets several appliers co-own a field when they set it to
93
+ * the same value, and an `Update` entry can overlap an `Apply` one.
94
+ */
95
+ export declare function fieldOwners(object: K8sObject | undefined, options?: {
96
+ includeSubresources?: boolean;
97
+ }): Map<string, string[]>;
98
+ /**
99
+ * Render one `fieldsV1` tree to sorted, dotted paths.
100
+ *
101
+ * Exported because a caller with an entry already in hand (a watch event, a
102
+ * stored snapshot) should not have to reassemble a whole object to decode it.
103
+ */
104
+ export declare function fieldPathsOf(fieldsV1: unknown, prefix?: string): string[];
105
+ /**
106
+ * One `fieldsV1` key to one path segment, or undefined for a key with no
107
+ * recognised prefix (a future encoding chant should skip rather than mangle).
108
+ */
109
+ export declare function renderSegment(key: string): string | undefined;
110
+ //# sourceMappingURL=managed-fields.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"managed-fields.d.ts","sourceRoot":"","sources":["../src/managed-fields.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAGH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAEzC,uEAAuE;AACvE,MAAM,WAAW,kBAAkB;IACjC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,mEAAmE;IACnE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACnC,uEAAuE;IACvE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,sEAAsE;AACtE,MAAM,WAAW,eAAe;IAC9B,yEAAyE;IACzE,OAAO,EAAE,MAAM,CAAC;IAChB,iEAAiE;IACjE,SAAS,EAAE,MAAM,CAAC;IAClB,gDAAgD;IAChD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,oEAAoE;IACpE,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,0DAA0D;IAC1D,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,wDAAwD;IACxD,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAED,+EAA+E;AAC/E,wBAAgB,eAAe,CAAC,MAAM,EAAE,SAAS,GAAG,SAAS,GAAG,kBAAkB,EAAE,CAMnF;AAED;;;;;;;;GAQG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,SAAS,GAAG,SAAS,GAAG,eAAe,EAAE,CAW5E;AAED,iFAAiF;AACjF,wBAAgB,UAAU,CAAC,MAAM,EAAE,SAAS,GAAG,SAAS,GAAG,MAAM,EAAE,CAMlE;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAC3B,MAAM,EAAE,SAAS,GAAG,SAAS,EAC7B,OAAO,EAAE,MAAM,GAAG,CAAC,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,EAChD,OAAO,GAAE;IAAE,mBAAmB,CAAC,EAAE,OAAO,CAAA;CAAO,GAC9C,MAAM,EAAE,CASV;AAED,gFAAgF;AAChF,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,SAAS,GAAG,SAAS,EAC7B,OAAO,GAAE;IAAE,mBAAmB,CAAC,EAAE,OAAO,CAAA;CAAO,GAC9C,MAAM,EAAE,CAEV;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CACzB,MAAM,EAAE,SAAS,GAAG,SAAS,EAC7B,OAAO,GAAE;IAAE,mBAAmB,CAAC,EAAE,OAAO,CAAA;CAAO,GAC9C,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAWvB;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,OAAO,EAAE,MAAM,SAAK,GAAG,MAAM,EAAE,CAErE;AAmBD;;;GAGG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAM7D"}
package/package.json CHANGED
@@ -1,9 +1,9 @@
1
1
  {
2
2
  "name": "@intentius/chant-k8s-client",
3
- "version": "0.31.0",
3
+ "version": "0.33.1",
4
4
  "description": "Typed Kubernetes API client for chant — the read/write path of the k8s lexicon, kept out of the build path",
5
5
  "license": "Apache-2.0",
6
- "homepage": "https://intentius.io/chant",
6
+ "homepage": "https://intentius.io/chant/lexicons/k8s/api-client/",
7
7
  "repository": {
8
8
  "type": "git",
9
9
  "url": "https://github.com/INTENTIUS/chant.git",