@k8ts/metadata 0.10.2 → 0.11.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.
Files changed (94) hide show
  1. package/dist/error.d.ts +1 -1
  2. package/dist/error.d.ts.map +1 -1
  3. package/dist/error.js +3 -3
  4. package/dist/error.js.map +1 -1
  5. package/dist/index.d.ts +2 -3
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/index.js +2 -5
  8. package/dist/index.js.map +1 -1
  9. package/dist/input/dict-input.d.ts +5 -8
  10. package/dist/input/dict-input.d.ts.map +1 -1
  11. package/dist/input/index.d.ts +8 -0
  12. package/dist/input/index.d.ts.map +1 -0
  13. package/dist/input/index.js +24 -0
  14. package/dist/input/index.js.map +1 -0
  15. package/dist/input/key/base.d.ts +6 -0
  16. package/dist/input/key/base.d.ts.map +1 -0
  17. package/dist/input/key/base.js +13 -0
  18. package/dist/input/key/base.js.map +1 -0
  19. package/dist/input/key/domain-prefix.d.ts +8 -0
  20. package/dist/input/key/domain-prefix.d.ts.map +1 -0
  21. package/dist/input/key/domain-prefix.js +19 -0
  22. package/dist/input/key/domain-prefix.js.map +1 -0
  23. package/dist/input/key/metadata-key.d.ts +21 -0
  24. package/dist/input/key/metadata-key.d.ts.map +1 -0
  25. package/dist/input/key/metadata-key.js +82 -0
  26. package/dist/input/key/metadata-key.js.map +1 -0
  27. package/dist/input/key/parse-key.d.ts +10 -0
  28. package/dist/input/key/parse-key.d.ts.map +1 -0
  29. package/dist/{key → input/key}/parse-key.js +10 -8
  30. package/dist/input/key/parse-key.js.map +1 -0
  31. package/dist/input/key/string-types.d.ts +10 -0
  32. package/dist/input/key/string-types.d.ts.map +1 -0
  33. package/dist/input/key/string-types.js +3 -0
  34. package/dist/input/key/string-types.js.map +1 -0
  35. package/dist/input/parse-dict.d.ts +3 -2
  36. package/dist/input/parse-dict.d.ts.map +1 -1
  37. package/dist/input/parse-dict.js +16 -7
  38. package/dist/input/parse-dict.js.map +1 -1
  39. package/dist/meta.d.ts +243 -273
  40. package/dist/meta.d.ts.map +1 -1
  41. package/dist/meta.js +259 -297
  42. package/dist/meta.js.map +1 -1
  43. package/dist/util.d.ts +1 -22
  44. package/dist/util.d.ts.map +1 -1
  45. package/dist/util.js +15 -110
  46. package/dist/util.js.map +1 -1
  47. package/dist/utils/map.d.ts +23 -0
  48. package/dist/utils/map.d.ts.map +1 -0
  49. package/dist/utils/map.js +113 -0
  50. package/dist/utils/map.js.map +1 -0
  51. package/dist/utils/order-meta-keyed-object.d.ts.map +1 -0
  52. package/dist/{order-meta-keyed-object.js → utils/order-meta-keyed-object.js} +2 -1
  53. package/dist/utils/order-meta-keyed-object.js.map +1 -0
  54. package/dist/utils/validate.d.ts +4 -0
  55. package/dist/utils/validate.d.ts.map +1 -0
  56. package/dist/utils/validate.js +38 -0
  57. package/dist/utils/validate.js.map +1 -0
  58. package/package.json +2 -8
  59. package/src/error.ts +1 -1
  60. package/src/index.ts +2 -3
  61. package/src/input/dict-input.ts +5 -12
  62. package/src/input/index.ts +7 -0
  63. package/src/input/key/base.ts +10 -0
  64. package/src/input/key/domain-prefix.ts +14 -0
  65. package/src/input/key/metadata-key.ts +90 -0
  66. package/src/{key → input/key}/parse-key.ts +12 -8
  67. package/src/input/key/string-types.ts +15 -0
  68. package/src/input/parse-dict.ts +23 -13
  69. package/src/meta.ts +391 -417
  70. package/src/util.ts +1 -115
  71. package/src/utils/map.ts +115 -0
  72. package/src/{order-meta-keyed-object.ts → utils/order-meta-keyed-object.ts} +2 -1
  73. package/src/utils/validate.ts +32 -0
  74. package/dist/key/index.d.ts +0 -4
  75. package/dist/key/index.d.ts.map +0 -1
  76. package/dist/key/index.js +0 -11
  77. package/dist/key/index.js.map +0 -1
  78. package/dist/key/parse-key.d.ts +0 -8
  79. package/dist/key/parse-key.d.ts.map +0 -1
  80. package/dist/key/parse-key.js.map +0 -1
  81. package/dist/key/repr.d.ts +0 -36
  82. package/dist/key/repr.d.ts.map +0 -1
  83. package/dist/key/repr.js +0 -113
  84. package/dist/key/repr.js.map +0 -1
  85. package/dist/key/types.d.ts +0 -20
  86. package/dist/key/types.d.ts.map +0 -1
  87. package/dist/key/types.js +0 -11
  88. package/dist/key/types.js.map +0 -1
  89. package/dist/order-meta-keyed-object.d.ts.map +0 -1
  90. package/dist/order-meta-keyed-object.js.map +0 -1
  91. package/src/key/index.ts +0 -4
  92. package/src/key/repr.ts +0 -128
  93. package/src/key/types.ts +0 -21
  94. /package/dist/{order-meta-keyed-object.d.ts → utils/order-meta-keyed-object.d.ts} +0 -0
