@intentius/chant-k8s-client 0.31.0 → 0.32.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.
- package/README.md +24 -0
- package/dist/client.d.ts +52 -8
- package/dist/client.d.ts.map +1 -1
- package/dist/conflict.d.ts +103 -0
- package/dist/conflict.d.ts.map +1 -0
- package/dist/errors.d.ts +14 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/field-manager.d.ts +62 -0
- package/dist/field-manager.d.ts.map +1 -0
- package/dist/index.d.ts +8 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/managed-fields.d.ts +110 -0
- package/dist/managed-fields.d.ts.map +1 -0
- package/package.json +2 -2
- package/src/client.test.ts +137 -1
- package/src/client.ts +116 -18
- package/src/conflict.test.ts +209 -0
- package/src/conflict.ts +257 -0
- package/src/errors.ts +18 -1
- package/src/field-manager.test.ts +110 -0
- package/src/field-manager.ts +111 -0
- package/src/index.ts +36 -1
- package/src/managed-fields.test.ts +199 -0
- package/src/managed-fields.ts +216 -0
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
|
-
/**
|
|
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
|
-
*
|
|
72
|
-
*
|
|
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
|
-
|
|
98
|
-
|
|
99
|
-
|
|
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. */
|
package/dist/client.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;
|
|
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
|
-
/**
|
|
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);
|
package/dist/errors.d.ts.map
CHANGED
|
@@ -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
|
|
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";
|
package/dist/index.d.ts.map
CHANGED
|
@@ -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;
|
|
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.
|
|
3
|
+
"version": "0.32.0",
|
|
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",
|