@geekmidas/manifest 10.0.0-alpha.99 → 10.0.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/CHANGELOG.md +117 -0
- package/dist/index.cjs +8 -1232
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +40 -1381
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +40 -1381
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +7 -1193
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -13
- package/src/index.ts +127 -0
- package/tsconfig.json +9 -0
- package/tsdown.config.ts +3 -0
package/dist/index.cjs
CHANGED
|
@@ -1,1235 +1,11 @@
|
|
|
1
|
-
|
|
2
|
-
//#region
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
var __getProtoOf = Object.getPrototypeOf;
|
|
8
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
-
var __commonJSMin = (cb, mod) => () => (mod || (cb((mod = { exports: {} }).exports, mod), cb = null), mod.exports);
|
|
10
|
-
var __copyProps = (to, from, except, desc) => {
|
|
11
|
-
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
12
|
-
key = keys[i];
|
|
13
|
-
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
|
|
14
|
-
get: ((k) => from[k]).bind(null, key),
|
|
15
|
-
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
16
|
-
});
|
|
17
|
-
}
|
|
18
|
-
return to;
|
|
19
|
-
};
|
|
20
|
-
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule || !__hasOwnProp.call(mod, "default") ? __defProp(target, "default", {
|
|
21
|
-
value: mod,
|
|
22
|
-
enumerable: true
|
|
23
|
-
}) : target, mod));
|
|
24
|
-
//#endregion
|
|
25
|
-
//#region src/declaration.ts
|
|
26
|
-
/**
|
|
27
|
-
* The version used when a database names none.
|
|
28
|
-
*
|
|
29
|
-
* The point is not which number this is but that there is only one of them.
|
|
30
|
-
* Local ran 18 while Aurora provisioned its own default of 17.7, and nothing in
|
|
31
|
-
* any declaration recorded the difference — a stage could behave differently
|
|
32
|
-
* from a developer's machine for a reason neither could see. Both now read
|
|
33
|
-
* this.
|
|
34
|
-
*/
|
|
35
|
-
const DEFAULT_POSTGRES_VERSION = 18;
|
|
36
|
-
/**
|
|
37
|
-
* What a `Telemetry` node provides to each process with an edge to it.
|
|
38
|
-
*
|
|
39
|
-
* `OTEL_SERVICE_NAME` is resolved per process — the app's name — so it is
|
|
40
|
-
* listed here but composed by whichever target hands it over.
|
|
41
|
-
*/
|
|
42
|
-
const TELEMETRY_KEYS = [
|
|
43
|
-
"OTEL_EXPORTER_OTLP_ENDPOINT",
|
|
44
|
-
"OTEL_EXPORTER_OTLP_HEADERS",
|
|
45
|
-
"OTEL_SERVICE_NAME",
|
|
46
|
-
"OTEL_TRACES_SAMPLER",
|
|
47
|
-
"OTEL_TRACES_SAMPLER_ARG"
|
|
48
|
-
];
|
|
49
|
-
/**
|
|
50
|
-
* What each kind may derive from.
|
|
51
|
-
*
|
|
52
|
-
* Small enough to state exhaustively, and stating it makes cycles impossible
|
|
53
|
-
* without a graph walk: readers are terminal, so no chain can return to its
|
|
54
|
-
* start. There is no `writer` — the database *is* the writer, which is what
|
|
55
|
-
* keeps a replica from being reached by accident.
|
|
56
|
-
*/
|
|
57
|
-
const DERIVES_FROM = {
|
|
58
|
-
"database-reader": ["database", "database-schema"],
|
|
59
|
-
"database-schema": ["database"],
|
|
60
|
-
"file-server": ["objects"],
|
|
61
|
-
cache: ["database", "database-schema"]
|
|
62
|
-
};
|
|
63
|
-
/**
|
|
64
|
-
* Which provided values may be shipped to a browser.
|
|
65
|
-
*
|
|
66
|
-
* Drives client-side prefixing (`VITE_`, `NEXT_PUBLIC_`) and nothing else — it
|
|
67
|
-
* is not a restriction on what may be depended on, since a server-side consumer
|
|
68
|
-
* can legitimately use any of them. A bucket's `url` presigns and stays private.
|
|
69
|
-
*/
|
|
70
|
-
const PUBLIC = {
|
|
71
|
-
objects: [],
|
|
72
|
-
"file-server": ["url"],
|
|
73
|
-
email: ["inboxUrl"],
|
|
74
|
-
database: [],
|
|
75
|
-
"database-reader": [],
|
|
76
|
-
"database-schema": [],
|
|
77
|
-
cache: [],
|
|
78
|
-
secret: [],
|
|
79
|
-
credential: [],
|
|
80
|
-
encryption: [],
|
|
81
|
-
"rest-api": ["url"],
|
|
82
|
-
queue: [],
|
|
83
|
-
topic: [],
|
|
84
|
-
oidc: ["issuer", "audience"],
|
|
85
|
-
"external-api": [],
|
|
86
|
-
function: [],
|
|
87
|
-
cron: [],
|
|
88
|
-
"mobile-app": [],
|
|
89
|
-
site: ["url"],
|
|
90
|
-
worker: [],
|
|
91
|
-
telemetry: []
|
|
92
|
-
};
|
|
93
|
-
//#endregion
|
|
94
|
-
//#region src/errors.ts
|
|
95
|
-
/**
|
|
96
|
-
* Manifest errors.
|
|
97
|
-
*
|
|
98
|
-
* Messages state the rule, which is constant; the offending value is a field.
|
|
99
|
-
* An interpolated message cannot be matched on, reads differently every time it
|
|
100
|
-
* is thrown, and carries user input into every log line that touches it.
|
|
101
|
-
*/
|
|
102
|
-
/** A construct id that cannot survive the names derived from it. */
|
|
103
|
-
var InvalidConstructId = class extends Error {
|
|
104
|
-
/** What was passed in. */
|
|
105
|
-
input;
|
|
106
|
-
/** What canonicalising it produced, which is what failed the rule. */
|
|
107
|
-
canonical;
|
|
108
|
-
constructor(input, canonical) {
|
|
109
|
-
super("A construct id must start with a letter and contain only letters and digits");
|
|
110
|
-
this.name = "InvalidConstructId";
|
|
111
|
-
this.input = input;
|
|
112
|
-
this.canonical = canonical;
|
|
113
|
-
}
|
|
114
|
-
};
|
|
115
|
-
/** A derived construct naming a parent the manifest does not contain. */
|
|
116
|
-
var UnknownParent = class extends Error {
|
|
117
|
-
/** The derived construct. */
|
|
118
|
-
id;
|
|
119
|
-
/** The parent it named. */
|
|
120
|
-
of;
|
|
121
|
-
/** Ids the manifest does contain, for the caller to match against. */
|
|
122
|
-
available;
|
|
123
|
-
constructor(id, of, available) {
|
|
124
|
-
super("A derived construct must name a parent present in the manifest");
|
|
125
|
-
this.name = "UnknownParent";
|
|
126
|
-
this.id = id;
|
|
127
|
-
this.of = of;
|
|
128
|
-
this.available = available;
|
|
129
|
-
}
|
|
130
|
-
};
|
|
131
|
-
/**
|
|
132
|
-
* A derived construct naming a parent that may not vend it — a reader of a
|
|
133
|
-
* reader, a schema of a schema.
|
|
134
|
-
*/
|
|
135
|
-
var IllegalDerivation = class extends Error {
|
|
136
|
-
id;
|
|
137
|
-
kind;
|
|
138
|
-
/** The kind of the parent it named. */
|
|
139
|
-
parentKind;
|
|
140
|
-
/** The parent kinds that may vend this one. */
|
|
141
|
-
allowed;
|
|
142
|
-
constructor(id, kind, parentKind, allowed) {
|
|
143
|
-
super("A derived construct must name a parent whose kind may vend it");
|
|
144
|
-
this.name = "IllegalDerivation";
|
|
145
|
-
this.id = id;
|
|
146
|
-
this.kind = kind;
|
|
147
|
-
this.parentKind = parentKind;
|
|
148
|
-
this.allowed = allowed;
|
|
149
|
-
}
|
|
150
|
-
};
|
|
151
|
-
//#endregion
|
|
152
|
-
//#region src/naming.ts
|
|
153
|
-
var import_lodash_snakecase = /* @__PURE__ */ __toESM((/* @__PURE__ */ __commonJSMin(((exports, module) => {
|
|
154
|
-
/** `Object#toString` result references. */
|
|
155
|
-
var symbolTag = "[object Symbol]";
|
|
156
|
-
/** Used to match words composed of alphanumeric characters. */
|
|
157
|
-
var reAsciiWord = /[^\x00-\x2f\x3a-\x40\x5b-\x60\x7b-\x7f]+/g;
|
|
158
|
-
/** Used to match Latin Unicode letters (excluding mathematical operators). */
|
|
159
|
-
var reLatin = /[\xc0-\xd6\xd8-\xf6\xf8-\xff\u0100-\u017f]/g;
|
|
160
|
-
/** Used to compose unicode character classes. */
|
|
161
|
-
var rsAstralRange = "\\ud800-\\udfff";
|
|
162
|
-
var rsComboMarksRange = "\\u0300-\\u036f\\ufe20-\\ufe23";
|
|
163
|
-
var rsComboSymbolsRange = "\\u20d0-\\u20f0";
|
|
164
|
-
var rsDingbatRange = "\\u2700-\\u27bf";
|
|
165
|
-
var rsLowerRange = "a-z\\xdf-\\xf6\\xf8-\\xff";
|
|
166
|
-
var rsMathOpRange = "\\xac\\xb1\\xd7\\xf7";
|
|
167
|
-
var rsNonCharRange = "\\x00-\\x2f\\x3a-\\x40\\x5b-\\x60\\x7b-\\xbf";
|
|
168
|
-
var rsPunctuationRange = "\\u2000-\\u206f";
|
|
169
|
-
var rsSpaceRange = " \\t\\x0b\\f\\xa0\\ufeff\\n\\r\\u2028\\u2029\\u1680\\u180e\\u2000\\u2001\\u2002\\u2003\\u2004\\u2005\\u2006\\u2007\\u2008\\u2009\\u200a\\u202f\\u205f\\u3000";
|
|
170
|
-
var rsUpperRange = "A-Z\\xc0-\\xd6\\xd8-\\xde";
|
|
171
|
-
var rsVarRange = "\\ufe0e\\ufe0f";
|
|
172
|
-
var rsBreakRange = rsMathOpRange + rsNonCharRange + rsPunctuationRange + rsSpaceRange;
|
|
173
|
-
/** Used to compose unicode capture groups. */
|
|
174
|
-
var rsApos = "['’]";
|
|
175
|
-
var rsBreak = "[" + rsBreakRange + "]";
|
|
176
|
-
var rsCombo = "[" + rsComboMarksRange + rsComboSymbolsRange + "]";
|
|
177
|
-
var rsDigits = "\\d+";
|
|
178
|
-
var rsDingbat = "[" + rsDingbatRange + "]";
|
|
179
|
-
var rsLower = "[" + rsLowerRange + "]";
|
|
180
|
-
var rsMisc = "[^" + rsAstralRange + rsBreakRange + rsDigits + rsDingbatRange + rsLowerRange + rsUpperRange + "]";
|
|
181
|
-
var rsModifier = "(?:" + rsCombo + "|\\ud83c[\\udffb-\\udfff])";
|
|
182
|
-
var rsNonAstral = "[^" + rsAstralRange + "]";
|
|
183
|
-
var rsRegional = "(?:\\ud83c[\\udde6-\\uddff]){2}";
|
|
184
|
-
var rsSurrPair = "[\\ud800-\\udbff][\\udc00-\\udfff]";
|
|
185
|
-
var rsUpper = "[" + rsUpperRange + "]";
|
|
186
|
-
var rsZWJ = "\\u200d";
|
|
187
|
-
/** Used to compose unicode regexes. */
|
|
188
|
-
var rsLowerMisc = "(?:" + rsLower + "|" + rsMisc + ")";
|
|
189
|
-
var rsUpperMisc = "(?:" + rsUpper + "|" + rsMisc + ")";
|
|
190
|
-
var rsOptLowerContr = "(?:" + rsApos + "(?:d|ll|m|re|s|t|ve))?";
|
|
191
|
-
var rsOptUpperContr = "(?:" + rsApos + "(?:D|LL|M|RE|S|T|VE))?";
|
|
192
|
-
var reOptMod = rsModifier + "?";
|
|
193
|
-
var rsOptVar = "[" + rsVarRange + "]?";
|
|
194
|
-
var rsOptJoin = "(?:" + rsZWJ + "(?:" + [
|
|
195
|
-
rsNonAstral,
|
|
196
|
-
rsRegional,
|
|
197
|
-
rsSurrPair
|
|
198
|
-
].join("|") + ")" + rsOptVar + reOptMod + ")*";
|
|
199
|
-
var rsSeq = rsOptVar + reOptMod + rsOptJoin;
|
|
200
|
-
var rsEmoji = "(?:" + [
|
|
201
|
-
rsDingbat,
|
|
202
|
-
rsRegional,
|
|
203
|
-
rsSurrPair
|
|
204
|
-
].join("|") + ")" + rsSeq;
|
|
205
|
-
/** Used to match apostrophes. */
|
|
206
|
-
var reApos = RegExp(rsApos, "g");
|
|
207
|
-
/**
|
|
208
|
-
* Used to match [combining diacritical marks](https://en.wikipedia.org/wiki/Combining_Diacritical_Marks) and
|
|
209
|
-
* [combining diacritical marks for symbols](https://en.wikipedia.org/wiki/Combining_Diacritical_Marks_for_Symbols).
|
|
210
|
-
*/
|
|
211
|
-
var reComboMark = RegExp(rsCombo, "g");
|
|
212
|
-
/** Used to match complex or compound words. */
|
|
213
|
-
var reUnicodeWord = RegExp([
|
|
214
|
-
rsUpper + "?" + rsLower + "+" + rsOptLowerContr + "(?=" + [
|
|
215
|
-
rsBreak,
|
|
216
|
-
rsUpper,
|
|
217
|
-
"$"
|
|
218
|
-
].join("|") + ")",
|
|
219
|
-
rsUpperMisc + "+" + rsOptUpperContr + "(?=" + [
|
|
220
|
-
rsBreak,
|
|
221
|
-
rsUpper + rsLowerMisc,
|
|
222
|
-
"$"
|
|
223
|
-
].join("|") + ")",
|
|
224
|
-
rsUpper + "?" + rsLowerMisc + "+" + rsOptLowerContr,
|
|
225
|
-
rsUpper + "+" + rsOptUpperContr,
|
|
226
|
-
rsDigits,
|
|
227
|
-
rsEmoji
|
|
228
|
-
].join("|"), "g");
|
|
229
|
-
/** Used to detect strings that need a more robust regexp to match words. */
|
|
230
|
-
var reHasUnicodeWord = /[a-z][A-Z]|[A-Z]{2,}[a-z]|[0-9][a-zA-Z]|[a-zA-Z][0-9]|[^a-zA-Z0-9 ]/;
|
|
231
|
-
/** Used to map Latin Unicode letters to basic Latin letters. */
|
|
232
|
-
var deburredLetters = {
|
|
233
|
-
"À": "A",
|
|
234
|
-
"Á": "A",
|
|
235
|
-
"Â": "A",
|
|
236
|
-
"Ã": "A",
|
|
237
|
-
"Ä": "A",
|
|
238
|
-
"Å": "A",
|
|
239
|
-
"à": "a",
|
|
240
|
-
"á": "a",
|
|
241
|
-
"â": "a",
|
|
242
|
-
"ã": "a",
|
|
243
|
-
"ä": "a",
|
|
244
|
-
"å": "a",
|
|
245
|
-
"Ç": "C",
|
|
246
|
-
"ç": "c",
|
|
247
|
-
"Ð": "D",
|
|
248
|
-
"ð": "d",
|
|
249
|
-
"È": "E",
|
|
250
|
-
"É": "E",
|
|
251
|
-
"Ê": "E",
|
|
252
|
-
"Ë": "E",
|
|
253
|
-
"è": "e",
|
|
254
|
-
"é": "e",
|
|
255
|
-
"ê": "e",
|
|
256
|
-
"ë": "e",
|
|
257
|
-
"Ì": "I",
|
|
258
|
-
"Í": "I",
|
|
259
|
-
"Î": "I",
|
|
260
|
-
"Ï": "I",
|
|
261
|
-
"ì": "i",
|
|
262
|
-
"í": "i",
|
|
263
|
-
"î": "i",
|
|
264
|
-
"ï": "i",
|
|
265
|
-
"Ñ": "N",
|
|
266
|
-
"ñ": "n",
|
|
267
|
-
"Ò": "O",
|
|
268
|
-
"Ó": "O",
|
|
269
|
-
"Ô": "O",
|
|
270
|
-
"Õ": "O",
|
|
271
|
-
"Ö": "O",
|
|
272
|
-
"Ø": "O",
|
|
273
|
-
"ò": "o",
|
|
274
|
-
"ó": "o",
|
|
275
|
-
"ô": "o",
|
|
276
|
-
"õ": "o",
|
|
277
|
-
"ö": "o",
|
|
278
|
-
"ø": "o",
|
|
279
|
-
"Ù": "U",
|
|
280
|
-
"Ú": "U",
|
|
281
|
-
"Û": "U",
|
|
282
|
-
"Ü": "U",
|
|
283
|
-
"ù": "u",
|
|
284
|
-
"ú": "u",
|
|
285
|
-
"û": "u",
|
|
286
|
-
"ü": "u",
|
|
287
|
-
"Ý": "Y",
|
|
288
|
-
"ý": "y",
|
|
289
|
-
"ÿ": "y",
|
|
290
|
-
"Æ": "Ae",
|
|
291
|
-
"æ": "ae",
|
|
292
|
-
"Þ": "Th",
|
|
293
|
-
"þ": "th",
|
|
294
|
-
"ß": "ss",
|
|
295
|
-
"Ā": "A",
|
|
296
|
-
"Ă": "A",
|
|
297
|
-
"Ą": "A",
|
|
298
|
-
"ā": "a",
|
|
299
|
-
"ă": "a",
|
|
300
|
-
"ą": "a",
|
|
301
|
-
"Ć": "C",
|
|
302
|
-
"Ĉ": "C",
|
|
303
|
-
"Ċ": "C",
|
|
304
|
-
"Č": "C",
|
|
305
|
-
"ć": "c",
|
|
306
|
-
"ĉ": "c",
|
|
307
|
-
"ċ": "c",
|
|
308
|
-
"č": "c",
|
|
309
|
-
"Ď": "D",
|
|
310
|
-
"Đ": "D",
|
|
311
|
-
"ď": "d",
|
|
312
|
-
"đ": "d",
|
|
313
|
-
"Ē": "E",
|
|
314
|
-
"Ĕ": "E",
|
|
315
|
-
"Ė": "E",
|
|
316
|
-
"Ę": "E",
|
|
317
|
-
"Ě": "E",
|
|
318
|
-
"ē": "e",
|
|
319
|
-
"ĕ": "e",
|
|
320
|
-
"ė": "e",
|
|
321
|
-
"ę": "e",
|
|
322
|
-
"ě": "e",
|
|
323
|
-
"Ĝ": "G",
|
|
324
|
-
"Ğ": "G",
|
|
325
|
-
"Ġ": "G",
|
|
326
|
-
"Ģ": "G",
|
|
327
|
-
"ĝ": "g",
|
|
328
|
-
"ğ": "g",
|
|
329
|
-
"ġ": "g",
|
|
330
|
-
"ģ": "g",
|
|
331
|
-
"Ĥ": "H",
|
|
332
|
-
"Ħ": "H",
|
|
333
|
-
"ĥ": "h",
|
|
334
|
-
"ħ": "h",
|
|
335
|
-
"Ĩ": "I",
|
|
336
|
-
"Ī": "I",
|
|
337
|
-
"Ĭ": "I",
|
|
338
|
-
"Į": "I",
|
|
339
|
-
"İ": "I",
|
|
340
|
-
"ĩ": "i",
|
|
341
|
-
"ī": "i",
|
|
342
|
-
"ĭ": "i",
|
|
343
|
-
"į": "i",
|
|
344
|
-
"ı": "i",
|
|
345
|
-
"Ĵ": "J",
|
|
346
|
-
"ĵ": "j",
|
|
347
|
-
"Ķ": "K",
|
|
348
|
-
"ķ": "k",
|
|
349
|
-
"ĸ": "k",
|
|
350
|
-
"Ĺ": "L",
|
|
351
|
-
"Ļ": "L",
|
|
352
|
-
"Ľ": "L",
|
|
353
|
-
"Ŀ": "L",
|
|
354
|
-
"Ł": "L",
|
|
355
|
-
"ĺ": "l",
|
|
356
|
-
"ļ": "l",
|
|
357
|
-
"ľ": "l",
|
|
358
|
-
"ŀ": "l",
|
|
359
|
-
"ł": "l",
|
|
360
|
-
"Ń": "N",
|
|
361
|
-
"Ņ": "N",
|
|
362
|
-
"Ň": "N",
|
|
363
|
-
"Ŋ": "N",
|
|
364
|
-
"ń": "n",
|
|
365
|
-
"ņ": "n",
|
|
366
|
-
"ň": "n",
|
|
367
|
-
"ŋ": "n",
|
|
368
|
-
"Ō": "O",
|
|
369
|
-
"Ŏ": "O",
|
|
370
|
-
"Ő": "O",
|
|
371
|
-
"ō": "o",
|
|
372
|
-
"ŏ": "o",
|
|
373
|
-
"ő": "o",
|
|
374
|
-
"Ŕ": "R",
|
|
375
|
-
"Ŗ": "R",
|
|
376
|
-
"Ř": "R",
|
|
377
|
-
"ŕ": "r",
|
|
378
|
-
"ŗ": "r",
|
|
379
|
-
"ř": "r",
|
|
380
|
-
"Ś": "S",
|
|
381
|
-
"Ŝ": "S",
|
|
382
|
-
"Ş": "S",
|
|
383
|
-
"Š": "S",
|
|
384
|
-
"ś": "s",
|
|
385
|
-
"ŝ": "s",
|
|
386
|
-
"ş": "s",
|
|
387
|
-
"š": "s",
|
|
388
|
-
"Ţ": "T",
|
|
389
|
-
"Ť": "T",
|
|
390
|
-
"Ŧ": "T",
|
|
391
|
-
"ţ": "t",
|
|
392
|
-
"ť": "t",
|
|
393
|
-
"ŧ": "t",
|
|
394
|
-
"Ũ": "U",
|
|
395
|
-
"Ū": "U",
|
|
396
|
-
"Ŭ": "U",
|
|
397
|
-
"Ů": "U",
|
|
398
|
-
"Ű": "U",
|
|
399
|
-
"Ų": "U",
|
|
400
|
-
"ũ": "u",
|
|
401
|
-
"ū": "u",
|
|
402
|
-
"ŭ": "u",
|
|
403
|
-
"ů": "u",
|
|
404
|
-
"ű": "u",
|
|
405
|
-
"ų": "u",
|
|
406
|
-
"Ŵ": "W",
|
|
407
|
-
"ŵ": "w",
|
|
408
|
-
"Ŷ": "Y",
|
|
409
|
-
"ŷ": "y",
|
|
410
|
-
"Ÿ": "Y",
|
|
411
|
-
"Ź": "Z",
|
|
412
|
-
"Ż": "Z",
|
|
413
|
-
"Ž": "Z",
|
|
414
|
-
"ź": "z",
|
|
415
|
-
"ż": "z",
|
|
416
|
-
"ž": "z",
|
|
417
|
-
"IJ": "IJ",
|
|
418
|
-
"ij": "ij",
|
|
419
|
-
"Œ": "Oe",
|
|
420
|
-
"œ": "oe",
|
|
421
|
-
"ʼn": "'n",
|
|
422
|
-
"ſ": "ss"
|
|
423
|
-
};
|
|
424
|
-
/** Detect free variable `global` from Node.js. */
|
|
425
|
-
var freeGlobal = typeof global == "object" && global && global.Object === Object && global;
|
|
426
|
-
/** Detect free variable `self`. */
|
|
427
|
-
var freeSelf = typeof self == "object" && self && self.Object === Object && self;
|
|
428
|
-
/** Used as a reference to the global object. */
|
|
429
|
-
var root = freeGlobal || freeSelf || Function("return this")();
|
|
430
|
-
/**
|
|
431
|
-
* A specialized version of `_.reduce` for arrays without support for
|
|
432
|
-
* iteratee shorthands.
|
|
433
|
-
*
|
|
434
|
-
* @private
|
|
435
|
-
* @param {Array} [array] The array to iterate over.
|
|
436
|
-
* @param {Function} iteratee The function invoked per iteration.
|
|
437
|
-
* @param {*} [accumulator] The initial value.
|
|
438
|
-
* @param {boolean} [initAccum] Specify using the first element of `array` as
|
|
439
|
-
* the initial value.
|
|
440
|
-
* @returns {*} Returns the accumulated value.
|
|
441
|
-
*/
|
|
442
|
-
function arrayReduce(array, iteratee, accumulator, initAccum) {
|
|
443
|
-
var index = -1, length = array ? array.length : 0;
|
|
444
|
-
if (initAccum && length) accumulator = array[++index];
|
|
445
|
-
while (++index < length) accumulator = iteratee(accumulator, array[index], index, array);
|
|
446
|
-
return accumulator;
|
|
447
|
-
}
|
|
448
|
-
/**
|
|
449
|
-
* Splits an ASCII `string` into an array of its words.
|
|
450
|
-
*
|
|
451
|
-
* @private
|
|
452
|
-
* @param {string} The string to inspect.
|
|
453
|
-
* @returns {Array} Returns the words of `string`.
|
|
454
|
-
*/
|
|
455
|
-
function asciiWords(string) {
|
|
456
|
-
return string.match(reAsciiWord) || [];
|
|
457
|
-
}
|
|
458
|
-
/**
|
|
459
|
-
* The base implementation of `_.propertyOf` without support for deep paths.
|
|
460
|
-
*
|
|
461
|
-
* @private
|
|
462
|
-
* @param {Object} object The object to query.
|
|
463
|
-
* @returns {Function} Returns the new accessor function.
|
|
464
|
-
*/
|
|
465
|
-
function basePropertyOf(object) {
|
|
466
|
-
return function(key) {
|
|
467
|
-
return object == null ? void 0 : object[key];
|
|
468
|
-
};
|
|
469
|
-
}
|
|
470
|
-
/**
|
|
471
|
-
* Used by `_.deburr` to convert Latin-1 Supplement and Latin Extended-A
|
|
472
|
-
* letters to basic Latin letters.
|
|
473
|
-
*
|
|
474
|
-
* @private
|
|
475
|
-
* @param {string} letter The matched letter to deburr.
|
|
476
|
-
* @returns {string} Returns the deburred letter.
|
|
477
|
-
*/
|
|
478
|
-
var deburrLetter = basePropertyOf(deburredLetters);
|
|
479
|
-
/**
|
|
480
|
-
* Checks if `string` contains a word composed of Unicode symbols.
|
|
481
|
-
*
|
|
482
|
-
* @private
|
|
483
|
-
* @param {string} string The string to inspect.
|
|
484
|
-
* @returns {boolean} Returns `true` if a word is found, else `false`.
|
|
485
|
-
*/
|
|
486
|
-
function hasUnicodeWord(string) {
|
|
487
|
-
return reHasUnicodeWord.test(string);
|
|
488
|
-
}
|
|
489
|
-
/**
|
|
490
|
-
* Splits a Unicode `string` into an array of its words.
|
|
491
|
-
*
|
|
492
|
-
* @private
|
|
493
|
-
* @param {string} The string to inspect.
|
|
494
|
-
* @returns {Array} Returns the words of `string`.
|
|
495
|
-
*/
|
|
496
|
-
function unicodeWords(string) {
|
|
497
|
-
return string.match(reUnicodeWord) || [];
|
|
498
|
-
}
|
|
499
|
-
/**
|
|
500
|
-
* Used to resolve the
|
|
501
|
-
* [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring)
|
|
502
|
-
* of values.
|
|
503
|
-
*/
|
|
504
|
-
var objectToString = Object.prototype.toString;
|
|
505
|
-
/** Built-in value references. */
|
|
506
|
-
var Symbol = root.Symbol;
|
|
507
|
-
/** Used to convert symbols to primitives and strings. */
|
|
508
|
-
var symbolProto = Symbol ? Symbol.prototype : void 0;
|
|
509
|
-
var symbolToString = symbolProto ? symbolProto.toString : void 0;
|
|
510
|
-
/**
|
|
511
|
-
* The base implementation of `_.toString` which doesn't convert nullish
|
|
512
|
-
* values to empty strings.
|
|
513
|
-
*
|
|
514
|
-
* @private
|
|
515
|
-
* @param {*} value The value to process.
|
|
516
|
-
* @returns {string} Returns the string.
|
|
517
|
-
*/
|
|
518
|
-
function baseToString(value) {
|
|
519
|
-
if (typeof value == "string") return value;
|
|
520
|
-
if (isSymbol(value)) return symbolToString ? symbolToString.call(value) : "";
|
|
521
|
-
var result = value + "";
|
|
522
|
-
return result == "0" && 1 / value == -Infinity ? "-0" : result;
|
|
523
|
-
}
|
|
524
|
-
/**
|
|
525
|
-
* Creates a function like `_.camelCase`.
|
|
526
|
-
*
|
|
527
|
-
* @private
|
|
528
|
-
* @param {Function} callback The function to combine each word.
|
|
529
|
-
* @returns {Function} Returns the new compounder function.
|
|
530
|
-
*/
|
|
531
|
-
function createCompounder(callback) {
|
|
532
|
-
return function(string) {
|
|
533
|
-
return arrayReduce(words(deburr(string).replace(reApos, "")), callback, "");
|
|
534
|
-
};
|
|
535
|
-
}
|
|
536
|
-
/**
|
|
537
|
-
* Checks if `value` is object-like. A value is object-like if it's not `null`
|
|
538
|
-
* and has a `typeof` result of "object".
|
|
539
|
-
*
|
|
540
|
-
* @static
|
|
541
|
-
* @memberOf _
|
|
542
|
-
* @since 4.0.0
|
|
543
|
-
* @category Lang
|
|
544
|
-
* @param {*} value The value to check.
|
|
545
|
-
* @returns {boolean} Returns `true` if `value` is object-like, else `false`.
|
|
546
|
-
* @example
|
|
547
|
-
*
|
|
548
|
-
* _.isObjectLike({});
|
|
549
|
-
* // => true
|
|
550
|
-
*
|
|
551
|
-
* _.isObjectLike([1, 2, 3]);
|
|
552
|
-
* // => true
|
|
553
|
-
*
|
|
554
|
-
* _.isObjectLike(_.noop);
|
|
555
|
-
* // => false
|
|
556
|
-
*
|
|
557
|
-
* _.isObjectLike(null);
|
|
558
|
-
* // => false
|
|
559
|
-
*/
|
|
560
|
-
function isObjectLike(value) {
|
|
561
|
-
return !!value && typeof value == "object";
|
|
562
|
-
}
|
|
563
|
-
/**
|
|
564
|
-
* Checks if `value` is classified as a `Symbol` primitive or object.
|
|
565
|
-
*
|
|
566
|
-
* @static
|
|
567
|
-
* @memberOf _
|
|
568
|
-
* @since 4.0.0
|
|
569
|
-
* @category Lang
|
|
570
|
-
* @param {*} value The value to check.
|
|
571
|
-
* @returns {boolean} Returns `true` if `value` is a symbol, else `false`.
|
|
572
|
-
* @example
|
|
573
|
-
*
|
|
574
|
-
* _.isSymbol(Symbol.iterator);
|
|
575
|
-
* // => true
|
|
576
|
-
*
|
|
577
|
-
* _.isSymbol('abc');
|
|
578
|
-
* // => false
|
|
579
|
-
*/
|
|
580
|
-
function isSymbol(value) {
|
|
581
|
-
return typeof value == "symbol" || isObjectLike(value) && objectToString.call(value) == symbolTag;
|
|
582
|
-
}
|
|
583
|
-
/**
|
|
584
|
-
* Converts `value` to a string. An empty string is returned for `null`
|
|
585
|
-
* and `undefined` values. The sign of `-0` is preserved.
|
|
586
|
-
*
|
|
587
|
-
* @static
|
|
588
|
-
* @memberOf _
|
|
589
|
-
* @since 4.0.0
|
|
590
|
-
* @category Lang
|
|
591
|
-
* @param {*} value The value to process.
|
|
592
|
-
* @returns {string} Returns the string.
|
|
593
|
-
* @example
|
|
594
|
-
*
|
|
595
|
-
* _.toString(null);
|
|
596
|
-
* // => ''
|
|
597
|
-
*
|
|
598
|
-
* _.toString(-0);
|
|
599
|
-
* // => '-0'
|
|
600
|
-
*
|
|
601
|
-
* _.toString([1, 2, 3]);
|
|
602
|
-
* // => '1,2,3'
|
|
603
|
-
*/
|
|
604
|
-
function toString(value) {
|
|
605
|
-
return value == null ? "" : baseToString(value);
|
|
606
|
-
}
|
|
607
|
-
/**
|
|
608
|
-
* Deburrs `string` by converting
|
|
609
|
-
* [Latin-1 Supplement](https://en.wikipedia.org/wiki/Latin-1_Supplement_(Unicode_block)#Character_table)
|
|
610
|
-
* and [Latin Extended-A](https://en.wikipedia.org/wiki/Latin_Extended-A)
|
|
611
|
-
* letters to basic Latin letters and removing
|
|
612
|
-
* [combining diacritical marks](https://en.wikipedia.org/wiki/Combining_Diacritical_Marks).
|
|
613
|
-
*
|
|
614
|
-
* @static
|
|
615
|
-
* @memberOf _
|
|
616
|
-
* @since 3.0.0
|
|
617
|
-
* @category String
|
|
618
|
-
* @param {string} [string=''] The string to deburr.
|
|
619
|
-
* @returns {string} Returns the deburred string.
|
|
620
|
-
* @example
|
|
621
|
-
*
|
|
622
|
-
* _.deburr('déjà vu');
|
|
623
|
-
* // => 'deja vu'
|
|
624
|
-
*/
|
|
625
|
-
function deburr(string) {
|
|
626
|
-
string = toString(string);
|
|
627
|
-
return string && string.replace(reLatin, deburrLetter).replace(reComboMark, "");
|
|
628
|
-
}
|
|
629
|
-
/**
|
|
630
|
-
* Converts `string` to
|
|
631
|
-
* [snake case](https://en.wikipedia.org/wiki/Snake_case).
|
|
632
|
-
*
|
|
633
|
-
* @static
|
|
634
|
-
* @memberOf _
|
|
635
|
-
* @since 3.0.0
|
|
636
|
-
* @category String
|
|
637
|
-
* @param {string} [string=''] The string to convert.
|
|
638
|
-
* @returns {string} Returns the snake cased string.
|
|
639
|
-
* @example
|
|
640
|
-
*
|
|
641
|
-
* _.snakeCase('Foo Bar');
|
|
642
|
-
* // => 'foo_bar'
|
|
643
|
-
*
|
|
644
|
-
* _.snakeCase('fooBar');
|
|
645
|
-
* // => 'foo_bar'
|
|
646
|
-
*
|
|
647
|
-
* _.snakeCase('--FOO-BAR--');
|
|
648
|
-
* // => 'foo_bar'
|
|
649
|
-
*/
|
|
650
|
-
var snakeCase = createCompounder(function(result, word, index) {
|
|
651
|
-
return result + (index ? "_" : "") + word.toLowerCase();
|
|
652
|
-
});
|
|
653
|
-
/**
|
|
654
|
-
* Splits `string` into an array of its words.
|
|
655
|
-
*
|
|
656
|
-
* @static
|
|
657
|
-
* @memberOf _
|
|
658
|
-
* @since 3.0.0
|
|
659
|
-
* @category String
|
|
660
|
-
* @param {string} [string=''] The string to inspect.
|
|
661
|
-
* @param {RegExp|string} [pattern] The pattern to match words.
|
|
662
|
-
* @param- {Object} [guard] Enables use as an iteratee for methods like `_.map`.
|
|
663
|
-
* @returns {Array} Returns the words of `string`.
|
|
664
|
-
* @example
|
|
665
|
-
*
|
|
666
|
-
* _.words('fred, barney, & pebbles');
|
|
667
|
-
* // => ['fred', 'barney', 'pebbles']
|
|
668
|
-
*
|
|
669
|
-
* _.words('fred, barney, & pebbles', /[^, ]+/g);
|
|
670
|
-
* // => ['fred', 'barney', '&', 'pebbles']
|
|
671
|
-
*/
|
|
672
|
-
function words(string, pattern, guard) {
|
|
673
|
-
string = toString(string);
|
|
674
|
-
pattern = guard ? void 0 : pattern;
|
|
675
|
-
if (pattern === void 0) return hasUnicodeWord(string) ? unicodeWords(string) : asciiWords(string);
|
|
676
|
-
return string.match(pattern) || [];
|
|
677
|
-
}
|
|
678
|
-
module.exports = snakeCase;
|
|
679
|
-
})))(), 1);
|
|
680
|
-
/**
|
|
681
|
-
* `UPPER_SNAKE_CASE`, with numbers kept against the word they follow.
|
|
682
|
-
*
|
|
683
|
-
* Matches `environmentCase` in `@geekmidas/envkit`, which reads the values these
|
|
684
|
-
* names key. The two must agree exactly, so this is the implementation and that
|
|
685
|
-
* one should defer to it.
|
|
686
|
-
*
|
|
687
|
-
* @example environmentCase('sendEmail') // 'SEND_EMAIL'
|
|
688
|
-
* @example environmentCase('api2') // 'API2' (digit joins its word)
|
|
689
|
-
*/
|
|
690
|
-
function environmentCase(name) {
|
|
691
|
-
return (0, import_lodash_snakecase.default)(name).toUpperCase().replace(/_\d+/g, (r) => r.replace("_", ""));
|
|
692
|
-
}
|
|
693
|
-
/**
|
|
694
|
-
* The env key a construct provides for one of its roles.
|
|
695
|
-
*
|
|
696
|
-
* @example provideKey('Uploads', 'url') // 'UPLOADS_URL'
|
|
697
|
-
* @example provideKey('Uploads', 'cdnUrl') // 'UPLOADS_CDN_URL'
|
|
698
|
-
*/
|
|
699
|
-
function provideKey(id, role) {
|
|
700
|
-
return environmentCase(`${id}_${role}`);
|
|
701
|
-
}
|
|
702
|
-
/**
|
|
703
|
-
* A construct's canonical id — PascalCase.
|
|
704
|
-
*
|
|
705
|
-
* `uploads`, `Uploads`, `user_uploads`, and `user-uploads` all canonicalise to
|
|
706
|
-
* the same id, so declaring two of them is a duplicate rather than a collision
|
|
707
|
-
* to detect.
|
|
708
|
-
*
|
|
709
|
-
* Runtime only. Writing the id in PascalCase is what keeps the *type* usable:
|
|
710
|
-
* the service key is `Uncapitalize<TName>`, a TypeScript intrinsic, so no
|
|
711
|
-
* type-level transform is needed and none has to be kept in step with this one.
|
|
712
|
-
*
|
|
713
|
-
* @example canonicalId('user-uploads') // 'UserUploads'
|
|
714
|
-
*/
|
|
715
|
-
function canonicalId(input) {
|
|
716
|
-
const id = (0, import_lodash_snakecase.default)(input).split("_").filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join("");
|
|
717
|
-
if (!VALID_ID.test(id)) throw new InvalidConstructId(input, id);
|
|
718
|
-
return id;
|
|
719
|
-
}
|
|
720
|
-
/**
|
|
721
|
-
* A canonical id: PascalCase, letters and digits only.
|
|
722
|
-
*
|
|
723
|
-
* Narrower than a JavaScript identifier — `_id` and `$ref` are legal JavaScript
|
|
724
|
-
* and rejected here — because the id also has to survive `environmentCase` into
|
|
725
|
-
* an env key and `cloudName` into a DNS-safe resource name.
|
|
726
|
-
*/
|
|
727
|
-
const VALID_ID = /^[A-Z][A-Za-z0-9]*$/;
|
|
728
|
-
/**
|
|
729
|
-
* The key a construct is reached under in the service record.
|
|
730
|
-
*
|
|
731
|
-
* The runtime twin of `Uncapitalize<TName>`, which types it — they must agree,
|
|
732
|
-
* so they live next to each other rather than being re-derived by each
|
|
733
|
-
* construct.
|
|
734
|
-
*
|
|
735
|
-
* @example serviceKey('UserUploads') // 'userUploads' → services.userUploads
|
|
736
|
-
*/
|
|
737
|
-
function serviceKey(id) {
|
|
738
|
-
return id.charAt(0).toLowerCase() + id.slice(1);
|
|
739
|
-
}
|
|
740
|
-
/**
|
|
741
|
-
* The table a cache keeps its entries in, when nobody named one.
|
|
742
|
-
*
|
|
743
|
-
* Derived from the cache's own id rather than fixed at `cache`, because a
|
|
744
|
-
* database may hold more than one and two caches sharing a table share a
|
|
745
|
-
* keyspace — `orders.cache('Sessions')` and `orders.cache('Rates')` would
|
|
746
|
-
* silently read each other's entries and evict each other's keys.
|
|
747
|
-
*
|
|
748
|
-
* Prefixed rather than suffixed so every cache sorts together in `\dt`, and
|
|
749
|
-
* prefixed at all so a cache named for a thing the application also stores —
|
|
750
|
-
* `orders.cache('Users')` — cannot collide with the table holding that thing.
|
|
751
|
-
*
|
|
752
|
-
* Read by whoever composes the URL and by whoever creates the table, so both
|
|
753
|
-
* default the same way.
|
|
754
|
-
*
|
|
755
|
-
* @example cacheTable('Sessions') // 'cache_sessions'
|
|
756
|
-
*/
|
|
757
|
-
function cacheTable(id) {
|
|
758
|
-
return `cache_${id.replace(/([a-z0-9])([A-Z])/g, "$1_$2").toLowerCase()}`;
|
|
759
|
-
}
|
|
760
|
-
/**
|
|
761
|
-
* Kebab-cases an identifier, acronym- and digit-aware.
|
|
762
|
-
*
|
|
763
|
-
* `userName` → `user-name`, `APIKey` → `api-key`, `S3Bucket` → `s3-bucket`.
|
|
764
|
-
*
|
|
765
|
-
* The last of those is why this is here rather than `snakecase(id)` with the
|
|
766
|
-
* underscores swapped: lodash splits a digit from the letter beside it, so
|
|
767
|
-
* `S3Bucket` became `s-3-bucket` on one provider and `s3-bucket` on the other.
|
|
768
|
-
* Two implementations of one rule, agreeing on every id anybody had tried.
|
|
769
|
-
*
|
|
770
|
-
* `environmentCase` already corrected for the same thing in the other
|
|
771
|
-
* direction — `api2` keeps its digit — so the two spellings of "kebab this id"
|
|
772
|
-
* in this file did not even agree with each other.
|
|
773
|
-
*/
|
|
774
|
-
function kebabCase(value) {
|
|
775
|
-
return value.replace(/([A-Z]+)([A-Z][a-z])/g, "$1-$2").replace(/([a-z0-9])([A-Z])/g, "$1-$2").replace(/[\s_]+/g, "-").toLowerCase();
|
|
776
|
-
}
|
|
777
|
-
/**
|
|
778
|
-
* The physical name a target provisions a construct under — lowercase kebab,
|
|
779
|
-
* scoped so two stages or apps sharing an account cannot collide.
|
|
780
|
-
*
|
|
781
|
-
* **One rule, every provider.** A construct is named the same thing on AWS and
|
|
782
|
-
* on Dokploy, which is what lets a name be read across them: `Database` in the
|
|
783
|
-
* `production` stage of `kitchen-sink` is `production-kitchen-sink-database`
|
|
784
|
-
* wherever it lands. The SST target's `prefixedName` is this function under
|
|
785
|
-
* another signature and defers to it.
|
|
786
|
-
*
|
|
787
|
-
* Idempotent in its prefix: an id that already carries the scope is not given a
|
|
788
|
-
* second one, so composing names cannot double up.
|
|
789
|
-
*
|
|
790
|
-
* @example cloudName({ stage: 'prod', app: 'myapp' }, 'UserUploads')
|
|
791
|
-
* // 'prod-myapp-user-uploads'
|
|
792
|
-
*/
|
|
793
|
-
function cloudName(scope, id) {
|
|
794
|
-
return scopedName([scope.stage, scope.app], id);
|
|
795
|
-
}
|
|
796
|
-
/**
|
|
797
|
-
* {@link cloudName} for a caller that holds its scope as a list.
|
|
798
|
-
*
|
|
799
|
-
* The SST target's stacks add a segment of their own, so the prefix is not
|
|
800
|
-
* always two parts — which is the only reason this signature exists.
|
|
801
|
-
*/
|
|
802
|
-
function scopedName(scope, id) {
|
|
803
|
-
const prefix = scope.join("-").toLowerCase();
|
|
804
|
-
const name = kebabCase(id);
|
|
805
|
-
return name.startsWith(prefix) ? name : `${prefix}-${name}`;
|
|
806
|
-
}
|
|
807
|
-
/**
|
|
808
|
-
* The domain a cookie must be scoped to so a surface and its callers share it.
|
|
809
|
-
*
|
|
810
|
-
* Derived from the addresses rather than configured, for the same reason the
|
|
811
|
-
* origins are: the set of things that talk to a surface is already in the graph,
|
|
812
|
-
* and the domain they have in common is a fact about that set. Returned with the
|
|
813
|
-
* leading dot a `Domain` attribute wants.
|
|
814
|
-
*
|
|
815
|
-
* Returns `undefined` when there is nothing to scope, which is the common case
|
|
816
|
-
* and not a failure:
|
|
817
|
-
*
|
|
818
|
-
* - **One host.** Locally everything is `localhost` on different ports, and
|
|
819
|
-
* cookies ignore the port — so a `Domain` would add nothing and `.localhost`
|
|
820
|
-
* is not a domain a browser will accept.
|
|
821
|
-
* - **Nothing in common.** Unrelated hosts cannot share a cookie at all, and
|
|
822
|
-
* emitting the longest common suffix anyway would be a value that silently
|
|
823
|
-
* fails to set.
|
|
824
|
-
*
|
|
825
|
-
* **The public-suffix limit, stated rather than discovered.** Two apps on
|
|
826
|
-
* `a.vercel.app` and `b.vercel.app` share `.vercel.app`, which every browser
|
|
827
|
-
* rejects because it is a registrable suffix rather than a registrable domain.
|
|
828
|
-
* Resolving that correctly needs the Public Suffix List, which is a downloaded,
|
|
829
|
-
* expiring dataset — so this requires at least two labels and otherwise trusts
|
|
830
|
-
* the addresses, and the value stays overridable for the case it gets wrong.
|
|
831
|
-
*/
|
|
832
|
-
function cookieDomain(urls) {
|
|
833
|
-
const hosts = /* @__PURE__ */ new Set();
|
|
834
|
-
for (const url of urls) try {
|
|
835
|
-
const { hostname } = new URL(url);
|
|
836
|
-
if (/^\d+(\.\d+){3}$/.test(hostname) || hostname.includes(":")) return;
|
|
837
|
-
hosts.add(hostname.toLowerCase());
|
|
838
|
-
} catch {
|
|
839
|
-
return;
|
|
840
|
-
}
|
|
841
|
-
if (hosts.size === 0) return;
|
|
842
|
-
if (hosts.size === 1) return;
|
|
843
|
-
const [first = [], ...rest] = [...hosts].map((host) => host.split(".").reverse());
|
|
844
|
-
const shared = [];
|
|
845
|
-
for (const [index, label] of first.entries()) {
|
|
846
|
-
if (!rest.every((labels) => labels[index] === label)) break;
|
|
847
|
-
shared.push(label);
|
|
848
|
-
}
|
|
849
|
-
if (shared.length < 2) return;
|
|
850
|
-
return `.${shared.reverse().join(".")}`;
|
|
851
|
-
}
|
|
852
|
-
/**
|
|
853
|
-
* The env key a construct's provided role actually becomes.
|
|
854
|
-
*
|
|
855
|
-
* Almost always `provideKey(id, role)` — and `secret` is the exception, because
|
|
856
|
-
* a secret's *name* is its key: `Auth` signs with `AUTH_SECRET`, which is also
|
|
857
|
-
* what better-auth's own tooling looks for, and qualifying it by role would
|
|
858
|
-
* produce `AUTH_SECRET_VALUE`.
|
|
859
|
-
*
|
|
860
|
-
* It lives here rather than in each target because two targets deriving the
|
|
861
|
-
* same key separately is exactly the drift the app/infra contract check exists
|
|
862
|
-
* to catch — and a check deriving the key differently from the thing it checks
|
|
863
|
-
* cannot catch anything.
|
|
864
|
-
*/
|
|
865
|
-
function providedKeyFor(id, kind, role) {
|
|
866
|
-
if (kind === "secret") return environmentCase(id);
|
|
867
|
-
if (kind === "telemetry") return "OTEL_EXPORTER_OTLP_ENDPOINT";
|
|
868
|
-
return provideKey(id, role);
|
|
869
|
-
}
|
|
870
|
-
//#endregion
|
|
871
|
-
//#region src/derive.ts
|
|
872
|
-
/** Whether a declaration names a parent. */
|
|
873
|
-
function isDerived(declaration) {
|
|
874
|
-
return declaration.kind in DERIVES_FROM && "of" in declaration && typeof declaration.of === "string";
|
|
875
|
-
}
|
|
876
|
-
/**
|
|
877
|
-
* Check every derived construct against its parent.
|
|
878
|
-
*
|
|
879
|
-
* Two rules: the parent exists, and its kind may vend this one. Together they
|
|
880
|
-
* make cycles unreachable — a reader is terminal, so no chain of `of` can
|
|
881
|
-
* return to where it started, and no walk is needed to prove it.
|
|
882
|
-
*/
|
|
883
|
-
function assertDerivations(manifest) {
|
|
884
|
-
for (const [id, declaration] of Object.entries(manifest)) {
|
|
885
|
-
if (!isDerived(declaration)) continue;
|
|
886
|
-
const parent = manifest[declaration.of];
|
|
887
|
-
if (!parent) throw new UnknownParent(id, declaration.of, Object.keys(manifest));
|
|
888
|
-
const allowed = DERIVES_FROM[declaration.kind];
|
|
889
|
-
if (!allowed.includes(parent.kind)) throw new IllegalDerivation(id, declaration.kind, parent.kind, allowed);
|
|
890
|
-
}
|
|
891
|
-
}
|
|
892
|
-
/**
|
|
893
|
-
* The order constructs must be provisioned in: every parent before its children.
|
|
894
|
-
*
|
|
895
|
-
* Resources are leaves and so come first in any order; only derived nodes
|
|
896
|
-
* constrain the sequence, and they form a shallow forest rather than a general
|
|
897
|
-
* graph. This walks each node's ancestors on demand instead of running a full
|
|
898
|
-
* topological sort, which is the same result at this depth and reads as what it
|
|
899
|
-
* is.
|
|
900
|
-
*
|
|
901
|
-
* Assumes {@link assertDerivations} has passed — a missing parent would
|
|
902
|
-
* otherwise be a silent omission here rather than an error.
|
|
903
|
-
*/
|
|
904
|
-
function provisionOrder(manifest) {
|
|
905
|
-
const ordered = [];
|
|
906
|
-
const placed = /* @__PURE__ */ new Set();
|
|
907
|
-
const place = (id) => {
|
|
908
|
-
if (placed.has(id)) return;
|
|
909
|
-
const declaration = manifest[id];
|
|
910
|
-
if (!declaration) return;
|
|
911
|
-
placed.add(id);
|
|
912
|
-
if (isDerived(declaration)) place(declaration.of);
|
|
913
|
-
ordered.push(id);
|
|
914
|
-
};
|
|
915
|
-
for (const id of Object.keys(manifest)) place(id);
|
|
916
|
-
return ordered;
|
|
917
|
-
}
|
|
918
|
-
/**
|
|
919
|
-
* Every edge a declaration carries, wherever the kind happens to keep them.
|
|
920
|
-
*
|
|
921
|
-
* Dependencies live in two places by design: on a node when the whole construct
|
|
922
|
-
* is the consumer (a site), and on each nested handler when the construct is a
|
|
923
|
-
* surface (a `rest-api`, whose routes each depend on their own things and
|
|
924
|
-
* nothing more). Flattening that difference here is what lets every consumer of
|
|
925
|
-
* the graph — reverse lookups, filtering, reference checks — ask one question.
|
|
926
|
-
*/
|
|
927
|
-
function dependenciesOf(declaration) {
|
|
928
|
-
const own = "dependencies" in declaration ? declaration.dependencies ?? [] : [];
|
|
929
|
-
const calls = "calls" in declaration ? declaration.calls ?? [] : [];
|
|
930
|
-
const nested = declaration.kind === "rest-api" ? declaration.endpoints.flatMap((endpoint) => endpoint.dependencies) : [];
|
|
931
|
-
const telemetry = "telemetry" in declaration && typeof declaration.telemetry === "string" ? [{
|
|
932
|
-
target: declaration.telemetry,
|
|
933
|
-
kind: "telemetry"
|
|
934
|
-
}] : [];
|
|
935
|
-
return [
|
|
936
|
-
...own,
|
|
937
|
-
...calls,
|
|
938
|
-
...nested,
|
|
939
|
-
...telemetry
|
|
940
|
-
];
|
|
941
|
-
}
|
|
942
|
-
/**
|
|
943
|
-
* The ids that depend on one construct — the graph read backwards.
|
|
944
|
-
*
|
|
945
|
-
* This is the whole mechanism behind CORS origins and trusted origins. Both are
|
|
946
|
-
* lists of *callers*, and a caller is exactly an inbound edge, so neither is
|
|
947
|
-
* ever written down: a surface that listed its own callers would have to be
|
|
948
|
-
* edited every time something new called it, which is the hand-maintained list
|
|
949
|
-
* this replaces.
|
|
950
|
-
*
|
|
951
|
-
* Sorted, because it feeds a comma-separated env value that would otherwise
|
|
952
|
-
* change whenever the manifest's key order did — and a value that churns is a
|
|
953
|
-
* container that redeploys for no reason.
|
|
954
|
-
*/
|
|
955
|
-
function dependentsOf(manifest, id) {
|
|
956
|
-
const callers = [];
|
|
957
|
-
for (const [callerId, declaration] of Object.entries(manifest)) {
|
|
958
|
-
if (callerId === id) continue;
|
|
959
|
-
if (dependenciesOf(declaration).some((edge) => edge.target === id)) callers.push(callerId);
|
|
960
|
-
}
|
|
961
|
-
return callers.sort();
|
|
962
|
-
}
|
|
963
|
-
/**
|
|
964
|
-
* How each site variant names a value it ships to the browser.
|
|
965
|
-
*
|
|
966
|
-
* The prefix *is* the framework's contract — `VITE_`, `NEXT_PUBLIC_` and
|
|
967
|
-
* `EXPO_PUBLIC_` all mean "inline this into the bundle" — so it is the one thing
|
|
968
|
-
* a variant changes, and it changes nothing else.
|
|
969
|
-
*/
|
|
970
|
-
const PUBLIC_PREFIX = {
|
|
971
|
-
static: "VITE_",
|
|
972
|
-
tanstack: "VITE_",
|
|
973
|
-
next: "NEXT_PUBLIC_",
|
|
974
|
-
expo: "EXPO_PUBLIC_"
|
|
975
|
-
};
|
|
976
|
-
/**
|
|
977
|
-
* The keys a site's bundle needs, mapped to the key each value comes from —
|
|
978
|
-
* `{ VITE_API_URL: 'API_URL' }`.
|
|
979
|
-
*
|
|
980
|
-
* A rename, not a second derivation: `API_URL` is resolved once, by whatever
|
|
981
|
-
* resolved it for the server, and the site reads the same value under the name
|
|
982
|
-
* its bundler will inline. That is what keeps a site and its API from coming to
|
|
983
|
-
* disagree about where the API is.
|
|
984
|
-
*
|
|
985
|
-
* Filtered by `PUBLIC` rather than by what the site asked for. A site may
|
|
986
|
-
* legitimately depend on anything — its server half, where it has one, reads env
|
|
987
|
-
* exactly as a function does — so this is not a restriction on edges. It decides
|
|
988
|
-
* one thing: which values may be prefixed into a bundle, which is what keeps
|
|
989
|
-
* `ORDERS_URL` and its password out of a JavaScript file served to the public.
|
|
990
|
-
*
|
|
991
|
-
* Shared by every target for the same reason `providedKeyFor` is: a site built
|
|
992
|
-
* locally and the same site built by a deploy must inline the same names.
|
|
993
|
-
*/
|
|
994
|
-
function publicEnvFor(declaration, manifest) {
|
|
995
|
-
const prefix = PUBLIC_PREFIX[declaration.variant];
|
|
996
|
-
const keys = {};
|
|
997
|
-
for (const edge of declaration.dependencies) {
|
|
998
|
-
const target = manifest[edge.target];
|
|
999
|
-
if (!target) continue;
|
|
1000
|
-
for (const role of PUBLIC[target.kind] ?? []) {
|
|
1001
|
-
const key = provideKey(edge.target, role);
|
|
1002
|
-
keys[`${prefix}${key}`] = key;
|
|
1003
|
-
}
|
|
1004
|
-
}
|
|
1005
|
-
return keys;
|
|
1006
|
-
}
|
|
1007
|
-
//#endregion
|
|
1008
|
-
//#region src/encryption.ts
|
|
1009
|
-
/**
|
|
1010
|
-
* The `kms://` URL an `Encryption` construct reads on AWS.
|
|
1011
|
-
*
|
|
1012
|
-
* Here because two packages meet on it: `@geekmidas/cloud` composes it from
|
|
1013
|
-
* the keys it provisioned, and `@geekmidas/constructs` parses it to reach
|
|
1014
|
-
* them. One function, so the two cannot disagree about a parameter's name.
|
|
1015
|
-
*/
|
|
1016
|
-
const KMS_SCHEME = "kms:";
|
|
1017
|
-
/**
|
|
1018
|
-
* `kms://<region>?key=<key arn>&index=<hmac key arn>[&endpoint=<url>]`
|
|
1019
|
-
*
|
|
1020
|
-
* `endpoint` reaches an emulator instead of AWS — floci, locally and in tests —
|
|
1021
|
-
* the same parameter an `sns://` connection string carries for the same reason.
|
|
1022
|
-
*/
|
|
1023
|
-
function kmsUrl(input) {
|
|
1024
|
-
const query = new URLSearchParams({
|
|
1025
|
-
key: input.key,
|
|
1026
|
-
index: input.index,
|
|
1027
|
-
...input.endpoint ? { endpoint: input.endpoint } : {}
|
|
1028
|
-
});
|
|
1029
|
-
return `${KMS_SCHEME}//${input.region}?${query}`;
|
|
1030
|
-
}
|
|
1031
|
-
//#endregion
|
|
1032
|
-
//#region src/external.ts
|
|
1033
|
-
/** The key in a by-stage `url` that answers for every stage not listed. */
|
|
1034
|
-
const DEFAULT_STAGE_URL = "default";
|
|
1035
|
-
/**
|
|
1036
|
-
* Where an external API answers on a deployed stage.
|
|
1037
|
-
*
|
|
1038
|
-
* One URL answers every stage; a record answers by stage name, falling back
|
|
1039
|
-
* to `default`. Read at build and deploy, so a stage nobody gave a URL fails
|
|
1040
|
-
* there — with the stage named — rather than at the first request.
|
|
1041
|
-
*
|
|
1042
|
-
* @throws {NoUrlForStage} when a record names neither the stage nor a default.
|
|
1043
|
-
*/
|
|
1044
|
-
function externalApiUrl(declaration, stage) {
|
|
1045
|
-
const { url } = declaration;
|
|
1046
|
-
if (typeof url === "string") return url;
|
|
1047
|
-
const resolved = url[stage] ?? url["default"];
|
|
1048
|
-
if (!resolved) throw new NoUrlForStage(declaration.id, stage, Object.keys(url));
|
|
1049
|
-
return resolved;
|
|
1050
|
-
}
|
|
1051
|
-
/** An external API's `url` record has nothing for the stage being deployed. */
|
|
1052
|
-
var NoUrlForStage = class extends Error {
|
|
1053
|
-
id;
|
|
1054
|
-
stage;
|
|
1055
|
-
stages;
|
|
1056
|
-
constructor(id, stage, stages) {
|
|
1057
|
-
super(`'${id}' has no URL for the stage '${stage}': its url names ${stages.length ? stages.map((s) => `'${s}'`).join(", ") : "no stages"}. Add '${stage}', or a '${DEFAULT_STAGE_URL}' for every stage not listed.`);
|
|
1058
|
-
this.id = id;
|
|
1059
|
-
this.stage = stage;
|
|
1060
|
-
this.stages = stages;
|
|
1061
|
-
this.name = "NoUrlForStage";
|
|
1062
|
-
}
|
|
1063
|
-
};
|
|
1064
|
-
//#endregion
|
|
1065
|
-
//#region src/headers.ts
|
|
1066
|
-
/**
|
|
1067
|
-
* Headers gkm reserves for its own services' calls to each other.
|
|
1068
|
-
*
|
|
1069
|
-
* Shared by `@geekmidas/constructs`, which sends and reads them, and
|
|
1070
|
-
* `@geekmidas/cli`, whose edges strip them from every request that arrives
|
|
1071
|
-
* from outside — so the two cannot name them differently.
|
|
1072
|
-
*/
|
|
1073
|
-
/**
|
|
1074
|
-
* The address of the client a service-to-service call is made on behalf of.
|
|
1075
|
-
*
|
|
1076
|
-
* An API asking its auth server for a session sends the client's address in
|
|
1077
|
-
* this rather than `X-Forwarded-For`: a forwarding header marks a request as
|
|
1078
|
-
* outside traffic, which would start the auth server's span in a new trace,
|
|
1079
|
-
* and the address is still needed — Better Auth rate-limits per client.
|
|
1080
|
-
*
|
|
1081
|
-
* Reserved: the stack's edge (Caddy, Traefik, the `gkm dev` edge) removes it
|
|
1082
|
-
* from incoming requests, and the auth server ignores it on any request that
|
|
1083
|
-
* is not an internal caller's, so it cannot be spoofed from outside.
|
|
1084
|
-
*/
|
|
1085
|
-
const CLIENT_IP_HEADER = "x-gkm-client-ip";
|
|
1086
|
-
//#endregion
|
|
1087
|
-
//#region src/migrations.ts
|
|
1088
|
-
/** Where every construct's folder lives, from the project root. */
|
|
1089
|
-
const MIGRATIONS_ROOT = "db";
|
|
1090
|
-
/** The two folders a construct's folder holds, and nothing else. */
|
|
1091
|
-
const DATABASE_FOLDERS = ["migrations", "seeds"];
|
|
1092
|
-
/**
|
|
1093
|
-
* The constructs that take migrations, parents before the tenants inside them.
|
|
1094
|
-
*
|
|
1095
|
-
* A database and each schema tenant: every one has an owner role and a schema
|
|
1096
|
-
* of its own, and so a history of its own. A reader has neither — it reads its
|
|
1097
|
-
* parent's schema — and a cache's table is created by provisioning, not by a
|
|
1098
|
-
* migration anyone writes.
|
|
1099
|
-
*/
|
|
1100
|
-
function migrationTargets(manifest) {
|
|
1101
|
-
return provisionOrder(manifest).flatMap((id) => {
|
|
1102
|
-
const declaration = manifest[id];
|
|
1103
|
-
if (!declaration) return [];
|
|
1104
|
-
if (declaration.kind === "database") return [target(id)];
|
|
1105
|
-
if (declaration.kind === "database-schema") return [{
|
|
1106
|
-
...target(id),
|
|
1107
|
-
of: declaration.of
|
|
1108
|
-
}];
|
|
1109
|
-
return [];
|
|
1110
|
-
});
|
|
1111
|
-
}
|
|
1112
|
-
/**
|
|
1113
|
-
* A construct's folder, relative to the project root.
|
|
1114
|
-
*
|
|
1115
|
-
* Named by the construct — `AuthDatabase` is `db/auth-database` — in the case
|
|
1116
|
-
* every other name outside TypeScript takes. Lowercase, because a folder that
|
|
1117
|
-
* differs from another only in case is two folders on Linux and one on a Mac;
|
|
1118
|
-
* and `canonicalId` reads it back to the construct exactly, which is how a
|
|
1119
|
-
* folder naming nothing is caught rather than skipped.
|
|
1120
|
-
*/
|
|
1121
|
-
function databaseFolder(id) {
|
|
1122
|
-
return `db/${kebabCase(id)}`;
|
|
1123
|
-
}
|
|
1124
|
-
/** Where a construct's migrations live: `db/auth-database/migrations`. */
|
|
1125
|
-
function migrationFolder(id) {
|
|
1126
|
-
return `${databaseFolder(id)}/migrations`;
|
|
1127
|
-
}
|
|
1128
|
-
/**
|
|
1129
|
-
* Where a construct's seeds live: `db/auth-database/seeds`.
|
|
1130
|
-
*
|
|
1131
|
-
* Reference data the app needs to run — a permission catalogue, roles, lookup
|
|
1132
|
-
* tables. Seeds are upserts run on every pass, after the migrations, so a
|
|
1133
|
-
* changed seed is applied by running it again. Sample data is factories'.
|
|
1134
|
-
*/
|
|
1135
|
-
function seedFolder(id) {
|
|
1136
|
-
return `${databaseFolder(id)}/seeds`;
|
|
1137
|
-
}
|
|
1138
|
-
function target(id) {
|
|
1139
|
-
return {
|
|
1140
|
-
id,
|
|
1141
|
-
folder: databaseFolder(id),
|
|
1142
|
-
migrations: migrationFolder(id),
|
|
1143
|
-
seeds: seedFolder(id)
|
|
1144
|
-
};
|
|
1145
|
-
}
|
|
1146
|
-
//#endregion
|
|
1147
|
-
//#region src/mobile.ts
|
|
1148
|
-
/**
|
|
1149
|
-
* How a mobile app is reached: its URL scheme, and the origins a surface trusts
|
|
1150
|
-
* for it.
|
|
1151
|
-
*
|
|
1152
|
-
* Stated once, here, because three targets ask — the local one (`gkm dev`,
|
|
1153
|
-
* `gkm test`), Dokploy and AWS — and a scheme one of them derived differently
|
|
1154
|
-
* would be an app whose sign-in callback the auth server refuses on exactly one
|
|
1155
|
-
* of them.
|
|
1156
|
-
*/
|
|
1157
|
-
/**
|
|
1158
|
-
* An app's scheme: the one given, or the project's name made a valid scheme.
|
|
1159
|
-
*
|
|
1160
|
-
* One for every stage. A development build and the store build register the
|
|
1161
|
-
* same scheme, so the auth server of whichever stage a build talks to trusts
|
|
1162
|
-
* it, and nothing about the app differs between stages but its URLs.
|
|
1163
|
-
*
|
|
1164
|
-
* A scheme is a letter followed by letters, digits, `+`, `-` or `.` — so
|
|
1165
|
-
* `Corner Shop` becomes `corner-shop`, and a name that starts with a digit gets a
|
|
1166
|
-
* leading `app`.
|
|
1167
|
-
*/
|
|
1168
|
-
function schemeBase(project, given) {
|
|
1169
|
-
const raw = (given ?? project).toLowerCase().replace(/[^a-z0-9+.-]+/g, "-").replace(/^-+|-+$/g, "");
|
|
1170
|
-
if (!raw) return "app";
|
|
1171
|
-
return /^[a-z]/.test(raw) ? raw : `app${raw}`;
|
|
1172
|
-
}
|
|
1173
|
-
/**
|
|
1174
|
-
* The origins a surface trusts for a mobile caller.
|
|
1175
|
-
*
|
|
1176
|
-
* Better Auth's Expo plugin sends the app's scheme as its origin
|
|
1177
|
-
* (`shop://`), and a link can carry a path after it (`shop://*`).
|
|
1178
|
-
* In development, Expo Go sends `exp://<host>:<port>` instead — so a local
|
|
1179
|
-
* stage adds the hosts a device actually reaches Metro on, and Metro's port
|
|
1180
|
-
* when the target placed it: a whole subnet is not trusted because one phone
|
|
1181
|
-
* is on it.
|
|
1182
|
-
*/
|
|
1183
|
-
function mobileOrigins(scheme, metro) {
|
|
1184
|
-
return [
|
|
1185
|
-
`${scheme}://`,
|
|
1186
|
-
`${scheme}://*`,
|
|
1187
|
-
...(metro?.hosts ?? []).flatMap((host) => [`exp://${host}:${metro?.port ?? "*"}`, `exp://${host}:${metro?.port ?? "*"}/**`])
|
|
1188
|
-
];
|
|
1189
|
-
}
|
|
1190
|
-
/** Whether an origin is a web one — what a cookie domain is derived from. */
|
|
1191
|
-
function isWebOrigin(origin) {
|
|
1192
|
-
return origin.startsWith("http://") || origin.startsWith("https://");
|
|
1
|
+
|
|
2
|
+
//#region src/index.ts
|
|
3
|
+
/** Flatten a manifest field (array or partitioned) into a plain array. */
|
|
4
|
+
function flattenManifestField(field) {
|
|
5
|
+
if (!field) return [];
|
|
6
|
+
return Array.isArray(field) ? [...field] : Object.values(field).flat();
|
|
1193
7
|
}
|
|
1194
|
-
//#endregion
|
|
1195
|
-
exports.CLIENT_IP_HEADER = CLIENT_IP_HEADER;
|
|
1196
|
-
exports.DATABASE_FOLDERS = DATABASE_FOLDERS;
|
|
1197
|
-
exports.DEFAULT_POSTGRES_VERSION = DEFAULT_POSTGRES_VERSION;
|
|
1198
|
-
exports.DEFAULT_STAGE_URL = DEFAULT_STAGE_URL;
|
|
1199
|
-
exports.DERIVES_FROM = DERIVES_FROM;
|
|
1200
|
-
exports.IllegalDerivation = IllegalDerivation;
|
|
1201
|
-
exports.InvalidConstructId = InvalidConstructId;
|
|
1202
|
-
exports.KMS_SCHEME = KMS_SCHEME;
|
|
1203
|
-
exports.MIGRATIONS_ROOT = MIGRATIONS_ROOT;
|
|
1204
|
-
exports.NoUrlForStage = NoUrlForStage;
|
|
1205
|
-
exports.PUBLIC = PUBLIC;
|
|
1206
|
-
exports.PUBLIC_PREFIX = PUBLIC_PREFIX;
|
|
1207
|
-
exports.TELEMETRY_KEYS = TELEMETRY_KEYS;
|
|
1208
|
-
exports.UnknownParent = UnknownParent;
|
|
1209
|
-
exports.assertDerivations = assertDerivations;
|
|
1210
|
-
exports.cacheTable = cacheTable;
|
|
1211
|
-
exports.canonicalId = canonicalId;
|
|
1212
|
-
exports.cloudName = cloudName;
|
|
1213
|
-
exports.cookieDomain = cookieDomain;
|
|
1214
|
-
exports.databaseFolder = databaseFolder;
|
|
1215
|
-
exports.dependenciesOf = dependenciesOf;
|
|
1216
|
-
exports.dependentsOf = dependentsOf;
|
|
1217
|
-
exports.environmentCase = environmentCase;
|
|
1218
|
-
exports.externalApiUrl = externalApiUrl;
|
|
1219
|
-
exports.isDerived = isDerived;
|
|
1220
|
-
exports.isWebOrigin = isWebOrigin;
|
|
1221
|
-
exports.kebabCase = kebabCase;
|
|
1222
|
-
exports.kmsUrl = kmsUrl;
|
|
1223
|
-
exports.migrationFolder = migrationFolder;
|
|
1224
|
-
exports.migrationTargets = migrationTargets;
|
|
1225
|
-
exports.mobileOrigins = mobileOrigins;
|
|
1226
|
-
exports.provideKey = provideKey;
|
|
1227
|
-
exports.providedKeyFor = providedKeyFor;
|
|
1228
|
-
exports.provisionOrder = provisionOrder;
|
|
1229
|
-
exports.publicEnvFor = publicEnvFor;
|
|
1230
|
-
exports.schemeBase = schemeBase;
|
|
1231
|
-
exports.scopedName = scopedName;
|
|
1232
|
-
exports.seedFolder = seedFolder;
|
|
1233
|
-
exports.serviceKey = serviceKey;
|
|
1234
8
|
|
|
9
|
+
//#endregion
|
|
10
|
+
exports.flattenManifestField = flattenManifestField;
|
|
1235
11
|
//# sourceMappingURL=index.cjs.map
|