package/src/meta.ts CHANGED
@@ -1,482 +1,456 @@
1
1
  import { seq } from "doddle"
2
- import { MetadataError } from "./error"
3
- import type { InputMeta, MetaInputParts } from "./input/dict-input"
4
- import { parseKey, parseMetaInput } from "./key"
5
- import { parseInnerKey, parseSectionKey, pNameValue } from "./key/parse-key"
6
- import { checkMetaString, MetadataKey, type DomainPrefix } from "./key/repr"
7
- import { Key } from "./key/types"
8
- import { orderMetaKeyedObject } from "./order-meta-keyed-object"
9
- import { equalsMap, toJS } from "./util"
10
- export type Meta = Meta.Meta
11
- const MetaMarker = Symbol("k8ts.org/metadata")
12
- export interface MetaLike {
13
- readonly [MetaMarker]: true
14
- }
15
- export namespace Meta {
16
- export function _checkNameValue(what: string, v: string) {
17
- if (!pNameValue.parse(v).isOk) {
18
- throw new MetadataError(`Invalid ${what}: ${v}`)
19
- }
20
- checkMetaString(what, v, 63)
21
- }
22
- export function _checkValue(key: string, v: string) {
23
- const parsed = parseKey(key)
24
- if (!(parsed instanceof MetadataKey)) {
25
- throw new MetadataError(`Expected value key, got section key for ${key}`)
26
- }
27
- if (parsed.metaType === "label") {
28
- checkMetaString(`value of ${key}`, v, 63)
29
- } else if (parsed.metaType === "core") {
30
- _checkNameValue(`value of ${key}`, v)
31
- }
32
- }
33
- export type Input = InputMeta
2
+ import { K8tsMetadataError } from "./error"
3
+ import { parseInnerKey, parseKey, parseMetaInput, parseSectionKey } from "./input"
4
+ import type { Metadata_Input, MetaInputParts } from "./input/dict-input"
5
+ import { type Metadata_Key_Domain } from "./input/key/domain-prefix"
6
+ import { Metadata_Key_Value } from "./input/key/metadata-key"
7
+ import type {
8
+ Metadata_Key_sDomain,
9
+ Metadata_Key_sValue,
10
+ NotPrefixed
11
+ } from "./input/key/string-types"
12
+ import { equalsMap, toJS } from "./utils/map"
13
+ import { orderMetaKeyedObject } from "./utils/order-meta-keyed-object"
14
+ import { checkMetadataValue } from "./utils/validate"
15
+
16
+ /**
17
+ * Mutable storage for k8s metadata. K8s metadata includes labels, annotations, and core fields.
18
+ * These are addressed using `CharPrefix`.
19
+ *
20
+ * - **Labels**: `%key` → `metadata.labels.key`
21
+ * - **Annotations**: `^key` → `metadata.annotations.key`
22
+ * - **Comments**: `#key` → Build-time metadata, not manifested in k8s objects.
23
+ * - **Core metadata**: `key` → `metadata.key` (e.g., `name`, `namespace`, etc.)
24
+ *
25
+ * In addition, you can address different metadata keys under the same domain using a `DomainKey`,
26
+ * of the form `example.com/`. When using a `DomainKey`, you use a `CharPrefix` on the inner keys.
27
+ *
28
+ * This lets you add different kinds of metadata under the same domain with ease.
29
+ *
30
+ * @example
31
+ * meta.add("%app", "my-app") // adds label 'app' with value 'my-app'
32
+ * meta.add("example.section/", {
33
+ * "%label1": "value1", // adds `%example.section/label1` with value 'value1'
34
+ * "^annotation1": "value2" // adds `^example.section/annotation1` with value 'value2'
35
+ * })
36
+ */
37
+ export class Metadata implements Iterable<[Metadata_Key_sValue, string]> {
38
+ private readonly _dict: Map<Metadata_Key_sValue, string>
39
+
34
40
  /**
35
- * Mutable storage for k8s metadata. K8s metadata includes labels, annotations, and core fields.
36
- * These are addressed using `CharPrefix`.
41
+ * Constructs a Metadata instance with a single key-value pair.
42
+ *
43
+ * @example
44
+ * const meta = new Metadata("%app", "my-app")
37
45
  *
38
- * - **Labels**: `%key` `metadata.labels.key`
39
- * - **Annotations**: `^key` `metadata.annotations.key`
40
- * - **Comments**: `#key` → Build-time metadata, not manifested in k8s objects.
41
- * - **Core metadata**: `key` `metadata.key` (e.g., `name`, `namespace`, etc.)
46
+ * @param key The value key
47
+ * @param value The value to associate with the key
48
+ */
49
+ constructor(key: Metadata_Key_sValue, value: string)
50
+ /**
51
+ * Constructs a Metadata instance with key-value pairs within a section namespace.
42
52
  *
43
- * In addition, you can address different metadata keys under the same domain using a
44
- * `DomainKey`, of the form `example.com/`. When using a `DomainKey`, you use a `CharPrefix` on
45
- * the inner keys.
53
+ * @example
54
+ * const meta = new Metadata("example.com/", { "%label": "value" })
46
55
  *
47
- * This lets you add different kinds of metadata under the same domain with ease.
56
+ * @param key The section key namespace
57
+ * @param value Nested object containing key-value pairs
58
+ */
59
+ constructor(key: Metadata_Key_sDomain, value: MetaInputParts.Nested)
60
+
61
+ /**
62
+ * Constructs a Metadata instance from an input object or returns an empty Metadata if no input
63
+ * provided.
48
64
  *
49
65
  * @example
50
- * meta.add("%app", "my-app") // adds label 'app' with value 'my-app'
51
- * meta.add("example.section/", {
52
- * "%label1": "value1", // adds `%example.section/label1` with value 'value1'
53
- * "^annotation1": "value2" // adds `^example.section/annotation1` with value 'value2'
54
- * })
66
+ * const meta = new Metadata({ "%app": "my-app", name: "resource" })
67
+ * const empty = new Metadata()
68
+ *
69
+ * @param input Object or map containing key-value pairs
55
70
  */
56
- export class Meta implements Iterable<[MetadataKey, string]>, MetaLike {
57
- readonly [MetaMarker] = true
58
- /**
59
- * Constructs a new Meta instance from a map of key-value pairs. Validates all keys and
60
- * values during construction.
61
- *
62
- * @param _dict Internal map storing metadata key-value pairs
63
- * @throws {MetadataError} If any key or value is invalid
64
- */
65
- constructor(private readonly _dict: Map<string, string>) {
66
- for (const [key, value] of _dict.entries()) {
67
- _checkValue(key, value)
68
- }
71
+ constructor(input?: Metadata_Input)
72
+ /**
73
+ * Constructs a new Metadata instance from a map of key-value pairs. Validates all keys and
74
+ * values during construction.
75
+ *
76
+ * @param _dict Internal map storing metadata key-value pairs
77
+ * @throws {K8tsMetadataError} If any key or value is invalid
78
+ */
79
+ constructor(a?: any, b?: any) {
80
+ this._dict = _pairToMap([a, b])
81
+ for (const [key, value] of this._dict.entries()) {
82
+ checkMetadataValue(key, value)
69
83
  }
84
+ }
70
85
 
71
- /**
72
- * Makes Meta instances iterable, yielding [ValueKey, string] pairs.
73
- *
74
- * @example
75
- * for (const [key, value] of meta) {
76
- * console.log(key.str, value)
77
- * }
78
- */
79
- *[Symbol.iterator]() {
80
- for (const entry of this._dict.entries()) {
81
- yield [parseKey(entry[0]) as MetadataKey, entry[1]] as [MetadataKey, string]
82
- }
83
- }
84
- protected _create(raw: Map<string, string>) {
85
- return new Meta(raw)
86
- }
87
- /**
88
- * Creates a deep clone of this object.
89
- *
90
- * @returns
91
- */
92
- clone() {
93
- return this._create(new Map(this._dict))
86
+ /**
87
+ * Makes Metadata instances iterable, yielding [ValueKey, string] pairs.
88
+ *
89
+ * @example
90
+ * for (const [key, value] of meta) {
91
+ * console.log(key.str, value)
92
+ * }
93
+ */
94
+ *[Symbol.iterator]() {
95
+ for (const entry of this._dict.entries()) {
96
+ yield entry
94
97
  }
98
+ }
95
99
 
96
- /**
97
- * Deletes a single value key from the metadata.
98
- *
99
- * @example
100
- * meta.delete("name") // deletes core metadata 'name'
101
- * meta.delete("%app") // deletes label 'app'
102
- *
103
- * @param key The value key to delete
104
- */
105
- delete(key: Key.Value): Meta
100
+ /**
101
+ * Creates a deep clone of this object.
102
+ *
103
+ * @returns
104
+ */
105
+ clone() {
106
+ return new Metadata(this)
107
+ }
108
+
109
+ /**
110
+ * Deletes a single value key from the metadata.
111
+ *
112
+ * @example
113
+ * meta.delete("name") // deletes core metadata 'name'
114
+ * meta.delete("%app") // deletes label 'app'
115
+ *
116
+ * @param key The value key to delete
117
+ */
118
+ delete(key: Metadata_Key_sValue): Metadata
106
119
 
107
- /**
108
- * Deletes specific keys under a domain prefix.
109
- *
110
- * @example
111
- * meta.delete("example.com/", "key1", "key2") // deletes specific keys in section
112
- *
113
- * @param ns The section key namespace
114
- * @param keys Specific value keys within the section to delete
115
- */
116
- delete(ns: Key.Domain, ...keys: Key.Value[]): Meta
120
+ /**
121
+ * Deletes specific keys under a domain prefix.
122
+ *
123
+ * @example
124
+ * meta.delete("example.com/", "key1", "key2") // deletes specific keys in section
125
+ *
126
+ * @param ns The section key namespace
127
+ * @param keys Specific value keys within the section to delete
128
+ */
129
+ delete(ns: Metadata_Key_sDomain, keys: Metadata_Key_sValue[]): Metadata
117
130
 
118
- /**
119
- * Deletes all keys within a section namespace.
120
- *
121
- * @example
122
- * meta.delete("example.com/") // deletes all keys in section
123
- *
124
- * @param ns The section key namespace to delete
125
- */
126
- delete(ns: Key.Domain): Meta
127
- delete(a: any, ...rest: any[]) {
128
- if (a.endsWith("/")) {
129
- const sectionKey = parseSectionKey(a)
130
- const onlyKeys = rest.map(parseInnerKey)
131
- if (onlyKeys.length > 0) {
132
- var deleteOnly = (key: MetadataKey) => onlyKeys.some(ok => ok.equals(key))
133
- } else {
134
- var deleteOnly = (_key: MetadataKey) => _key.domain().equals(sectionKey)
135
- }
136
- for (const k of this._keys) {
137
- if (deleteOnly(k)) {
138
- this._dict.delete(k.str)
139
- }
140
- }
131
+ /**
132
+ * Deletes all keys within a section namespace.
133
+ *
134
+ * @example
135
+ * meta.delete("example.com/") // deletes all keys in section
136
+ *
137
+ * @param ns The section key namespace to delete
138
+ */
139
+ delete(ns: Metadata_Key_sDomain): Metadata
140
+ delete(a: any, ...rest: any[]) {
141
+ if (a.endsWith("/")) {
142
+ const sectionKey = parseSectionKey(a)
143
+ const onlyKeys = rest.map(parseInnerKey)
144
+ if (onlyKeys.length > 0) {
145
+ var deleteOnly = (key: Metadata_Key_Value) => onlyKeys.some(ok => ok.equals(key))
141
146
  } else {
142
- this._dict.delete(a)
147
+ var deleteOnly = (_key: Metadata_Key_Value) => _key.domain().equals(sectionKey)
143
148
  }
144
- return this
149
+ for (const k of this._keys) {
150
+ if (deleteOnly(k)) {
151
+ this._dict.delete(k.str)
152
+ }
153
+ }
154
+ } else {
155
+ this._dict.delete(a)
145
156
  }
157
+ return this
158
+ }
146
159
 
147
- /**
148
- * Adds a single key-value pair to the metadata. Throws if the key already exists.
149
- *
150
- * @example
151
- * meta.add("%app", "my-app") // adds label
152
- * meta.add("name", "my-resource") // adds core metadata
153
- *
154
- * @param key The value key to add
155
- * @param value The value to associate with the key
156
- */
157
- add(key: Key.Value, value?: string): Meta
160
+ /**
161
+ * Adds a single key-value pair to the metadata. Throws if the key already exists.
162
+ *
163
+ * @example
164
+ * meta.add("%app", "my-app") // adds label
165
+ * meta.add("name", "my-resource") // adds core metadata
166
+ *
167
+ * @param key The value key to add
168
+ * @param value The value to associate with the key
169
+ */
170
+ add(key: Metadata_Key_sValue, value?: string): Metadata
158
171
 
159
- /**
160
- * Adds a nested object of key-value pairs within a section namespace. Throws if any key
161
- * already exists.
162
- *
163
- * @example
164
- * meta.add("example.com/", { "%label": "value", "^annotation": "data" })
165
- *
166
- * @param key The section key namespace
167
- * @param value Nested object containing key-value pairs
168
- */
169
- add(key: Key.Domain, value: MetaInputParts.Nested): Meta
172
+ /**
173
+ * Adds a nested object of key-value pairs within a section namespace. Throws if any key already
174
+ * exists.
175
+ *
176
+ * @example
177
+ * meta.add("example.com/", { "%label": "value", "^annotation": "data" })
178
+ *
179
+ * @param key The section key namespace
180
+ * @param value Nested object containing key-value pairs
181
+ */
182
+ add<Domain extends NotPrefixed<Domain>>(key: Domain, value: MetaInputParts.Nested): Metadata
170
183
 
171
- /**
172
- * Adds multiple key-value pairs from an input object. Throws if any key already exists.
173
- *
174
- * @example
175
- * meta.add({ "%app": "my-app", name: "my-resource" })
176
- *
177
- * @param input Object or map containing key-value pairs to add
178
- */
179
- add(input: InputMeta): Meta
180
- add(a: any, b?: any) {
181
- const parsed = _pairToMap([a, b])
182
- for (const [k, v] of parsed) {
183
- if (this._dict.has(k)) {
184
- const prev = this._dict.get(k)
185
- throw new MetadataError(`Duplicate entry for ${k}, was ${prev} now ${v}`, {
186
- key: (k as any).str
187
- })
188
- }
189
- this._dict.set(k, v)
184
+ /**
185
+ * Adds multiple key-value pairs from an input object. Throws if any key already exists.
186
+ *
187
+ * @example
188
+ * meta.add({ "%app": "my-app", name: "my-resource" })
189
+ *
190
+ * @param input Object or map containing key-value pairs to add
191
+ */
192
+ add(input: Metadata_Input): Metadata
193
+ add(a: any, b?: any) {
194
+ const parsed = _pairToMap([a, b])
195
+ for (const [k, v] of parsed) {
196
+ if (this._dict.has(k)) {
197
+ const prev = this._dict.get(k)
198
+ throw new K8tsMetadataError(`Duplicate entry for ${k}, was ${prev} now ${v}`, {
199
+ key: (k as any).str
200
+ })
190
201
  }
191
- return this
202
+ this._dict.set(k, v)
192
203
  }
204
+ return this
205
+ }
193
206
 
194
- /**
195
- * Compares this Meta instance to another for equality. Two instances are equal if they
196
- * contain the same key-value pairs.
197
- *
198
- * @param other The other Meta instance or input to compare against
199
- * @returns Whether the two Meta instances are equal
200
- */
201
- equals(other: Meta.Input) {
202
- return equalsMap(this._dict, make(other)._dict)
203
- }
207
+ /**
208
+ * Compares this Metadata instance to another for equality. Two instances are equal if they
209
+ * contain the same key-value pairs.
210
+ *
211
+ * @param other The other Metadata instance or input to compare against
212
+ * @returns Whether the two Metadata instances are equal
213
+ */
214
+ equals(other: Metadata_Input) {
215
+ return equalsMap(this._dict, new Metadata(other)._dict)
216
+ }
204
217
 
205
- /**
206
- * Overwrites a single key-value pair, replacing any existing value.
207
- *
208
- * @example
209
- * meta.overwrite("%app", "new-app") // replaces existing label value
210
- *
211
- * @param key The value key to overwrite
212
- * @param value The new value (undefined removes the key)
213
- */
214
- overwrite(key: Key.Value, value: string | undefined): Meta
218
+ /**
219
+ * Overwrites a single key-value pair, replacing any existing value.
220
+ *
221
+ * @example
222
+ * meta.overwrite("%app", "new-app") // replaces existing label value
223
+ *
224
+ * @param key The value key to overwrite
225
+ * @param value The new value (undefined removes the key)
226
+ */
227
+ overwrite(key: Metadata_Key_sValue, value: string | undefined): Metadata
215
228
 
216
- /**
217
- * Overwrites key-value pairs within a section namespace.
218
- *
219
- * @example
220
- * meta.overwrite("example.com/", { "%label": "new-value" })
221
- *
222
- * @param key The section key namespace
223
- * @param value Nested object containing key-value pairs to overwrite
224
- */
225
- overwrite(key: Key.Domain, value: MetaInputParts.Nested): Meta
229
+ /**
230
+ * Overwrites key-value pairs within a section namespace.
231
+ *
232
+ * @example
233
+ * meta.overwrite("example.com/", { "%label": "new-value" })
234
+ *
235
+ * @param key The section key namespace
236
+ * @param value Nested object containing key-value pairs to overwrite
237
+ */
238
+ overwrite<Domain extends NotPrefixed<Domain>>(
239
+ key: Domain,
240
+ value: MetaInputParts.Nested
241
+ ): Metadata
226
242
 
227
- /**
228
- * Overwrites multiple key-value pairs from an input object.
229
- *
230
- * @example
231
- * meta.overwrite({ "%app": "new-app", name: "new-name" })
232
- *
233
- * @param input Object or map containing key-value pairs to overwrite
234
- */
235
- overwrite(input?: InputMeta): Meta
236
- overwrite(a?: any, b?: any) {
237
- if (a === undefined) {
238
- return this
239
- }
240
- const fromPair = _pairToMap([a, b])
241
- for (const [k, v] of fromPair.entries()) {
242
- this._dict.set(k, v)
243
- }
243
+ /**
244
+ * Overwrites multiple key-value pairs from an input object.
245
+ *
246
+ * @example
247
+ * meta.overwrite({ "%app": "new-app", name: "new-name" })
248
+ *
249
+ * @param input Object or map containing key-value pairs to overwrite
250
+ */
251
+ overwrite(input?: Metadata_Input): Metadata
252
+ overwrite(a?: any, b?: any) {
253
+ if (a === undefined) {
244
254
  return this
245
255
  }
246
-
247
- /**
248
- * Checks if a key with a given domain prefix exists in the metadata.
249
- *
250
- * @example
251
- * meta.has("example.com/") // Checks for any key with this domain
252
- * meta.has("%app") // Checks for the label 'app'
253
- *
254
- * @param domainPrefix The domain prefix to check for
255
- * @returns True if any keys exist under the specified domain prefix, false otherwise
256
- */
257
- has(domainPrefix: Key.Domain): boolean
258
- /** @param key */
259
- has(key: Key.Value): boolean
260
- has(key: any) {
261
- const parsed = parseKey(key)
262
- if (parsed instanceof MetadataKey) {
263
- return this._dict.has(key)
264
- } else {
265
- return this._matchDomainPrefixes(parsed).size > 0
266
- }
256
+ const fromPair = _pairToMap([a, b])
257
+ for (const [k, v] of fromPair.entries()) {
258
+ this._dict.set(k, v)
267
259
  }
260
+ return this
261
+ }
268
262
 
269
- /**
270
- * Retrieves the value for the specified key. Throws if the key doesn't exist.
271
- *
272
- * @example
273
- * const appName = meta.get("%app")
274
- *
275
- * @param key The value key to retrieve
276
- * @returns The value associated with the key
277
- * @throws {MetadataError} If the key is not found
278
- */
279
- get(key: Key.Value) {
280
- const parsed = parseKey(key)
281
- const v = this._dict.get(key)
282
- if (v === undefined) {
283
- throw new MetadataError(`Key ${key} not found!`, { key })
284
- }
285
- return v
263
+ /**
264
+ * Checks if a key with a given domain prefix exists in the metadata.
265
+ *
266
+ * @example
267
+ * meta.has("example.com/") // Checks for any key with this domain
268
+ * meta.has("%app") // Checks for the label 'app'
269
+ *
270
+ * @param domainPrefix The domain prefix to check for
271
+ * @returns True if any keys exist under the specified domain prefix, false otherwise
272
+ */
273
+ has<Domain extends NotPrefixed<Domain>>(domainPrefix: Domain): boolean
274
+ /** @param key */
275
+ has(key: Metadata_Key_sValue): boolean
276
+ has(key: any) {
277
+ const parsed = parseKey(key)
278
+ if (parsed instanceof Metadata_Key_Value) {
279
+ return this._dict.has(key)
280
+ } else {
281
+ return this._matchDomainPrefixes(parsed).size > 0
286
282
  }
283
+ }
287
284
 
288
- /**
289
- * Attempts to retrieve the value for the specified key, returning a fallback if not found.
290
- *
291
- * @example
292
- * const appName = meta.tryGet("%app", "default-app")
293
- *
294
- * @param key The value key to retrieve
295
- * @param fallback Optional fallback value if key doesn't exist
296
- * @returns The value associated with the key, or the fallback value
297
- * @throws {MetadataError} If a domain key is provided instead of a value key
298
- */
299
- tryGet(key: Key.Value, fallback?: string) {
300
- const parsed = parseKey(key)
301
- if (!(parsed instanceof MetadataKey)) {
302
- throw new MetadataError("Unexpected domain key!", { key })
303
- }
304
- return this._dict.get(key) ?? fallback
285
+ /**
286
+ * Retrieves the value for the specified key. Throws if the key doesn't exist.
287
+ *
288
+ * @example
289
+ * const appName = meta.get("%app")
290
+ *
291
+ * @param key The value key to retrieve
292
+ * @returns The value associated with the key
293
+ * @throws {K8tsMetadataError} If the key is not found
294
+ */
295
+ get(key: Metadata_Key_sValue) {
296
+ const parsed = parseKey(key)
297
+ const v = this._dict.get(key)
298
+ if (v === undefined) {
299
+ throw new K8tsMetadataError(`Key ${key} not found!`, { key })
305
300
  }
301
+ return v
302
+ }
306
303
 
307
- private _matchDomainPrefixes(key: DomainPrefix) {
308
- return seq(this)
309
- .filter(([k, v]) => k.parent?.equals(key) ?? false)
310
- .toMap(x => [x[0].str, x[1]] as const)
311
- .pull()
304
+ /**
305
+ * Attempts to retrieve the value for the specified key, returning a fallback if not found.
306
+ *
307
+ * @example
308
+ * const appName = meta.tryGet("%app", "default-app")
309
+ *
310
+ * @param key The value key to retrieve
311
+ * @param fallback Optional fallback value if key doesn't exist
312
+ * @returns The value associated with the key, or the fallback value
313
+ * @throws {K8tsMetadataError} If a domain key is provided instead of a value key
314
+ */
315
+ tryGet(key: Metadata_Key_sValue, fallback?: string) {
316
+ const parsed = parseKey(key)
317
+ if (!(parsed instanceof Metadata_Key_Value)) {
318
+ throw new K8tsMetadataError("Unexpected domain key!", { key })
312
319
  }
320
+ return this._dict.get(key) ?? fallback
321
+ }
313
322
 
314
- /**
315
- * Creates a new Meta instance containing only some of the keys. You can pass both entire
316
- * keys and domain prefixes to include all keys under that domain.
317
- *
318
- * @example
319
- * const subset = meta.pick("%app", "name", "example.com/")
320
- *
321
- * @param keySpecs Keys or domain prefixes to include in the result
322
- * @returns A new Meta instance with only the picked keys
323
- */
324
- pick(...keySpecs: (Key.Domain | Key.Value)[]) {
325
- const parsed = keySpecs.map(parseKey)
326
- const keyStrSet = new Set<string>()
327
- for (const key of parsed) {
328
- if (key instanceof MetadataKey) {
329
- keyStrSet.add(key.str)
330
- } else {
331
- const sectionKeys = this._matchDomainPrefixes(key)
332
- for (const k of sectionKeys.keys()) {
333
- keyStrSet.add(k)
334
- }
335
- }
336
- }
337
- const out = new Map<string, string>()
338
- for (const [k, v] of this._dict.entries()) {
339
- if (keyStrSet.has(k)) out.set(k, v)
340
- }
341
- return this._create(out)
342
- }
323
+ private get _parsedPairs() {
324
+ return seq(this._dict)
325
+ .map(([k, v]) => [parseKey(k) as Metadata_Key_Value, v] as const)
326
+ .toArray()
327
+ .pull()
328
+ }
343
329
 
344
- private _prefixed(prefix: string) {
345
- const out: { [k: string]: string } = {}
346
- for (const [k, v] of this) {
347
- if (k.prefix() === prefix) {
348
- out[k.suffix] = v
330
+ private _matchDomainPrefixes(key: Metadata_Key_Domain) {
331
+ return seq(this._parsedPairs)
332
+ .filter(([k, v]) => k.parent?.equals(key) ?? false)
333
+ .toMap(x => [x[0].str, x[1]] as const)
334
+ .pull()
335
+ }
336
+
337
+ /**
338
+ * Creates a new Metadata instance containing only some of the keys. You can pass both entire
339
+ * keys and domain prefixes to include all keys under that domain.
340
+ *
341
+ * @example
342
+ * const subset = meta.pick("%app", "name", "example.com/")
343
+ *
344
+ * @param keySpecs Keys or domain prefixes to include in the result
345
+ * @returns A new Metadata instance with only the picked keys
346
+ */
347
+ pick(...keySpecs: (Metadata_Key_sDomain | Metadata_Key_sValue)[]) {
348
+ const parsed = keySpecs.map(parseKey)
349
+ const keyStrSet = new Set<string>()
350
+ for (const key of parsed) {
351
+ if (key instanceof Metadata_Key_Value) {
352
+ keyStrSet.add(key.str)
353
+ } else {
354
+ const sectionKeys = this._matchDomainPrefixes(key)
355
+ for (const k of sectionKeys.keys()) {
356
+ keyStrSet.add(k)
349
357
  }
350
358
  }
351
- return orderMetaKeyedObject(out)
352
359
  }
353
-
354
- /**
355
- * Returns all labels as a plain object that can be embedded into a k8s manifest, with keys
356
- * in canonical order.
357
- *
358
- * @example
359
- * const labels = Meta.make({
360
- * "%app": "my-app",
361
- * "%tier": "backend"
362
- * }).labels
363
- * // { app: "my-app", tier: "backend" }
364
- */
365
- get labels() {
366
- return this._prefixed("%")
367
- }
368
-
369
- /**
370
- * Returns all annotations as a plain object that can be embedded into a k8s manifest, with
371
- * keys in canonical order.
372
- *
373
- * @example
374
- * const annotations = Meta.make({
375
- * "^note": "This is important",
376
- * "^description": "Detailed info"
377
- * }).annotations
378
- * // { note: "This is important", description: "Detailed info" }
379
- */
380
- get annotations() {
381
- return this._prefixed("^")
360
+ const out = new Map<Metadata_Key_sValue, string>()
361
+ for (const [k, v] of this._dict.entries()) {
362
+ if (keyStrSet.has(k)) out.set(k as any, v)
382
363
  }
364
+ return new Metadata(out)
365
+ }
383
366
 
384
- /**
385
- * Returns all comments (build-time metadata) as a plain object, with keys in canonical
386
- * order.
387
- *
388
- * @example
389
- * const comments = Meta.make({
390
- * "#note": "Internal use only"
391
- * }).comments
392
- * // { note: "Internal use only" }
393
- */
394
- get comments() {
395
- return this._prefixed("#")
396
- }
397
- /**
398
- * Returns all core metadata fields (`name` and `namespace`) as a plain object that can be
399
- * embedded into a k8s manifest, with keys in canonical order.
400
- *
401
- * @example
402
- * const core = meta.core // { name: "my-resource", namespace: "default" }
403
- */
404
- get core() {
405
- return this._prefixed("") as {
406
- [key in Key.Special]?: string
367
+ private _prefixed(prefix: string) {
368
+ const out: { [k: string]: string } = {}
369
+ for (const [k, v] of this._parsedPairs) {
370
+ if (k.prefix() === prefix) {
371
+ out[k.suffix] = v
407
372
  }
408
373
  }
409
-
410
- /**
411
- * Returns all metadata key-value pairs as a flat JavaScript object, with each key prefixed
412
- * appropriately.
413
- *
414
- * @example
415
- * const all = Meta.make({
416
- * "%app": "my-app",
417
- * "^note": "This is important",
418
- * name: "my-resource"
419
- * }).values
420
- * // { "%app": "my-app", "^note": "This is important", "name": "my-resource" }
421
- */
422
- get values() {
423
- return toJS(this._dict)
424
- }
425
-
426
- private get _keys(): MetadataKey[] {
427
- return seq(this)
428
- .map(([k, v]) => k)
429
- .toArray()
430
- .pull()
431
- }
374
+ return orderMetaKeyedObject(out)
432
375
  }
433
376
 
434
377
  /**
435
- * Creates a Meta instance with a single key-value pair.
378
+ * Returns all labels as a plain object that can be embedded into a k8s manifest, with keys in
379
+ * canonical order.
436
380
  *
437
381
  * @example
438
- * const meta = Meta.make("%app", "my-app")
439
- *
440
- * @param key The value key
441
- * @param value The value to associate with the key
382
+ * const labels = Metadata.make({
383
+ * "%app": "my-app",
384
+ * "%tier": "backend"
385
+ * }).labels
386
+ * // { app: "my-app", tier: "backend" }
442
387
  */
443
- export function make(key: Key.Value, value: string): Meta
388
+ get labels() {
389
+ return this._prefixed("%")
390
+ }
444
391
 
445
392
  /**
446
- * Creates a Meta instance with key-value pairs within a section namespace.
393
+ * Returns all annotations as a plain object that can be embedded into a k8s manifest, with keys
394
+ * in canonical order.
447
395
  *
448
396
  * @example
449
- * const meta = Meta.make("example.com/", { "%label": "value" })
450
- *
451
- * @param key The section key namespace
452
- * @param value Nested object containing key-value pairs
397
+ * const annotations = Metadata.make({
398
+ * "^note": "This is important",
399
+ * "^description": "Detailed info"
400
+ * }).annotations
401
+ * // { note: "This is important", description: "Detailed info" }
453
402
  */
454
- export function make(key: Key.Domain, value: MetaInputParts.Nested): Meta
403
+ get annotations() {
404
+ return this._prefixed("^")
405
+ }
455
406
 
456
407
  /**
457
- * Creates a Meta instance from an input object or returns an empty Meta if no input provided.
408
+ * Returns all comments (build-time metadata) as a plain object, with keys in canonical order.
458
409
  *
459
410
  * @example
460
- * const meta = Meta.make({ "%app": "my-app", name: "resource" })
461
- * const empty = Meta.make()
411
+ * const comments = Metadata.make({
412
+ * "#note": "Internal use only"
413
+ * }).comments
414
+ * // { note: "Internal use only" }
415
+ */
416
+ get comments() {
417
+ return this._prefixed("#")
418
+ }
419
+
420
+ /**
421
+ * Returns all metadata key-value pairs as a flat JavaScript object, with each key prefixed
422
+ * appropriately.
462
423
  *
463
- * @param input Object or map containing key-value pairs
424
+ * @example
425
+ * const all = Metadata.make({
426
+ * "%app": "my-app",
427
+ * "^note": "This is important",
428
+ * name: "my-resource"
429
+ * }).values
430
+ * // { "%app": "my-app", "^note": "This is important", "name": "my-resource" }
464
431
  */
465
- export function make(input?: InputMeta): Meta
466
- export function make(a?: any, b?: any) {
467
- return new Meta(_pairToMap([a, b]))
432
+ get record() {
433
+ return toJS(this._dict)
468
434
  }
469
- function _pairToObject(pair: [string | MetadataKey, string | object] | [object]) {
470
- let [key, value] = pair
471
- key = key instanceof MetadataKey ? key.str : key
472
- if (typeof key === "string") {
473
- return {
474
- [key]: value as string
475
- }
476
- }
477
- return key
435
+
436
+ private get _keys(): Metadata_Key_Value[] {
437
+ return seq(this._parsedPairs)
438
+ .map(([k, v]) => k)
439
+ .toArray()
440
+ .pull()
478
441
  }
479
- function _pairToMap(pair: [string | MetadataKey, string | object] | [object]) {
480
- return parseMetaInput(_pairToObject(pair))
442
+ }
443
+
444
+ function _pairToObject(pair: [string | Metadata_Key_Value, string | object] | [object]) {
445
+ let [key, value] = pair
446
+ key = key instanceof Metadata_Key_Value ? key.str : key
447
+ if (typeof key === "string") {
448
+ return {
449
+ [key]: value as string
450
+ }
481
451
  }
452
+ return key
453
+ }
454
+ function _pairToMap(pair: [Metadata_Key_sValue, string | object] | [object]) {
455
+ return parseMetaInput(_pairToObject(pair))
482
456
  }