@palbase/backend 22.1.0 → 23.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/dist/bin/palbase-backend.cjs +439 -18
- package/dist/bin/palbase-backend.cjs.map +1 -1
- package/dist/bin/palbase-backend.js +4 -4
- package/dist/{chunk-SQC5EIWY.js → chunk-FSGSB42K.js} +11 -6
- package/dist/chunk-FSGSB42K.js.map +1 -0
- package/dist/{chunk-I3ON7MYF.js → chunk-HAF67F2H.js} +73 -16
- package/dist/chunk-HAF67F2H.js.map +1 -0
- package/dist/{chunk-QMVK4X3V.js → chunk-OMRTHM4X.js} +98 -98
- package/dist/chunk-OMRTHM4X.js.map +1 -0
- package/dist/{chunk-74XDEF5J.js → chunk-REZU6UKT.js} +432 -17
- package/dist/chunk-REZU6UKT.js.map +1 -0
- package/dist/{chunk-YL4C5NRY.js → chunk-Y5HXVUMP.js} +2 -2
- package/dist/{chunk-N32VDWKH.js → chunk-ZC6Q2BRD.js} +4 -59
- package/dist/chunk-ZC6Q2BRD.js.map +1 -0
- package/dist/db/env.cjs.map +1 -1
- package/dist/db/env.d.cts +3 -21
- package/dist/db/env.d.ts +3 -21
- package/dist/db/index.cjs +72 -15
- package/dist/db/index.cjs.map +1 -1
- package/dist/db/index.d.cts +3 -2
- package/dist/db/index.d.ts +3 -2
- package/dist/db/index.js +1 -1
- package/dist/{endpoint-BVT6jcVW.d.cts → endpoint-BavvbW4P.d.ts} +77 -7
- package/dist/{endpoint-BVT6jcVW.d.ts → endpoint-i8TTCohk.d.cts} +77 -7
- package/dist/engine/index.cjs +439 -18
- package/dist/engine/index.cjs.map +1 -1
- package/dist/engine/index.d.cts +6 -5
- package/dist/engine/index.d.ts +6 -5
- package/dist/engine/index.js +3 -3
- package/dist/{index-vwHoS0l2.d.ts → index-B3jmmItD.d.ts} +111 -5
- package/dist/{index-CCZqzych.d.ts → index-B7YBEG5w.d.ts} +96 -71
- package/dist/{index-BqCiHao8.d.cts → index-Bmvx1EvJ.d.cts} +111 -5
- package/dist/{index-BS1gW4nV.d.cts → index-E7OscPJT.d.cts} +96 -71
- package/dist/index.cjs +285 -1099
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +89 -1134
- package/dist/index.d.ts +89 -1134
- package/dist/index.js +107 -902
- package/dist/index.js.map +1 -1
- package/dist/openapi/index.cjs +3 -57
- package/dist/openapi/index.cjs.map +1 -1
- package/dist/openapi/index.d.cts +3 -2
- package/dist/openapi/index.d.ts +3 -2
- package/dist/openapi/index.js +5 -22
- package/dist/openapi/index.js.map +1 -1
- package/dist/{registry-Bsuf-orT.d.ts → registry-C3H2uPeZ.d.cts} +13 -169
- package/dist/{registry-BWttGlaT.d.cts → registry-DY3d9l1k.d.ts} +13 -169
- package/dist/{purchases/keys.cjs → stack.cjs} +4 -4
- package/dist/stack.cjs.map +1 -0
- package/dist/stack.d.cts +76 -0
- package/dist/stack.d.ts +76 -0
- package/dist/stack.js +1 -0
- package/docs/README.md +7 -6
- package/docs/llms-full.txt +7 -260
- package/docs/llms.txt +0 -2
- package/package.json +6 -6
- package/template/package.json +1 -1
- package/dist/chunk-74XDEF5J.js.map +0 -1
- package/dist/chunk-I3ON7MYF.js.map +0 -1
- package/dist/chunk-N32VDWKH.js.map +0 -1
- package/dist/chunk-QMVK4X3V.js.map +0 -1
- package/dist/chunk-SQC5EIWY.js.map +0 -1
- package/dist/purchases/keys.cjs.map +0 -1
- package/dist/purchases/keys.d.cts +0 -42
- package/dist/purchases/keys.d.ts +0 -42
- package/dist/purchases/keys.js +0 -1
- package/docs/config.md +0 -147
- package/docs/resources.md +0 -97
- package/template/config/secrets.ts +0 -24
- /package/dist/{chunk-YL4C5NRY.js.map → chunk-Y5HXVUMP.js.map} +0 -0
- /package/dist/{purchases/keys.js.map → stack.js.map} +0 -0
package/dist/index.cjs
CHANGED
|
@@ -26,17 +26,11 @@ __export(src_exports, {
|
|
|
26
26
|
Client: () => Client,
|
|
27
27
|
Conflict: () => Conflict,
|
|
28
28
|
Controller: () => Controller,
|
|
29
|
-
DEFAULT_TEMPLATE_LOCALE: () => DEFAULT_TEMPLATE_LOCALE,
|
|
30
29
|
Database: () => Database,
|
|
31
30
|
Delete: () => Delete,
|
|
32
31
|
Deny: () => Deny,
|
|
33
32
|
Documents: () => Documents,
|
|
34
|
-
EGRESS_CONFIG_KIND: () => EGRESS_CONFIG_KIND,
|
|
35
|
-
EGRESS_TIMEOUT_DEFAULT_MS: () => EGRESS_TIMEOUT_DEFAULT_MS,
|
|
36
|
-
EGRESS_TIMEOUT_MAX_MS: () => EGRESS_TIMEOUT_MAX_MS,
|
|
37
|
-
EGRESS_TIMEOUT_MIN_MS: () => EGRESS_TIMEOUT_MIN_MS,
|
|
38
33
|
EXTENSION_DEPENDENCIES: () => EXTENSION_DEPENDENCIES,
|
|
39
|
-
FLAGS_CONFIG_KIND: () => FLAGS_CONFIG_KIND,
|
|
40
34
|
Flags: () => Flags,
|
|
41
35
|
Forbidden: () => Forbidden,
|
|
42
36
|
Get: () => Get,
|
|
@@ -45,35 +39,25 @@ __export(src_exports, {
|
|
|
45
39
|
HttpError: () => HttpError,
|
|
46
40
|
Job: () => Job,
|
|
47
41
|
Log: () => Log,
|
|
48
|
-
NOTIFICATIONS_CONFIG_KIND: () => NOTIFICATIONS_CONFIG_KIND,
|
|
49
42
|
NotFound: () => NotFound,
|
|
50
43
|
Notifications: () => Notifications,
|
|
51
44
|
On: () => On,
|
|
52
45
|
OptionalUser: () => OptionalUser,
|
|
53
46
|
PALBASE_EXTENSIONS: () => PALBASE_EXTENSIONS,
|
|
54
|
-
PROVIDER_CATALOG: () => PROVIDER_CATALOG,
|
|
55
47
|
PalError: () => PalError,
|
|
56
48
|
PalbaseModuleError: () => PalbaseModuleError,
|
|
57
49
|
Param: () => Param,
|
|
58
50
|
Patch: () => Patch,
|
|
59
51
|
PolicyBuilder: () => PolicyBuilder,
|
|
60
52
|
Post: () => Post,
|
|
61
|
-
Purchases: () => Purchases,
|
|
62
53
|
Put: () => Put,
|
|
63
54
|
Query: () => Query,
|
|
64
55
|
QueryParams: () => QueryParams,
|
|
65
|
-
RESERVED_SECRET_PREFIX: () => RESERVED_SECRET_PREFIX,
|
|
66
56
|
Realtime: () => Realtime,
|
|
67
57
|
Req: () => Req,
|
|
68
58
|
RequestId: () => RequestId,
|
|
69
|
-
RequireEntitlement: () => RequireEntitlement,
|
|
70
|
-
Resource: () => Resource,
|
|
71
|
-
SECRETS_CONFIG_KIND: () => SECRETS_CONFIG_KIND,
|
|
72
|
-
STORAGE_CONFIG_KIND: () => STORAGE_CONFIG_KIND,
|
|
73
59
|
Secrets: () => Secrets,
|
|
74
|
-
Spend: () => Spend,
|
|
75
60
|
Storage: () => Storage,
|
|
76
|
-
TEST_USERS_CONFIG_KIND: () => TEST_USERS_CONFIG_KIND,
|
|
77
61
|
TooManyRequests: () => TooManyRequests,
|
|
78
62
|
TraceId: () => TraceId,
|
|
79
63
|
TxPlanError: () => TxPlanError,
|
|
@@ -84,33 +68,19 @@ __export(src_exports, {
|
|
|
84
68
|
User: () => User,
|
|
85
69
|
Webhook: () => Webhook,
|
|
86
70
|
__getRuntime: () => __getRuntime,
|
|
87
|
-
__registerResource: () => __registerResource,
|
|
88
71
|
__requestALS: () => __requestALS,
|
|
89
72
|
__resetRegisteredControllers: () => __resetRegisteredControllers,
|
|
90
|
-
__runResourceBoot: () => __runResourceBoot,
|
|
91
73
|
__runWithRuntime: () => __runWithRuntime,
|
|
92
74
|
__setRuntime: () => __setRuntime,
|
|
93
|
-
__shutdownResources: () => __shutdownResources,
|
|
94
75
|
bigint: () => bigint,
|
|
95
76
|
boolean: () => boolean,
|
|
96
|
-
bucket: () => bucket,
|
|
97
77
|
buildModuleClients: () => buildModuleClients,
|
|
98
|
-
buildProvider: () => buildProvider,
|
|
99
78
|
dec: () => dec,
|
|
100
79
|
defineChannels: () => defineChannels,
|
|
101
|
-
defineEgress: () => defineEgress,
|
|
102
80
|
defineError: () => defineError,
|
|
103
|
-
defineFlags: () => defineFlags,
|
|
104
81
|
defineMiddleware: () => defineMiddleware,
|
|
105
|
-
defineNotifications: () => defineNotifications,
|
|
106
82
|
defineSchema: () => defineSchema,
|
|
107
|
-
defineSecrets: () => defineSecrets,
|
|
108
|
-
defineStorage: () => defineStorage,
|
|
109
|
-
defineTestUsers: () => defineTestUsers,
|
|
110
|
-
entitlementFor: () => entitlementFor,
|
|
111
83
|
enumType: () => enumType,
|
|
112
|
-
extractVariables: () => extractVariables,
|
|
113
|
-
flag: () => flag,
|
|
114
84
|
getErrorRegistry: () => getErrorRegistry,
|
|
115
85
|
getHookConfig: () => getHookConfig,
|
|
116
86
|
getJobConfig: () => getJobConfig,
|
|
@@ -123,26 +93,20 @@ __export(src_exports, {
|
|
|
123
93
|
jsonb: () => jsonb,
|
|
124
94
|
makeEnvDts: () => makeEnvDts,
|
|
125
95
|
makeHttpClient: () => makeHttpClient,
|
|
126
|
-
|
|
96
|
+
makeStackDts: () => makeStackDts,
|
|
127
97
|
makeTypedDB: () => makeTypedDB,
|
|
128
98
|
now: () => now,
|
|
129
99
|
numeric: () => numeric,
|
|
130
100
|
openai: () => openai,
|
|
131
101
|
ownerOnly: () => ownerOnly,
|
|
132
|
-
parseFileSizeLimit: () => parseFileSizeLimit,
|
|
133
102
|
policy: () => policy,
|
|
134
103
|
publicChannel: () => publicChannel,
|
|
135
104
|
raw: () => raw,
|
|
136
105
|
recordThrows: () => recordThrows,
|
|
137
|
-
reservedSecretKey: () => reservedSecretKey,
|
|
138
|
-
secret: () => secret,
|
|
139
|
-
spendFor: () => spendFor,
|
|
140
|
-
testUser: () => testUser,
|
|
141
106
|
text: () => text,
|
|
142
107
|
timestamp: () => timestamp,
|
|
143
108
|
toSchemaJSON: () => toSchemaJSON,
|
|
144
109
|
uuid: () => uuid,
|
|
145
|
-
validateUploadAgainstStorage: () => validateUploadAgainstStorage,
|
|
146
110
|
vector: () => vector,
|
|
147
111
|
z: () => import_zod2.z
|
|
148
112
|
});
|
|
@@ -601,7 +565,10 @@ function makeTablesAccessor(ops) {
|
|
|
601
565
|
delete: (id) => ops().delete(name, id),
|
|
602
566
|
findById: (id) => ops().findById(name, id),
|
|
603
567
|
findMany: (query) => ops().findMany(name, query),
|
|
604
|
-
search: (params) => ops().search(name, params)
|
|
568
|
+
search: (params) => ops().search(name, params),
|
|
569
|
+
similar: (id, params) => ops().similar(name, id, params),
|
|
570
|
+
recommend: (params) => ops().recommend(name, params),
|
|
571
|
+
supersede: (id, row) => ops().supersede(name, id, row)
|
|
605
572
|
};
|
|
606
573
|
}
|
|
607
574
|
}
|
|
@@ -610,6 +577,7 @@ function makeTablesAccessor(ops) {
|
|
|
610
577
|
}
|
|
611
578
|
var rawDatabase = makeServiceProxy("Database");
|
|
612
579
|
function makeTypedSurface(raw2) {
|
|
580
|
+
const reco = raw2;
|
|
613
581
|
const ops = {
|
|
614
582
|
query: (sql, params) => raw2.query(sql, params),
|
|
615
583
|
insert: (table, data) => raw2.insert(table, data),
|
|
@@ -617,10 +585,13 @@ function makeTypedSurface(raw2) {
|
|
|
617
585
|
delete: (table, id) => raw2.delete(table, id),
|
|
618
586
|
findById: (table, id) => raw2.findById(table, id),
|
|
619
587
|
findMany: (table, query) => raw2.findMany(table, query),
|
|
620
|
-
search: (table, params) => raw2.search(table, params)
|
|
588
|
+
search: (table, params) => raw2.search(table, params),
|
|
589
|
+
similar: (table, id, params) => reco.similar(table, id, params),
|
|
590
|
+
recommend: (table, params) => reco.recommend(table, params),
|
|
591
|
+
supersede: (table, id, row) => raw2.supersede(table, id, row)
|
|
621
592
|
};
|
|
622
593
|
return Object.assign(ops, {
|
|
623
|
-
tables: makeTablesAccessor(() =>
|
|
594
|
+
tables: makeTablesAccessor(() => reco),
|
|
624
595
|
transaction(fn) {
|
|
625
596
|
const builder = new TxPlanBuilder();
|
|
626
597
|
return runTxPlan(raw2, makeTxTablesAccessor(builder), builder, fn);
|
|
@@ -683,7 +654,6 @@ var Cache = makeServiceProxy("Cache");
|
|
|
683
654
|
var Secrets = makeServiceProxy("Secrets");
|
|
684
655
|
var Log = makeServiceProxy("Log");
|
|
685
656
|
var Notifications = makeServiceProxy("Notifications");
|
|
686
|
-
var Purchases = makeServiceProxy("Purchases");
|
|
687
657
|
var rawFlags = makeServiceProxy("Flags");
|
|
688
658
|
var Flags = Object.assign(
|
|
689
659
|
{
|
|
@@ -727,250 +697,6 @@ var Flags = Object.assign(
|
|
|
727
697
|
);
|
|
728
698
|
var Realtime = makeServiceProxy("Realtime");
|
|
729
699
|
|
|
730
|
-
// src/errors.ts
|
|
731
|
-
var HTTP_ERROR_BRAND = /* @__PURE__ */ Symbol.for("palbase.backend.httpError");
|
|
732
|
-
var HttpError = class extends Error {
|
|
733
|
-
status;
|
|
734
|
-
error;
|
|
735
|
-
errorDescription;
|
|
736
|
-
data;
|
|
737
|
-
/** See {@link HTTP_ERROR_BRAND} — how the engine recognises this across SDK copies. */
|
|
738
|
-
[HTTP_ERROR_BRAND] = true;
|
|
739
|
-
constructor(status, error, errorDescription, data) {
|
|
740
|
-
super(errorDescription);
|
|
741
|
-
this.name = "HttpError";
|
|
742
|
-
this.status = status;
|
|
743
|
-
this.error = error;
|
|
744
|
-
this.errorDescription = errorDescription;
|
|
745
|
-
if (data !== void 0) {
|
|
746
|
-
this.data = data;
|
|
747
|
-
}
|
|
748
|
-
}
|
|
749
|
-
/**
|
|
750
|
-
* Serialize to the standard Palbase error response format.
|
|
751
|
-
* The `requestId` is injected by the runtime layer from the request context.
|
|
752
|
-
* When called without arguments (e.g. JSON.stringify), request_id is omitted.
|
|
753
|
-
* When `data` is set, it is appended as a strict-superset field.
|
|
754
|
-
*/
|
|
755
|
-
toJSON(requestId) {
|
|
756
|
-
const result = {
|
|
757
|
-
error: this.error,
|
|
758
|
-
error_description: this.errorDescription,
|
|
759
|
-
status: this.status
|
|
760
|
-
};
|
|
761
|
-
if (requestId) {
|
|
762
|
-
result.request_id = requestId;
|
|
763
|
-
}
|
|
764
|
-
if (this.data !== void 0) {
|
|
765
|
-
result.data = this.data;
|
|
766
|
-
}
|
|
767
|
-
return result;
|
|
768
|
-
}
|
|
769
|
-
};
|
|
770
|
-
var PalError = class extends HttpError {
|
|
771
|
-
constructor(status, code, description, data) {
|
|
772
|
-
super(status, code, description, data);
|
|
773
|
-
this.name = "PalError";
|
|
774
|
-
}
|
|
775
|
-
};
|
|
776
|
-
var NamedHttpError = class extends HttpError {
|
|
777
|
-
constructor(status, defaultCode, name, message, code, data) {
|
|
778
|
-
super(status, code ?? defaultCode, message ?? defaultMessage(name), data);
|
|
779
|
-
this.name = name;
|
|
780
|
-
}
|
|
781
|
-
};
|
|
782
|
-
function defaultMessage(name) {
|
|
783
|
-
const spaced = name.replace(/([a-z0-9])([A-Z])/g, "$1 $2");
|
|
784
|
-
return spaced.charAt(0).toUpperCase() + spaced.slice(1).toLowerCase();
|
|
785
|
-
}
|
|
786
|
-
var BadRequest = class extends NamedHttpError {
|
|
787
|
-
constructor(data, message) {
|
|
788
|
-
super(400, "bad_request", "BadRequest", message, void 0, data);
|
|
789
|
-
}
|
|
790
|
-
};
|
|
791
|
-
var Unauthorized = class extends NamedHttpError {
|
|
792
|
-
constructor(message, code, data) {
|
|
793
|
-
super(401, "unauthorized", "Unauthorized", message, code, data);
|
|
794
|
-
}
|
|
795
|
-
};
|
|
796
|
-
var Forbidden = class extends NamedHttpError {
|
|
797
|
-
constructor(message, code, data) {
|
|
798
|
-
super(403, "forbidden", "Forbidden", message, code, data);
|
|
799
|
-
}
|
|
800
|
-
};
|
|
801
|
-
var NotFound = class extends NamedHttpError {
|
|
802
|
-
constructor(message, code, data) {
|
|
803
|
-
super(404, "not_found", "NotFound", message, code, data);
|
|
804
|
-
}
|
|
805
|
-
};
|
|
806
|
-
var Conflict = class extends NamedHttpError {
|
|
807
|
-
constructor(message, code, data) {
|
|
808
|
-
super(409, "conflict", "Conflict", message, code, data);
|
|
809
|
-
}
|
|
810
|
-
};
|
|
811
|
-
var TooManyRequests = class extends NamedHttpError {
|
|
812
|
-
constructor(data, message) {
|
|
813
|
-
super(429, "too_many_requests", "TooManyRequests", message, void 0, data);
|
|
814
|
-
}
|
|
815
|
-
};
|
|
816
|
-
|
|
817
|
-
// src/purchases/errors.ts
|
|
818
|
-
function asPalstoreError(err) {
|
|
819
|
-
if (!(err instanceof Error)) return null;
|
|
820
|
-
const e = err;
|
|
821
|
-
if (typeof e.code !== "string" || typeof e.status !== "number") return null;
|
|
822
|
-
return { code: e.code, status: e.status, message: e.message };
|
|
823
|
-
}
|
|
824
|
-
function toHttpError(err) {
|
|
825
|
-
if (err instanceof HttpError) return err;
|
|
826
|
-
const p = asPalstoreError(err);
|
|
827
|
-
if (!p) return err;
|
|
828
|
-
switch (p.code) {
|
|
829
|
-
case "entitlement_required": {
|
|
830
|
-
const entitlement = err.entitlement ?? null;
|
|
831
|
-
return new PalError(403, "entitlement_required", p.message, { entitlement });
|
|
832
|
-
}
|
|
833
|
-
case "quota_exceeded":
|
|
834
|
-
case "credit_insufficient": {
|
|
835
|
-
const state = err.state;
|
|
836
|
-
return new PalError(429, p.code, p.message, state);
|
|
837
|
-
}
|
|
838
|
-
default:
|
|
839
|
-
return err;
|
|
840
|
-
}
|
|
841
|
-
}
|
|
842
|
-
|
|
843
|
-
// src/purchases/registry.ts
|
|
844
|
-
var ENTITLEMENTS = /* @__PURE__ */ Symbol.for("palbase.backend.purchases.entitlements");
|
|
845
|
-
var SPENDS = /* @__PURE__ */ Symbol.for("palbase.backend.purchases.spends");
|
|
846
|
-
function carrierOf(target) {
|
|
847
|
-
return typeof target === "function" ? target : target.constructor ?? target;
|
|
848
|
-
}
|
|
849
|
-
function own(carrier, slot) {
|
|
850
|
-
if (!Object.prototype.hasOwnProperty.call(carrier, slot)) {
|
|
851
|
-
carrier[slot] = {};
|
|
852
|
-
}
|
|
853
|
-
return carrier[slot];
|
|
854
|
-
}
|
|
855
|
-
function recordEntitlement(target, fnName, key) {
|
|
856
|
-
own(carrierOf(target), ENTITLEMENTS)[fnName] = key;
|
|
857
|
-
}
|
|
858
|
-
function recordSpend(target, fnName, meta) {
|
|
859
|
-
own(carrierOf(target), SPENDS)[fnName] = meta;
|
|
860
|
-
}
|
|
861
|
-
function entitlementFor(ctor, fnName) {
|
|
862
|
-
return carrierOf(ctor)[ENTITLEMENTS]?.[fnName];
|
|
863
|
-
}
|
|
864
|
-
function spendFor(ctor, fnName) {
|
|
865
|
-
return carrierOf(ctor)[SPENDS]?.[fnName];
|
|
866
|
-
}
|
|
867
|
-
|
|
868
|
-
// src/purchases/subject.ts
|
|
869
|
-
var subjectByRequest = /* @__PURE__ */ new WeakMap();
|
|
870
|
-
function storeEnv() {
|
|
871
|
-
return process.env.PALSTORE_STORE_ENV === "sandbox" ? "sandbox" : "production";
|
|
872
|
-
}
|
|
873
|
-
async function currentSubjectId() {
|
|
874
|
-
const store = __requestALS.getStore();
|
|
875
|
-
if (!store) {
|
|
876
|
-
throw new Error(
|
|
877
|
-
"Purchases decorators used outside a request scope. @RequireEntitlement/@Spend resolve the subject from the request's authenticated user, so they only work inside an endpoint handler."
|
|
878
|
-
);
|
|
879
|
-
}
|
|
880
|
-
const memoized = subjectByRequest.get(store);
|
|
881
|
-
if (memoized) return memoized;
|
|
882
|
-
const userId = store.userId;
|
|
883
|
-
if (!userId) {
|
|
884
|
-
throw new Unauthorized(
|
|
885
|
-
"This endpoint requires an authenticated user: purchases entitlements and quota are per-user, and the subject is resolved from the request's identity."
|
|
886
|
-
);
|
|
887
|
-
}
|
|
888
|
-
const resolving = Purchases.resolveSubject({ userRef: userId, storeEnv: storeEnv() }).then(
|
|
889
|
-
(r) => r.subjectId
|
|
890
|
-
);
|
|
891
|
-
subjectByRequest.set(store, resolving);
|
|
892
|
-
resolving.catch(() => subjectByRequest.delete(store));
|
|
893
|
-
return resolving;
|
|
894
|
-
}
|
|
895
|
-
|
|
896
|
-
// src/purchases/decorators.ts
|
|
897
|
-
var WRAPPED = /* @__PURE__ */ Symbol.for("palbase.backend.purchases.wrapped");
|
|
898
|
-
function idempotencyKeyFor(key) {
|
|
899
|
-
const store = __requestALS.getStore();
|
|
900
|
-
const base = store?.idempotencyKey || store?.requestId;
|
|
901
|
-
if (!base) {
|
|
902
|
-
throw new Error(
|
|
903
|
-
"@Spend needs a request-scoped idempotency key, and the runtime supplied neither an Idempotency-Key header nor a request id. Spending without one would let a retried request charge the user twice."
|
|
904
|
-
);
|
|
905
|
-
}
|
|
906
|
-
return `${base}:${key}`;
|
|
907
|
-
}
|
|
908
|
-
function ensureWrapper(target, fnName, descriptor) {
|
|
909
|
-
const original = descriptor.value;
|
|
910
|
-
if (typeof original !== "function") {
|
|
911
|
-
throw new Error(
|
|
912
|
-
`@RequireEntitlement/@Spend can only decorate a method; "${fnName}" is not one.`
|
|
913
|
-
);
|
|
914
|
-
}
|
|
915
|
-
if (original[WRAPPED]) return;
|
|
916
|
-
const wrapper = async function(...args) {
|
|
917
|
-
const ctor = this.constructor;
|
|
918
|
-
const entitlement = entitlementFor(ctor, fnName);
|
|
919
|
-
const spend = spendFor(ctor, fnName);
|
|
920
|
-
let subjectId;
|
|
921
|
-
try {
|
|
922
|
-
subjectId = await currentSubjectId();
|
|
923
|
-
} catch (err) {
|
|
924
|
-
throw toHttpError(err);
|
|
925
|
-
}
|
|
926
|
-
if (entitlement !== void 0) {
|
|
927
|
-
try {
|
|
928
|
-
await Purchases.require(subjectId, entitlement);
|
|
929
|
-
} catch (err) {
|
|
930
|
-
throw toHttpError(err);
|
|
931
|
-
}
|
|
932
|
-
}
|
|
933
|
-
if (spend === void 0) {
|
|
934
|
-
return original.apply(this, args);
|
|
935
|
-
}
|
|
936
|
-
let handlerFailure;
|
|
937
|
-
try {
|
|
938
|
-
return await Purchases.withSpend(
|
|
939
|
-
subjectId,
|
|
940
|
-
spend.key,
|
|
941
|
-
{ count: spend.count, idempotencyKey: idempotencyKeyFor(spend.key) },
|
|
942
|
-
async () => {
|
|
943
|
-
try {
|
|
944
|
-
return await original.apply(this, args);
|
|
945
|
-
} catch (err) {
|
|
946
|
-
handlerFailure = { err };
|
|
947
|
-
throw err;
|
|
948
|
-
}
|
|
949
|
-
}
|
|
950
|
-
);
|
|
951
|
-
} catch (err) {
|
|
952
|
-
if (handlerFailure !== void 0 && handlerFailure.err === err) throw err;
|
|
953
|
-
throw toHttpError(err);
|
|
954
|
-
}
|
|
955
|
-
};
|
|
956
|
-
wrapper[WRAPPED] = true;
|
|
957
|
-
descriptor.value = wrapper;
|
|
958
|
-
}
|
|
959
|
-
function RequireEntitlement(key) {
|
|
960
|
-
return function(target, propertyKey, descriptor) {
|
|
961
|
-
const fnName = String(propertyKey);
|
|
962
|
-
recordEntitlement(target, fnName, key);
|
|
963
|
-
ensureWrapper(target, fnName, descriptor);
|
|
964
|
-
};
|
|
965
|
-
}
|
|
966
|
-
function Spend(key) {
|
|
967
|
-
return function(target, propertyKey, descriptor) {
|
|
968
|
-
const fnName = String(propertyKey);
|
|
969
|
-
recordSpend(target, fnName, { key, count: 1 });
|
|
970
|
-
ensureWrapper(target, fnName, descriptor);
|
|
971
|
-
};
|
|
972
|
-
}
|
|
973
|
-
|
|
974
700
|
// src/clients/http.ts
|
|
975
701
|
var PalbaseModuleError = class extends Error {
|
|
976
702
|
code;
|
|
@@ -1316,12 +1042,12 @@ function buildFlagsClient(http, cfg) {
|
|
|
1316
1042
|
return {
|
|
1317
1043
|
data: Object.keys(values).map((name) => {
|
|
1318
1044
|
const value = values[name];
|
|
1319
|
-
const
|
|
1045
|
+
const flag = {
|
|
1320
1046
|
name,
|
|
1321
1047
|
enabled: flagEnabled(value)
|
|
1322
1048
|
};
|
|
1323
|
-
if (typeof value === "string")
|
|
1324
|
-
return
|
|
1049
|
+
if (typeof value === "string") flag.variant = { name: value };
|
|
1050
|
+
return flag;
|
|
1325
1051
|
}),
|
|
1326
1052
|
error: null,
|
|
1327
1053
|
status: res.status
|
|
@@ -1549,13 +1275,13 @@ function buildNotificationsClient(http) {
|
|
|
1549
1275
|
|
|
1550
1276
|
// src/clients/realtime.ts
|
|
1551
1277
|
var import_node_crypto = require("crypto");
|
|
1552
|
-
function mintToken(
|
|
1278
|
+
function mintToken(secret) {
|
|
1553
1279
|
const now2 = Math.floor(Date.now() / 1e3);
|
|
1554
1280
|
const header = Buffer.from(JSON.stringify({ alg: "HS256", typ: "JWT" })).toString("base64url");
|
|
1555
1281
|
const payload = Buffer.from(
|
|
1556
1282
|
JSON.stringify({ iss: "backend-runtime", role: "service_role", iat: now2, exp: now2 + 60 })
|
|
1557
1283
|
).toString("base64url");
|
|
1558
|
-
const sig = (0, import_node_crypto.createHmac)("sha256",
|
|
1284
|
+
const sig = (0, import_node_crypto.createHmac)("sha256", secret).update(`${header}.${payload}`).digest("base64url");
|
|
1559
1285
|
return `${header}.${payload}.${sig}`;
|
|
1560
1286
|
}
|
|
1561
1287
|
function buildRealtimeClient(cfg) {
|
|
@@ -1726,11 +1452,11 @@ function validateStoragePath(p) {
|
|
|
1726
1452
|
);
|
|
1727
1453
|
}
|
|
1728
1454
|
}
|
|
1729
|
-
function toFileObject(
|
|
1455
|
+
function toFileObject(bucket, row) {
|
|
1730
1456
|
return {
|
|
1731
1457
|
name: row.path ?? "",
|
|
1732
1458
|
path: row.path ?? "",
|
|
1733
|
-
bucket: row.bucket ??
|
|
1459
|
+
bucket: row.bucket ?? bucket,
|
|
1734
1460
|
size: typeof row.size === "number" ? row.size : 0,
|
|
1735
1461
|
contentType: row.contentType ?? "",
|
|
1736
1462
|
checksum: row.checksum ?? "",
|
|
@@ -1846,35 +1572,6 @@ function buildModuleClients(cfg) {
|
|
|
1846
1572
|
};
|
|
1847
1573
|
}
|
|
1848
1574
|
|
|
1849
|
-
// src/purchases/keys-gen.ts
|
|
1850
|
-
function liveKeys(entries) {
|
|
1851
|
-
return Object.entries(entries ?? {}).filter(([, def]) => def?.removed !== true).map(([key]) => key).sort();
|
|
1852
|
-
}
|
|
1853
|
-
function memberName(key) {
|
|
1854
|
-
return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(key) ? key : JSON.stringify(key);
|
|
1855
|
-
}
|
|
1856
|
-
function members(keys) {
|
|
1857
|
-
if (keys.length === 0) return "";
|
|
1858
|
-
return `
|
|
1859
|
-
${keys.map((k) => ` ${memberName(k)}: true;`).join("\n")}
|
|
1860
|
-
`;
|
|
1861
|
-
}
|
|
1862
|
-
function makePurchasesDts(manifest) {
|
|
1863
|
-
const entitlements = liveKeys(manifest.entitlements);
|
|
1864
|
-
const limits = liveKeys({ ...manifest.limits, ...manifest.credits });
|
|
1865
|
-
return `// palbase-purchases.d.ts \u2014 GENERATED by @palbase/backend. Do not edit.
|
|
1866
|
-
// Source: the project's palstore catalog revision.
|
|
1867
|
-
|
|
1868
|
-
declare module "@palbase/backend/purchases" {
|
|
1869
|
-
interface Entitlements {${members(entitlements)}}
|
|
1870
|
-
|
|
1871
|
-
interface Limits {${members(limits)}}
|
|
1872
|
-
}
|
|
1873
|
-
|
|
1874
|
-
export {};
|
|
1875
|
-
`;
|
|
1876
|
-
}
|
|
1877
|
-
|
|
1878
1575
|
// src/db/policy.ts
|
|
1879
1576
|
var PolicyBuilder = class {
|
|
1880
1577
|
_def;
|
|
@@ -1950,27 +1647,81 @@ function defineSchema(input) {
|
|
|
1950
1647
|
if (table.checks !== void 0 && table.checks.length > 0) tableDef.checks = table.checks.slice();
|
|
1951
1648
|
if (table.indexes !== void 0 && table.indexes.length > 0) tableDef.indexes = table.indexes.slice();
|
|
1952
1649
|
if (table.search !== void 0) {
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
throw new Error(`table ${String(name)}: search.text bo\u015F olamaz \u2014 FTS kolu istemiyorsan alan\u0131 hi\xE7 yazma`);
|
|
1960
|
-
}
|
|
1961
|
-
const legs = table.search.vector === void 0 ? [] : Array.isArray(table.search.vector) ? table.search.vector : [table.search.vector];
|
|
1962
|
-
for (const leg of legs) {
|
|
1963
|
-
if (leg.model !== void 0 && (leg.from === void 0 || leg.from.length === 0)) {
|
|
1964
|
-
throw new Error(`table ${String(name)}: search.vector.model beyan edildi ama 'from' yok \u2014 embed kayna\u011F\u0131 kolonlar zorunlu (C-2)`);
|
|
1650
|
+
const s = table.search;
|
|
1651
|
+
if (s.from !== void 0) {
|
|
1652
|
+
if (s.vector !== void 0) {
|
|
1653
|
+
throw new Error(
|
|
1654
|
+
`table ${String(name)}: search beyan\u0131nda tek bi\xE7im kullan\u0131n \u2014 'from' (yeni) ile 'vector' (eski) birlikte olamaz`
|
|
1655
|
+
);
|
|
1965
1656
|
}
|
|
1966
|
-
if (
|
|
1967
|
-
throw new Error(`table ${String(name)}: search.
|
|
1657
|
+
if (s.from.length === 0) {
|
|
1658
|
+
throw new Error(`table ${String(name)}: search.from bo\u015F olamaz`);
|
|
1659
|
+
}
|
|
1660
|
+
if (s.model === void 0) {
|
|
1661
|
+
throw new Error(
|
|
1662
|
+
`table ${String(name)}: search.from model'siz anlams\u0131z \u2014 auto-embed i\xE7in model verin (BYO i\xE7in eski 'vector' bi\xE7imini kullan\u0131n)`
|
|
1663
|
+
);
|
|
1664
|
+
}
|
|
1665
|
+
if (Array.isArray(s.text) && s.text.length === 0) {
|
|
1666
|
+
throw new Error(`table ${String(name)}: search.text bo\u015F dizi olamaz \u2014 FTS istemiyorsan text: false yaz\u0131n`);
|
|
1667
|
+
}
|
|
1668
|
+
} else {
|
|
1669
|
+
if (typeof s.text === "boolean") {
|
|
1670
|
+
throw new Error(`table ${String(name)}: text:${String(s.text)} yaln\u0131z yeni bi\xE7imde ('from' ile) ge\xE7erli`);
|
|
1671
|
+
}
|
|
1672
|
+
if (s.text === void 0 && s.vector === void 0) {
|
|
1673
|
+
throw new Error(
|
|
1674
|
+
`table ${String(name)}: search beyan\u0131 bo\u015F \u2014 en az bir kol (text ya da vector) verin, yoksa alan\u0131 hi\xE7 yazmay\u0131n`
|
|
1675
|
+
);
|
|
1676
|
+
}
|
|
1677
|
+
if (s.text !== void 0 && s.text.length === 0) {
|
|
1678
|
+
throw new Error(`table ${String(name)}: search.text bo\u015F olamaz \u2014 FTS kolu istemiyorsan alan\u0131 hi\xE7 yazma`);
|
|
1679
|
+
}
|
|
1680
|
+
const legs = s.vector === void 0 ? [] : Array.isArray(s.vector) ? s.vector : [s.vector];
|
|
1681
|
+
for (const leg of legs) {
|
|
1682
|
+
if (leg.model !== void 0 && (leg.from === void 0 || leg.from.length === 0)) {
|
|
1683
|
+
throw new Error(`table ${String(name)}: search.vector.model beyan edildi ama 'from' yok \u2014 embed kayna\u011F\u0131 kolonlar zorunlu (C-2)`);
|
|
1684
|
+
}
|
|
1685
|
+
if (leg.model === void 0 && leg.from !== void 0) {
|
|
1686
|
+
throw new Error(`table ${String(name)}: search.vector.from model'siz anlams\u0131z \u2014 auto-embed i\xE7in model verin`);
|
|
1687
|
+
}
|
|
1968
1688
|
}
|
|
1969
1689
|
}
|
|
1970
|
-
tableDef.search =
|
|
1690
|
+
tableDef.search = s;
|
|
1691
|
+
}
|
|
1692
|
+
if (table.memory !== void 0) {
|
|
1693
|
+
if (table.memory.from.length === 0) {
|
|
1694
|
+
throw new Error(`table ${String(name)}: memory.from bo\u015F olamaz`);
|
|
1695
|
+
}
|
|
1696
|
+
tableDef.memory = table.memory;
|
|
1971
1697
|
}
|
|
1972
1698
|
tables[name] = tableDef;
|
|
1973
1699
|
}
|
|
1700
|
+
for (const [name, def] of Object.entries(tables)) {
|
|
1701
|
+
const m = def.memory;
|
|
1702
|
+
if (m === void 0) continue;
|
|
1703
|
+
const target = Object.values(tables).find((t) => t.name === m.into);
|
|
1704
|
+
if (target === void 0) {
|
|
1705
|
+
throw new Error(`table ${name}: memory.into "${m.into}" \u015Femada declared de\u011Fil`);
|
|
1706
|
+
}
|
|
1707
|
+
const subject = m.subject ?? "owner";
|
|
1708
|
+
if (!(subject in def.columns)) {
|
|
1709
|
+
throw new Error(`table ${name}: memory.subject "${subject}" kolonu kaynak tabloda yok`);
|
|
1710
|
+
}
|
|
1711
|
+
if (!(subject in target.columns)) {
|
|
1712
|
+
throw new Error(`table ${name}: memory.subject "${subject}" kolonu hedef "${m.into}" tablosunda yok`);
|
|
1713
|
+
}
|
|
1714
|
+
const factCol = target.columns["fact"];
|
|
1715
|
+
const factDef = factCol !== void 0 && "_def" in factCol ? factCol._def : factCol;
|
|
1716
|
+
if (factDef === void 0 || factDef.type !== "text") {
|
|
1717
|
+
throw new Error(`table ${name}: memory.into "${m.into}" tablosunda "fact" (text) kolonu zorunlu`);
|
|
1718
|
+
}
|
|
1719
|
+
for (const c of m.from) {
|
|
1720
|
+
if (!(c in def.columns)) {
|
|
1721
|
+
throw new Error(`table ${name}: memory.from kolonu "${c}" kaynak tabloda yok`);
|
|
1722
|
+
}
|
|
1723
|
+
}
|
|
1724
|
+
}
|
|
1974
1725
|
const extensions = [...new Set(input.extensions ?? [])];
|
|
1975
1726
|
return { tables, extensions };
|
|
1976
1727
|
}
|
|
@@ -2013,9 +1764,45 @@ function policyToJSON(policy2) {
|
|
|
2013
1764
|
permissive: policy2.permissive !== false
|
|
2014
1765
|
};
|
|
2015
1766
|
}
|
|
2016
|
-
function
|
|
1767
|
+
function commonSearchFields(search, out) {
|
|
1768
|
+
if (search.synonyms !== void 0 && Object.keys(search.synonyms).length > 0) {
|
|
1769
|
+
out.synonyms = Object.fromEntries(
|
|
1770
|
+
Object.entries(search.synonyms).map(([word, alts]) => [word, [...alts]])
|
|
1771
|
+
);
|
|
1772
|
+
}
|
|
1773
|
+
if (search.validity === true) out.validity = true;
|
|
1774
|
+
}
|
|
1775
|
+
function searchToJSON(search, vectorColumn) {
|
|
1776
|
+
if (search.from !== void 0 && search.model !== void 0) {
|
|
1777
|
+
const out2 = {};
|
|
1778
|
+
const textCols = search.text === false ? void 0 : Array.isArray(search.text) && search.text.length > 0 ? search.text : search.from;
|
|
1779
|
+
if (textCols !== void 0) out2.text = { columns: [...textCols] };
|
|
1780
|
+
const v = { metric: search.metric ?? "cosine" };
|
|
1781
|
+
if (vectorColumn !== void 0) v.column = vectorColumn;
|
|
1782
|
+
v.embed = {
|
|
1783
|
+
provider: search.model.provider,
|
|
1784
|
+
model: search.model.model,
|
|
1785
|
+
from: [...search.from],
|
|
1786
|
+
...search.model.apiKeyName !== void 0 ? { apiKeyName: search.model.apiKeyName } : {},
|
|
1787
|
+
...search.model.dimensions !== void 0 ? { dimensions: search.model.dimensions } : {},
|
|
1788
|
+
...search.model.baseURL !== void 0 ? { baseURL: search.model.baseURL } : {}
|
|
1789
|
+
};
|
|
1790
|
+
if (search.staleness !== void 0) v.staleness = search.staleness;
|
|
1791
|
+
if (vectorColumn === void 0) {
|
|
1792
|
+
v.mode = "chunks";
|
|
1793
|
+
if (search.chunks !== void 0) {
|
|
1794
|
+
const c = {};
|
|
1795
|
+
if (search.chunks.size !== void 0 && search.chunks.size > 0) c.sizeChars = search.chunks.size;
|
|
1796
|
+
if (search.chunks.overlap !== void 0 && search.chunks.overlap > 0) c.overlapChars = search.chunks.overlap;
|
|
1797
|
+
if (Object.keys(c).length > 0) v.chunks = c;
|
|
1798
|
+
}
|
|
1799
|
+
}
|
|
1800
|
+
out2.vector = [v];
|
|
1801
|
+
commonSearchFields(search, out2);
|
|
1802
|
+
return out2;
|
|
1803
|
+
}
|
|
2017
1804
|
const out = {};
|
|
2018
|
-
if (search.text
|
|
1805
|
+
if (Array.isArray(search.text) && search.text.length > 0) out.text = { columns: [...search.text] };
|
|
2019
1806
|
const legs = search.vector === void 0 ? [] : Array.isArray(search.vector) ? search.vector : [search.vector];
|
|
2020
1807
|
if (legs.length > 0) {
|
|
2021
1808
|
out.vector = legs.map((leg) => {
|
|
@@ -2035,8 +1822,17 @@ function searchToJSON(search) {
|
|
|
2035
1822
|
return v;
|
|
2036
1823
|
});
|
|
2037
1824
|
}
|
|
1825
|
+
commonSearchFields(search, out);
|
|
2038
1826
|
return out;
|
|
2039
1827
|
}
|
|
1828
|
+
function memoryToJSON(m) {
|
|
1829
|
+
return {
|
|
1830
|
+
from: [...m.from],
|
|
1831
|
+
into: m.into,
|
|
1832
|
+
...m.subject !== void 0 ? { subject: m.subject } : {},
|
|
1833
|
+
extract: { provider: m.extract.provider, model: m.extract.model }
|
|
1834
|
+
};
|
|
1835
|
+
}
|
|
2040
1836
|
function tableToJSON(table) {
|
|
2041
1837
|
const columns = {};
|
|
2042
1838
|
for (const [name, column] of Object.entries(table.columns)) {
|
|
@@ -2073,9 +1869,13 @@ function tableToJSON(table) {
|
|
|
2073
1869
|
out.indexes = table.indexes.map((i) => ({ name: i.name, columns: [...i.columns] }));
|
|
2074
1870
|
}
|
|
2075
1871
|
if (table.search !== void 0) {
|
|
2076
|
-
const
|
|
1872
|
+
const vectorColumn = Object.entries(columns).find(([, c]) => c.dimensions !== void 0)?.[0];
|
|
1873
|
+
const sj = searchToJSON(table.search, vectorColumn);
|
|
2077
1874
|
if (sj.text !== void 0 || sj.vector !== void 0) out.search = sj;
|
|
2078
1875
|
}
|
|
1876
|
+
if (table.memory !== void 0) {
|
|
1877
|
+
out.memory = memoryToJSON(table.memory);
|
|
1878
|
+
}
|
|
2079
1879
|
return out;
|
|
2080
1880
|
}
|
|
2081
1881
|
function toSchemaJSON(schema) {
|
|
@@ -2370,6 +2170,9 @@ var openai = {
|
|
|
2370
2170
|
...opts?.dimensions !== void 0 ? { dimensions: opts.dimensions } : {},
|
|
2371
2171
|
...opts?.baseURL !== void 0 ? { baseURL: opts.baseURL } : {}
|
|
2372
2172
|
};
|
|
2173
|
+
},
|
|
2174
|
+
chat(model) {
|
|
2175
|
+
return { provider: "openai", model };
|
|
2373
2176
|
}
|
|
2374
2177
|
};
|
|
2375
2178
|
|
|
@@ -2470,10 +2273,10 @@ function buildChildEdges(schema) {
|
|
|
2470
2273
|
const ref = builder._def.references;
|
|
2471
2274
|
if (ref === void 0) continue;
|
|
2472
2275
|
if (ref.table === childName) continue;
|
|
2473
|
-
const
|
|
2474
|
-
if (
|
|
2475
|
-
if (
|
|
2476
|
-
|
|
2276
|
+
const bucket = edges.get(ref.table);
|
|
2277
|
+
if (bucket === void 0) continue;
|
|
2278
|
+
if (bucket.some((e) => e.child === childName)) continue;
|
|
2279
|
+
bucket.push({ child: childName, column: col });
|
|
2477
2280
|
}
|
|
2478
2281
|
}
|
|
2479
2282
|
return edges;
|
|
@@ -2547,638 +2350,50 @@ export {};
|
|
|
2547
2350
|
`;
|
|
2548
2351
|
}
|
|
2549
2352
|
|
|
2550
|
-
// src/
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
var VARIANT_FITS = ["cover", "contain", "inside"];
|
|
2554
|
-
function normalizeVariants(input) {
|
|
2555
|
-
if (input === null || typeof input !== "object" || Array.isArray(input)) {
|
|
2556
|
-
throw new Error("bucket variants must be an object of { <name>: { width?, height?, format? } }");
|
|
2557
|
-
}
|
|
2558
|
-
const out = {};
|
|
2559
|
-
for (const [name, spec] of Object.entries(input)) {
|
|
2560
|
-
if (!/^[a-z0-9][a-z0-9_-]*$/i.test(name)) {
|
|
2561
|
-
throw new Error(
|
|
2562
|
-
`bucket variant name "${name}" must be alphanumeric with optional - or _ (e.g. "thumb", "card_2x")`
|
|
2563
|
-
);
|
|
2564
|
-
}
|
|
2565
|
-
if (spec === null || typeof spec !== "object" || Array.isArray(spec)) {
|
|
2566
|
-
throw new Error(`bucket variant "${name}" must be an object like { width: 100, height: 100 }`);
|
|
2567
|
-
}
|
|
2568
|
-
for (const dim of ["width", "height"]) {
|
|
2569
|
-
const value = spec[dim];
|
|
2570
|
-
if (value === void 0) continue;
|
|
2571
|
-
if (typeof value !== "number" || !Number.isInteger(value) || value <= 0) {
|
|
2572
|
-
throw new Error(`bucket variant "${name}" ${dim} must be a positive integer, got ${String(value)}`);
|
|
2573
|
-
}
|
|
2574
|
-
}
|
|
2575
|
-
if (spec.width === void 0 && spec.height === void 0) {
|
|
2576
|
-
throw new Error(`bucket variant "${name}" must set width and/or height`);
|
|
2577
|
-
}
|
|
2578
|
-
if (spec.format !== void 0 && !VARIANT_FORMATS.includes(spec.format)) {
|
|
2579
|
-
throw new Error(
|
|
2580
|
-
`bucket variant "${name}" format must be one of ${VARIANT_FORMATS.join(", ")}, got "${String(spec.format)}"`
|
|
2581
|
-
);
|
|
2582
|
-
}
|
|
2583
|
-
if (spec.fit !== void 0 && !VARIANT_FITS.includes(spec.fit)) {
|
|
2584
|
-
throw new Error(
|
|
2585
|
-
`bucket variant "${name}" fit must be one of ${VARIANT_FITS.join(", ")}, got "${String(spec.fit)}"`
|
|
2586
|
-
);
|
|
2587
|
-
}
|
|
2588
|
-
if (spec.quality !== void 0) {
|
|
2589
|
-
const q = spec.quality;
|
|
2590
|
-
if (typeof q !== "number" || !Number.isInteger(q) || q < 1 || q > 100) {
|
|
2591
|
-
throw new Error(`bucket variant "${name}" quality must be an integer 1\u2013100, got ${String(q)}`);
|
|
2592
|
-
}
|
|
2593
|
-
}
|
|
2594
|
-
if (spec.fit === "cover" && (spec.width === void 0 || spec.height === void 0)) {
|
|
2595
|
-
throw new Error(`bucket variant "${name}" uses fit "cover", which needs BOTH width and height`);
|
|
2596
|
-
}
|
|
2597
|
-
out[name] = {
|
|
2598
|
-
...spec.width !== void 0 ? { width: spec.width } : {},
|
|
2599
|
-
...spec.height !== void 0 ? { height: spec.height } : {},
|
|
2600
|
-
...spec.fit !== void 0 ? { fit: spec.fit } : {},
|
|
2601
|
-
...spec.format !== void 0 ? { format: spec.format } : {},
|
|
2602
|
-
...spec.quality !== void 0 ? { quality: spec.quality } : {}
|
|
2603
|
-
};
|
|
2604
|
-
}
|
|
2605
|
-
return out;
|
|
2353
|
+
// src/stack-gen.ts
|
|
2354
|
+
function liveNames(names) {
|
|
2355
|
+
return [...new Set(names)].sort();
|
|
2606
2356
|
}
|
|
2607
|
-
|
|
2608
|
-
|
|
2609
|
-
kb: 1024,
|
|
2610
|
-
mb: 1024 ** 2,
|
|
2611
|
-
gb: 1024 ** 3,
|
|
2612
|
-
tb: 1024 ** 4
|
|
2613
|
-
};
|
|
2614
|
-
function parseFileSizeLimit(input) {
|
|
2615
|
-
if (typeof input === "number") {
|
|
2616
|
-
if (!Number.isFinite(input) || input < 0 || !Number.isInteger(input)) {
|
|
2617
|
-
throw new Error(
|
|
2618
|
-
`bucket fileSizeLimit must be a non-negative integer number of bytes, got ${input}`
|
|
2619
|
-
);
|
|
2620
|
-
}
|
|
2621
|
-
return input;
|
|
2622
|
-
}
|
|
2623
|
-
const trimmed = input.trim();
|
|
2624
|
-
const match = /^(\d+(?:\.\d+)?)\s*([a-zA-Z]+)?$/.exec(trimmed);
|
|
2625
|
-
if (!match) {
|
|
2626
|
-
throw new Error(
|
|
2627
|
-
`bucket fileSizeLimit string must be "<number><unit>" like "5MB" or "1GB", got "${input}"`
|
|
2628
|
-
);
|
|
2629
|
-
}
|
|
2630
|
-
const value = Number.parseFloat(match[1]);
|
|
2631
|
-
const unit = (match[2] ?? "b").toLowerCase();
|
|
2632
|
-
const multiplier = UNIT_BYTES[unit];
|
|
2633
|
-
if (multiplier === void 0) {
|
|
2634
|
-
throw new Error(
|
|
2635
|
-
`bucket fileSizeLimit has an unknown unit "${match[2]}" \u2014 use B, KB, MB, GB, or TB (e.g. "5MB")`
|
|
2636
|
-
);
|
|
2637
|
-
}
|
|
2638
|
-
const bytes = value * multiplier;
|
|
2639
|
-
if (!Number.isFinite(bytes) || bytes < 0) {
|
|
2640
|
-
throw new Error(`bucket fileSizeLimit resolved to an invalid byte count: ${bytes}`);
|
|
2641
|
-
}
|
|
2642
|
-
return Math.round(bytes);
|
|
2357
|
+
function memberName(name) {
|
|
2358
|
+
return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(name) ? name : JSON.stringify(name);
|
|
2643
2359
|
}
|
|
2644
|
-
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
|
|
2648
|
-
|
|
2649
|
-
if (!Array.isArray(opts.allowedMimeTypes)) {
|
|
2650
|
-
throw new Error("bucket allowedMimeTypes must be an array of MIME-type strings");
|
|
2651
|
-
}
|
|
2652
|
-
for (const mime of opts.allowedMimeTypes) {
|
|
2653
|
-
if (typeof mime !== "string" || !MIME_RE.test(mime.trim())) {
|
|
2654
|
-
throw new Error(
|
|
2655
|
-
`bucket allowedMimeTypes entry "${mime}" is not a valid MIME type (expected "type/subtype", e.g. "image/png")`
|
|
2656
|
-
);
|
|
2657
|
-
}
|
|
2658
|
-
}
|
|
2659
|
-
allowedMimeTypes = [...new Set(opts.allowedMimeTypes.map((m) => m.trim()))];
|
|
2660
|
-
}
|
|
2661
|
-
return {
|
|
2662
|
-
public: opts.public ?? false,
|
|
2663
|
-
fileSizeLimit,
|
|
2664
|
-
allowedMimeTypes,
|
|
2665
|
-
variants: opts.variants === void 0 ? null : normalizeVariants(opts.variants)
|
|
2666
|
-
};
|
|
2667
|
-
}
|
|
2668
|
-
function defineStorage(input) {
|
|
2669
|
-
if (input === null || typeof input !== "object" || typeof input.buckets !== "object") {
|
|
2670
|
-
throw new Error("defineStorage expects { buckets: { <name>: bucket({...}) } }");
|
|
2671
|
-
}
|
|
2672
|
-
const buckets = {};
|
|
2673
|
-
for (const name of Object.keys(input.buckets)) {
|
|
2674
|
-
if (name.length === 0) {
|
|
2675
|
-
throw new Error("bucket name must be a non-empty string");
|
|
2676
|
-
}
|
|
2677
|
-
const def = input.buckets[name];
|
|
2678
|
-
if (def === void 0) continue;
|
|
2679
|
-
buckets[name] = def;
|
|
2680
|
-
}
|
|
2681
|
-
return { __config: STORAGE_CONFIG_KIND, buckets };
|
|
2360
|
+
function members(names) {
|
|
2361
|
+
if (names.length === 0) return "";
|
|
2362
|
+
return `
|
|
2363
|
+
${names.map((n) => ` ${memberName(n)}: true;`).join("\n")}
|
|
2364
|
+
`;
|
|
2682
2365
|
}
|
|
2683
|
-
|
|
2684
|
-
|
|
2685
|
-
|
|
2686
|
-
|
|
2687
|
-
|
|
2688
|
-
|
|
2689
|
-
|
|
2690
|
-
|
|
2691
|
-
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
|
|
2695
|
-
|
|
2696
|
-
|
|
2697
|
-
}
|
|
2698
|
-
hosts.push(h.trim().toLowerCase());
|
|
2699
|
-
}
|
|
2700
|
-
if (input.timeoutMs === void 0) {
|
|
2701
|
-
return { __config: EGRESS_CONFIG_KIND, hosts };
|
|
2702
|
-
}
|
|
2703
|
-
const timeoutMs = input.timeoutMs;
|
|
2704
|
-
if (typeof timeoutMs !== "number" || !Number.isInteger(timeoutMs)) {
|
|
2705
|
-
throw new Error("defineEgress: timeoutMs must be an integer number of milliseconds");
|
|
2706
|
-
}
|
|
2707
|
-
if (timeoutMs < EGRESS_TIMEOUT_MIN_MS || timeoutMs > EGRESS_TIMEOUT_MAX_MS) {
|
|
2708
|
-
throw new Error(
|
|
2709
|
-
`defineEgress: timeoutMs must be between ${EGRESS_TIMEOUT_MIN_MS} and ${EGRESS_TIMEOUT_MAX_MS} (got ${timeoutMs})`
|
|
2710
|
-
);
|
|
2711
|
-
}
|
|
2712
|
-
return { __config: EGRESS_CONFIG_KIND, hosts, timeoutMs };
|
|
2366
|
+
function bucketMembers(buckets) {
|
|
2367
|
+
const normalized = buckets.map((b) => typeof b === "string" ? { name: b, variants: [] } : b);
|
|
2368
|
+
const byName = /* @__PURE__ */ new Map();
|
|
2369
|
+
for (const b of normalized) byName.set(b.name, b.variants);
|
|
2370
|
+
const names = [...byName.keys()].sort();
|
|
2371
|
+
if (names.length === 0) return "";
|
|
2372
|
+
const rows = names.map((name) => {
|
|
2373
|
+
const variants = [...new Set(byName.get(name) ?? [])].sort();
|
|
2374
|
+
const union = variants.length === 0 ? "never" : variants.map((v) => JSON.stringify(v)).join(" | ");
|
|
2375
|
+
return ` ${memberName(name)}: { variants: ${union} };`;
|
|
2376
|
+
});
|
|
2377
|
+
return `
|
|
2378
|
+
${rows.join("\n")}
|
|
2379
|
+
`;
|
|
2713
2380
|
}
|
|
2381
|
+
function makeStackDts(names) {
|
|
2382
|
+
const secrets = liveNames(names.secrets);
|
|
2383
|
+
const flags = liveNames(names.flags);
|
|
2384
|
+
return `// palbase-stack.d.ts \u2014 GENERATED by @palbase/backend. Do not edit.
|
|
2385
|
+
// Source: the linked environment's stack.
|
|
2714
2386
|
|
|
2715
|
-
|
|
2716
|
-
|
|
2717
|
-
var RESERVED_SECRET_PREFIX = "PB_NOTIFICATIONS";
|
|
2718
|
-
var PROVIDER_CATALOG = {
|
|
2719
|
-
apns: {
|
|
2720
|
-
channel: "push",
|
|
2721
|
-
required: ["teamId", "keyId", "bundleId"],
|
|
2722
|
-
optional: ["isProduction"],
|
|
2723
|
-
secrets: ["p8"]
|
|
2724
|
-
},
|
|
2725
|
-
fcm: {
|
|
2726
|
-
channel: "push",
|
|
2727
|
-
required: [],
|
|
2728
|
-
optional: [],
|
|
2729
|
-
secrets: ["serviceAccount"]
|
|
2730
|
-
},
|
|
2731
|
-
sendgrid: {
|
|
2732
|
-
channel: "email",
|
|
2733
|
-
required: ["fromDomain"],
|
|
2734
|
-
optional: [],
|
|
2735
|
-
secrets: ["apiKey"]
|
|
2736
|
-
},
|
|
2737
|
-
ses: {
|
|
2738
|
-
channel: "email",
|
|
2739
|
-
required: ["region", "accessKeyId", "fromDomain"],
|
|
2740
|
-
optional: [],
|
|
2741
|
-
secrets: ["secretAccessKey"]
|
|
2742
|
-
},
|
|
2743
|
-
smtp: {
|
|
2744
|
-
channel: "email",
|
|
2745
|
-
required: ["host", "port", "fromEmail"],
|
|
2746
|
-
optional: ["username", "useStarttls"],
|
|
2747
|
-
secrets: ["password"]
|
|
2748
|
-
},
|
|
2749
|
-
acs: {
|
|
2750
|
-
channel: "email",
|
|
2751
|
-
required: ["fromEmail"],
|
|
2752
|
-
optional: ["fromName"],
|
|
2753
|
-
secrets: ["connectionString"]
|
|
2754
|
-
},
|
|
2755
|
-
twilio: {
|
|
2756
|
-
channel: "sms",
|
|
2757
|
-
// account_sid required; one of from_number / messaging_service_sid required
|
|
2758
|
-
// (enforced by the refine in buildProvider, not by the flat `required` list).
|
|
2759
|
-
required: ["accountSid"],
|
|
2760
|
-
optional: ["fromNumber", "messagingServiceSid"],
|
|
2761
|
-
secrets: ["authToken"]
|
|
2762
|
-
}
|
|
2763
|
-
};
|
|
2764
|
-
var DEFAULT_TEMPLATE_LOCALE = "en";
|
|
2765
|
-
function extractVariables(...sources) {
|
|
2766
|
-
const found = /* @__PURE__ */ new Set();
|
|
2767
|
-
for (const src of sources) {
|
|
2768
|
-
if (!src) continue;
|
|
2769
|
-
for (const match of src.matchAll(/\{\{\{?\s*([^{}]+?)\s*\}?\}\}/g)) {
|
|
2770
|
-
const token = (match[1] ?? "").trim();
|
|
2771
|
-
if (token === "" || "#/^!>&".includes(token.charAt(0)) || token === "else") continue;
|
|
2772
|
-
if (/\s/.test(token)) continue;
|
|
2773
|
-
const root = token.split(".")[0] ?? "";
|
|
2774
|
-
if (root === "" || root === "this" || root.startsWith("@")) continue;
|
|
2775
|
-
found.add(root);
|
|
2776
|
-
}
|
|
2777
|
-
}
|
|
2778
|
-
return [...found].sort();
|
|
2779
|
-
}
|
|
2780
|
-
function buildEmailTemplate(slug, opts) {
|
|
2781
|
-
const row = (locale, c) => {
|
|
2782
|
-
if (!c.subject) throw new Error(`notifications: email template "${slug}" (${locale}) needs a subject`);
|
|
2783
|
-
if (!c.html) throw new Error(`notifications: email template "${slug}" (${locale}) needs an html body`);
|
|
2784
|
-
const def = {
|
|
2785
|
-
slug,
|
|
2786
|
-
locale,
|
|
2787
|
-
subject: c.subject,
|
|
2788
|
-
html_body: c.html,
|
|
2789
|
-
variables: c.variables ? [...c.variables] : extractVariables(c.subject, c.html, c.text)
|
|
2790
|
-
};
|
|
2791
|
-
if (c.text !== void 0) def.text_body = c.text;
|
|
2792
|
-
return def;
|
|
2793
|
-
};
|
|
2794
|
-
const rows = [row(opts.locale ?? DEFAULT_TEMPLATE_LOCALE, opts)];
|
|
2795
|
-
for (const [locale, content] of Object.entries(opts.locales ?? {})) {
|
|
2796
|
-
rows.push(row(locale, content));
|
|
2797
|
-
}
|
|
2798
|
-
return rows;
|
|
2799
|
-
}
|
|
2800
|
-
function buildSmsTemplate(slug, opts) {
|
|
2801
|
-
const row = (locale, c) => {
|
|
2802
|
-
if (!c.body) throw new Error(`notifications: sms template "${slug}" (${locale}) needs a body`);
|
|
2803
|
-
return {
|
|
2804
|
-
slug,
|
|
2805
|
-
locale,
|
|
2806
|
-
body: c.body,
|
|
2807
|
-
variables: c.variables ? [...c.variables] : extractVariables(c.body)
|
|
2808
|
-
};
|
|
2809
|
-
};
|
|
2810
|
-
const rows = [row(opts.locale ?? DEFAULT_TEMPLATE_LOCALE, opts)];
|
|
2811
|
-
for (const [locale, content] of Object.entries(opts.locales ?? {})) {
|
|
2812
|
-
rows.push(row(locale, content));
|
|
2813
|
-
}
|
|
2814
|
-
return rows;
|
|
2815
|
-
}
|
|
2816
|
-
function buildTemplates(input) {
|
|
2817
|
-
const out = { email: [], sms: [] };
|
|
2818
|
-
const seen = /* @__PURE__ */ new Set();
|
|
2819
|
-
const claim = (channel, slug, locale) => {
|
|
2820
|
-
const key = `${channel}/${slug}/${locale}`;
|
|
2821
|
-
if (seen.has(key)) {
|
|
2822
|
-
throw new Error(`notifications: ${channel} template "${slug}" declares locale "${locale}" twice`);
|
|
2823
|
-
}
|
|
2824
|
-
seen.add(key);
|
|
2825
|
-
};
|
|
2826
|
-
for (const [slug, opts] of Object.entries(input.email ?? {})) {
|
|
2827
|
-
for (const row of buildEmailTemplate(slug, opts)) {
|
|
2828
|
-
claim("email", row.slug, row.locale);
|
|
2829
|
-
out.email.push(row);
|
|
2830
|
-
}
|
|
2831
|
-
}
|
|
2832
|
-
for (const [slug, opts] of Object.entries(input.sms ?? {})) {
|
|
2833
|
-
for (const row of buildSmsTemplate(slug, opts)) {
|
|
2834
|
-
claim("sms", row.slug, row.locale);
|
|
2835
|
-
out.sms.push(row);
|
|
2836
|
-
}
|
|
2837
|
-
}
|
|
2838
|
-
return out;
|
|
2839
|
-
}
|
|
2840
|
-
function buildProvider(name, opts) {
|
|
2841
|
-
const entry = PROVIDER_CATALOG[name];
|
|
2842
|
-
const src = { ...opts };
|
|
2843
|
-
const enabled = src.enabled === void 0 ? false : Boolean(src.enabled);
|
|
2844
|
-
if (!enabled) {
|
|
2845
|
-
return { enabled: false };
|
|
2846
|
-
}
|
|
2847
|
-
const def = { enabled: true };
|
|
2848
|
-
for (const field of entry.required) {
|
|
2849
|
-
const value = src[field];
|
|
2850
|
-
if (value === void 0 || value === null || value === "") {
|
|
2851
|
-
throw new Error(
|
|
2852
|
-
`notifications: provider "${name}" is enabled but missing required field "${field}"`
|
|
2853
|
-
);
|
|
2854
|
-
}
|
|
2855
|
-
def[field] = value;
|
|
2856
|
-
}
|
|
2857
|
-
for (const field of entry.optional) {
|
|
2858
|
-
if (src[field] !== void 0) {
|
|
2859
|
-
def[field] = src[field];
|
|
2860
|
-
}
|
|
2861
|
-
}
|
|
2862
|
-
if (name === "twilio") {
|
|
2863
|
-
const hasFrom = Boolean(def.fromNumber);
|
|
2864
|
-
const hasMsg = Boolean(def.messagingServiceSid);
|
|
2865
|
-
if (!hasFrom && !hasMsg) {
|
|
2866
|
-
throw new Error(
|
|
2867
|
-
'notifications: provider "twilio" requires one of "fromNumber" or "messagingServiceSid"'
|
|
2868
|
-
);
|
|
2869
|
-
}
|
|
2870
|
-
}
|
|
2871
|
-
return def;
|
|
2872
|
-
}
|
|
2873
|
-
function defineNotifications(input) {
|
|
2874
|
-
if (input === null || typeof input !== "object") {
|
|
2875
|
-
throw new Error("defineNotifications expects { push?, email?, sms? }");
|
|
2876
|
-
}
|
|
2877
|
-
const config = {
|
|
2878
|
-
__config: NOTIFICATIONS_CONFIG_KIND,
|
|
2879
|
-
push: {},
|
|
2880
|
-
email: {},
|
|
2881
|
-
sms: {},
|
|
2882
|
-
templates: buildTemplates(input.templates ?? {})
|
|
2883
|
-
};
|
|
2884
|
-
const push = input.push ?? {};
|
|
2885
|
-
if (push.apns !== void 0) config.push.apns = buildProvider("apns", push.apns);
|
|
2886
|
-
if (push.fcm !== void 0) config.push.fcm = buildProvider("fcm", push.fcm);
|
|
2887
|
-
const email = input.email ?? {};
|
|
2888
|
-
if (email.sendgrid !== void 0) config.email.sendgrid = buildProvider("sendgrid", email.sendgrid);
|
|
2889
|
-
if (email.ses !== void 0) config.email.ses = buildProvider("ses", email.ses);
|
|
2890
|
-
if (email.smtp !== void 0) config.email.smtp = buildProvider("smtp", email.smtp);
|
|
2891
|
-
if (email.acs !== void 0) config.email.acs = buildProvider("acs", email.acs);
|
|
2892
|
-
const sms = input.sms ?? {};
|
|
2893
|
-
if (sms.twilio !== void 0) config.sms.twilio = buildProvider("twilio", sms.twilio);
|
|
2894
|
-
return config;
|
|
2895
|
-
}
|
|
2896
|
-
function reservedSecretKey(provider, secretField) {
|
|
2897
|
-
return `${RESERVED_SECRET_PREFIX}_${camelToUpperSnake(provider)}_${camelToUpperSnake(secretField)}`;
|
|
2898
|
-
}
|
|
2899
|
-
function camelToUpperSnake(s) {
|
|
2900
|
-
return s.replace(/([a-z0-9])([A-Z])/g, "$1_$2").toUpperCase();
|
|
2901
|
-
}
|
|
2387
|
+
declare module "@palbase/backend/stack" {
|
|
2388
|
+
interface Secrets {${members(secrets)}}
|
|
2902
2389
|
|
|
2903
|
-
|
|
2904
|
-
var TEST_USERS_CONFIG_KIND = "test-users";
|
|
2905
|
-
var TEST_USER_NAME_RE = /^[a-zA-Z][a-zA-Z0-9_]*$/;
|
|
2906
|
-
var EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
2907
|
-
var E164_RE = /^\+[1-9]\d{1,14}$/;
|
|
2908
|
-
var OTP_LENGTH = 6;
|
|
2909
|
-
var OTP_RE = /^\d{6}$/;
|
|
2910
|
-
var MIN_PASSWORD_LENGTH = 8;
|
|
2911
|
-
function isPlainObject2(value) {
|
|
2912
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2913
|
-
}
|
|
2914
|
-
function normalizeSeed(seed, userName) {
|
|
2915
|
-
if (seed === void 0) return {};
|
|
2916
|
-
if (!isPlainObject2(seed)) {
|
|
2917
|
-
throw new Error(`testUser ${JSON.stringify(userName)}: seed must be { <table>: [ {...} ] }`);
|
|
2918
|
-
}
|
|
2919
|
-
const out = {};
|
|
2920
|
-
for (const [table, rows] of Object.entries(seed)) {
|
|
2921
|
-
if (!Array.isArray(rows)) {
|
|
2922
|
-
throw new Error(
|
|
2923
|
-
`testUser ${JSON.stringify(userName)}: seed.${table} must be an array of rows`
|
|
2924
|
-
);
|
|
2925
|
-
}
|
|
2926
|
-
if (rows.length === 0) {
|
|
2927
|
-
throw new Error(
|
|
2928
|
-
`testUser ${JSON.stringify(userName)}: seed.${table} is empty \u2014 remove it or add a row`
|
|
2929
|
-
);
|
|
2930
|
-
}
|
|
2931
|
-
for (const row of rows) {
|
|
2932
|
-
if (!isPlainObject2(row)) {
|
|
2933
|
-
throw new Error(
|
|
2934
|
-
`testUser ${JSON.stringify(userName)}: every row in seed.${table} must be an object`
|
|
2935
|
-
);
|
|
2936
|
-
}
|
|
2937
|
-
}
|
|
2938
|
-
out[table] = rows;
|
|
2939
|
-
}
|
|
2940
|
-
return out;
|
|
2941
|
-
}
|
|
2942
|
-
function testUser(opts = {}) {
|
|
2943
|
-
if (!isPlainObject2(opts)) {
|
|
2944
|
-
throw new Error("testUser() expects { email?, password?, phone?, otp?, seed? }");
|
|
2945
|
-
}
|
|
2946
|
-
const hasEmail = opts.email !== void 0;
|
|
2947
|
-
const hasPassword = opts.password !== void 0;
|
|
2948
|
-
if (hasEmail !== hasPassword) {
|
|
2949
|
-
throw new Error(
|
|
2950
|
-
"testUser() needs email AND password together (a fixture with stable credentials) or neither (a template the server generates credentials for)"
|
|
2951
|
-
);
|
|
2952
|
-
}
|
|
2953
|
-
let email = null;
|
|
2954
|
-
let password = null;
|
|
2955
|
-
if (hasEmail) {
|
|
2956
|
-
if (typeof opts.email !== "string" || !EMAIL_RE.test(opts.email)) {
|
|
2957
|
-
throw new Error(`testUser email ${JSON.stringify(opts.email)} is not a valid address`);
|
|
2958
|
-
}
|
|
2959
|
-
if (typeof opts.password !== "string" || opts.password.length < MIN_PASSWORD_LENGTH) {
|
|
2960
|
-
throw new Error(
|
|
2961
|
-
`testUser password must be at least ${MIN_PASSWORD_LENGTH} characters (got ${typeof opts.password === "string" ? opts.password.length : 0})`
|
|
2962
|
-
);
|
|
2963
|
-
}
|
|
2964
|
-
email = opts.email;
|
|
2965
|
-
password = opts.password;
|
|
2966
|
-
}
|
|
2967
|
-
const hasPhone = opts.phone !== void 0;
|
|
2968
|
-
const hasOTP = opts.otp !== void 0;
|
|
2969
|
-
if (hasPhone !== hasOTP) {
|
|
2970
|
-
throw new Error(
|
|
2971
|
-
"testUser() needs phone AND otp together \u2014 nothing can receive an SMS at a fabricated number, so a phone fixture without its fixed code can never be signed in as"
|
|
2972
|
-
);
|
|
2973
|
-
}
|
|
2974
|
-
let phone = null;
|
|
2975
|
-
let otp = null;
|
|
2976
|
-
if (hasPhone) {
|
|
2977
|
-
if (typeof opts.phone !== "string" || !E164_RE.test(opts.phone)) {
|
|
2978
|
-
throw new Error(
|
|
2979
|
-
`testUser phone ${JSON.stringify(opts.phone)} must be E.164, e.g. "+905551112233"`
|
|
2980
|
-
);
|
|
2981
|
-
}
|
|
2982
|
-
if (typeof opts.otp !== "string" || !OTP_RE.test(opts.otp)) {
|
|
2983
|
-
throw new Error(
|
|
2984
|
-
`testUser otp ${JSON.stringify(opts.otp)} must be ${OTP_LENGTH} digits`
|
|
2985
|
-
);
|
|
2986
|
-
}
|
|
2987
|
-
phone = opts.phone;
|
|
2988
|
-
otp = opts.otp;
|
|
2989
|
-
}
|
|
2990
|
-
return { email, password, phone, otp, seed: normalizeSeed(opts.seed, email ?? "<template>") };
|
|
2991
|
-
}
|
|
2992
|
-
function defineTestUsers(input) {
|
|
2993
|
-
if (!isPlainObject2(input) || !isPlainObject2(input.users)) {
|
|
2994
|
-
throw new Error("defineTestUsers expects { users: { <name>: testUser({...}) } }");
|
|
2995
|
-
}
|
|
2996
|
-
const users = {};
|
|
2997
|
-
const emails = /* @__PURE__ */ new Map();
|
|
2998
|
-
for (const name of Object.keys(input.users)) {
|
|
2999
|
-
if (!TEST_USER_NAME_RE.test(name)) {
|
|
3000
|
-
throw new Error(
|
|
3001
|
-
`test user name ${JSON.stringify(name)} is invalid \u2014 must start with a letter and contain only letters, digits, and underscores`
|
|
3002
|
-
);
|
|
3003
|
-
}
|
|
3004
|
-
const def = input.users[name];
|
|
3005
|
-
if (def === void 0) continue;
|
|
3006
|
-
if (def.email !== null) {
|
|
3007
|
-
const lower = def.email.toLowerCase();
|
|
3008
|
-
const already = emails.get(lower);
|
|
3009
|
-
if (already !== void 0) {
|
|
3010
|
-
throw new Error(
|
|
3011
|
-
`test users ${JSON.stringify(already)} and ${JSON.stringify(name)} declare the same e-mail ${JSON.stringify(def.email)}`
|
|
3012
|
-
);
|
|
3013
|
-
}
|
|
3014
|
-
emails.set(lower, name);
|
|
3015
|
-
}
|
|
3016
|
-
users[name] = def;
|
|
3017
|
-
}
|
|
3018
|
-
return { __config: TEST_USERS_CONFIG_KIND, users };
|
|
3019
|
-
}
|
|
2390
|
+
interface Flags {${members(flags)}}
|
|
3020
2391
|
|
|
3021
|
-
|
|
3022
|
-
var FLAGS_CONFIG_KIND = "flags";
|
|
3023
|
-
var FLAG_KEY_RE = /^[a-zA-Z][a-zA-Z0-9_]*$/;
|
|
3024
|
-
function valueMatchesType(type, value) {
|
|
3025
|
-
switch (type) {
|
|
3026
|
-
case "boolean":
|
|
3027
|
-
return typeof value === "boolean";
|
|
3028
|
-
case "number":
|
|
3029
|
-
return typeof value === "number" && Number.isFinite(value);
|
|
3030
|
-
case "string":
|
|
3031
|
-
return typeof value === "string";
|
|
3032
|
-
case "json":
|
|
3033
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3034
|
-
}
|
|
3035
|
-
}
|
|
3036
|
-
function flag(opts) {
|
|
3037
|
-
if (opts === null || typeof opts !== "object") {
|
|
3038
|
-
throw new Error("flag() expects { type, default, variants?, description? }");
|
|
3039
|
-
}
|
|
3040
|
-
const { type } = opts;
|
|
3041
|
-
if (type !== "boolean" && type !== "number" && type !== "string" && type !== "json") {
|
|
3042
|
-
throw new Error(`flag type must be "boolean", "number", "string", or "json", got ${JSON.stringify(type)}`);
|
|
3043
|
-
}
|
|
3044
|
-
if (!valueMatchesType(type, opts.default)) {
|
|
3045
|
-
throw new Error(
|
|
3046
|
-
`flag default ${JSON.stringify(opts.default)} does not match type "${type}"`
|
|
3047
|
-
);
|
|
3048
|
-
}
|
|
3049
|
-
let variants = null;
|
|
3050
|
-
if (opts.variants !== void 0) {
|
|
3051
|
-
if (opts.type !== "string") {
|
|
3052
|
-
throw new Error(`flag variants are only valid for type "string" (got type "${type}")`);
|
|
3053
|
-
}
|
|
3054
|
-
if (!Array.isArray(opts.variants)) {
|
|
3055
|
-
throw new Error("flag variants must be an array of strings");
|
|
3056
|
-
}
|
|
3057
|
-
for (const v of opts.variants) {
|
|
3058
|
-
if (typeof v !== "string" || v.length === 0) {
|
|
3059
|
-
throw new Error(`flag variant ${JSON.stringify(v)} must be a non-empty string`);
|
|
3060
|
-
}
|
|
3061
|
-
}
|
|
3062
|
-
variants = [...new Set(opts.variants)];
|
|
3063
|
-
if (variants.length === 0) {
|
|
3064
|
-
throw new Error("flag variants must be a non-empty list when supplied");
|
|
3065
|
-
}
|
|
3066
|
-
if (!variants.includes(opts.default)) {
|
|
3067
|
-
throw new Error(
|
|
3068
|
-
`flag default ${JSON.stringify(opts.default)} is not one of its variants [${variants.map((v) => JSON.stringify(v)).join(", ")}]`
|
|
3069
|
-
);
|
|
3070
|
-
}
|
|
3071
|
-
}
|
|
3072
|
-
let conditions = null;
|
|
3073
|
-
if (opts.conditions !== void 0) {
|
|
3074
|
-
if (!Array.isArray(opts.conditions)) {
|
|
3075
|
-
throw new Error("flag conditions must be an array of { when, value }");
|
|
3076
|
-
}
|
|
3077
|
-
const compiled = [];
|
|
3078
|
-
for (const [i, branch] of opts.conditions.entries()) {
|
|
3079
|
-
if (branch === null || typeof branch !== "object") {
|
|
3080
|
-
throw new Error(`flag condition ${i} must be an object { when, value }`);
|
|
3081
|
-
}
|
|
3082
|
-
if (typeof branch.when !== "string" || branch.when.trim().length === 0) {
|
|
3083
|
-
throw new Error(`flag condition ${i} needs a non-empty "when" expression`);
|
|
3084
|
-
}
|
|
3085
|
-
if (!valueMatchesType(type, branch.value)) {
|
|
3086
|
-
throw new Error(
|
|
3087
|
-
`flag condition ${i} value ${JSON.stringify(branch.value)} does not match type "${type}"`
|
|
3088
|
-
);
|
|
3089
|
-
}
|
|
3090
|
-
if (variants !== null && !variants.includes(branch.value)) {
|
|
3091
|
-
throw new Error(
|
|
3092
|
-
`flag condition ${i} value ${JSON.stringify(branch.value)} is not one of its variants [${variants.map((v) => JSON.stringify(v)).join(", ")}]`
|
|
3093
|
-
);
|
|
3094
|
-
}
|
|
3095
|
-
compiled.push({ when: branch.when.trim(), value: branch.value });
|
|
3096
|
-
}
|
|
3097
|
-
conditions = compiled.length > 0 ? compiled : null;
|
|
3098
|
-
}
|
|
3099
|
-
let description = null;
|
|
3100
|
-
if (opts.description !== void 0) {
|
|
3101
|
-
if (typeof opts.description !== "string") {
|
|
3102
|
-
throw new Error("flag description must be a string");
|
|
3103
|
-
}
|
|
3104
|
-
const trimmed = opts.description.trim();
|
|
3105
|
-
description = trimmed.length > 0 ? trimmed : null;
|
|
3106
|
-
}
|
|
3107
|
-
return {
|
|
3108
|
-
type,
|
|
3109
|
-
default: opts.default,
|
|
3110
|
-
variants,
|
|
3111
|
-
conditions,
|
|
3112
|
-
description
|
|
3113
|
-
};
|
|
3114
|
-
}
|
|
3115
|
-
function defineFlags(input) {
|
|
3116
|
-
if (input === null || typeof input !== "object" || typeof input.flags !== "object") {
|
|
3117
|
-
throw new Error("defineFlags expects { flags: { <key>: flag({...}) } }");
|
|
3118
|
-
}
|
|
3119
|
-
const flags = {};
|
|
3120
|
-
for (const key of Object.keys(input.flags)) {
|
|
3121
|
-
if (!FLAG_KEY_RE.test(key)) {
|
|
3122
|
-
throw new Error(
|
|
3123
|
-
`flag key ${JSON.stringify(key)} is invalid \u2014 must start with a letter and contain only letters, digits, and underscores`
|
|
3124
|
-
);
|
|
3125
|
-
}
|
|
3126
|
-
const def = input.flags[key];
|
|
3127
|
-
if (def === void 0) continue;
|
|
3128
|
-
flags[key] = def;
|
|
3129
|
-
}
|
|
3130
|
-
return { __config: FLAGS_CONFIG_KIND, flags };
|
|
2392
|
+
interface Buckets {${bucketMembers(names.buckets)}}
|
|
3131
2393
|
}
|
|
3132
2394
|
|
|
3133
|
-
|
|
3134
|
-
|
|
3135
|
-
var SECRET_NAME_RE = /^[A-Z][A-Z0-9_]*$/;
|
|
3136
|
-
function secret(name, opts = {}) {
|
|
3137
|
-
if (typeof name !== "string" || !SECRET_NAME_RE.test(name)) {
|
|
3138
|
-
throw new Error(
|
|
3139
|
-
`secret name ${JSON.stringify(name)} is invalid \u2014 must be an environment-variable name: an uppercase letter, then uppercase letters, digits, and underscores (e.g. "NEO4J_PASSWORD")`
|
|
3140
|
-
);
|
|
3141
|
-
}
|
|
3142
|
-
if (opts === null || typeof opts !== "object") {
|
|
3143
|
-
throw new Error(`secret("${name}") expects { required?, description? }`);
|
|
3144
|
-
}
|
|
3145
|
-
let required = true;
|
|
3146
|
-
if (opts.required !== void 0) {
|
|
3147
|
-
if (typeof opts.required !== "boolean") {
|
|
3148
|
-
throw new Error(`secret("${name}") required must be a boolean`);
|
|
3149
|
-
}
|
|
3150
|
-
required = opts.required;
|
|
3151
|
-
}
|
|
3152
|
-
let description = null;
|
|
3153
|
-
if (opts.description !== void 0) {
|
|
3154
|
-
if (typeof opts.description !== "string") {
|
|
3155
|
-
throw new Error(`secret("${name}") description must be a string`);
|
|
3156
|
-
}
|
|
3157
|
-
const trimmed = opts.description.trim();
|
|
3158
|
-
description = trimmed.length > 0 ? trimmed : null;
|
|
3159
|
-
}
|
|
3160
|
-
return { name, required, description };
|
|
3161
|
-
}
|
|
3162
|
-
function defineSecrets(input) {
|
|
3163
|
-
if (input === null || typeof input !== "object" || !Array.isArray(input.secrets)) {
|
|
3164
|
-
throw new Error('defineSecrets expects { secrets: [secret("NAME"), ...] }');
|
|
3165
|
-
}
|
|
3166
|
-
const byName = /* @__PURE__ */ new Map();
|
|
3167
|
-
for (const def of input.secrets) {
|
|
3168
|
-
if (def === null || typeof def !== "object" || typeof def.name !== "string" || typeof def.required !== "boolean") {
|
|
3169
|
-
throw new Error(
|
|
3170
|
-
`defineSecrets: every entry must come from secret() \u2014 got ${JSON.stringify(def)}`
|
|
3171
|
-
);
|
|
3172
|
-
}
|
|
3173
|
-
if (byName.has(def.name)) {
|
|
3174
|
-
throw new Error(
|
|
3175
|
-
`secret "${def.name}" is declared twice in config/secrets.ts \u2014 remove one; a name has exactly one requirement`
|
|
3176
|
-
);
|
|
3177
|
-
}
|
|
3178
|
-
byName.set(def.name, def);
|
|
3179
|
-
}
|
|
3180
|
-
const secrets = [...byName.values()].sort((a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0);
|
|
3181
|
-
return { __config: SECRETS_CONFIG_KIND, secrets };
|
|
2395
|
+
export {};
|
|
2396
|
+
`;
|
|
3182
2397
|
}
|
|
3183
2398
|
|
|
3184
2399
|
// src/decorators/registry.ts
|
|
@@ -3186,7 +2401,7 @@ var ROUTES = /* @__PURE__ */ Symbol.for("palbase.backend.routes");
|
|
|
3186
2401
|
var PARAM_BUFFER = /* @__PURE__ */ Symbol.for("palbase.backend.paramBuffer");
|
|
3187
2402
|
var RETURN_BUFFER = /* @__PURE__ */ Symbol.for("palbase.backend.returnBuffer");
|
|
3188
2403
|
var THROWS_BUFFER = /* @__PURE__ */ Symbol.for("palbase.backend.throwsBuffer");
|
|
3189
|
-
function
|
|
2404
|
+
function carrierOf(target) {
|
|
3190
2405
|
const ctor = typeof target === "function" ? target : target.constructor ?? target;
|
|
3191
2406
|
return ctor;
|
|
3192
2407
|
}
|
|
@@ -3203,7 +2418,7 @@ function ownParamBuffer(carrier) {
|
|
|
3203
2418
|
return carrier[PARAM_BUFFER];
|
|
3204
2419
|
}
|
|
3205
2420
|
function recordRoute(target, fnName, method, subpath, options) {
|
|
3206
|
-
const carrier =
|
|
2421
|
+
const carrier = carrierOf(target);
|
|
3207
2422
|
const routes = ownRoutes(carrier);
|
|
3208
2423
|
const buffer = ownParamBuffer(carrier);
|
|
3209
2424
|
const params = (buffer[fnName] ?? []).slice().sort((a, b) => a.index - b.index);
|
|
@@ -3219,7 +2434,7 @@ function recordRoute(target, fnName, method, subpath, options) {
|
|
|
3219
2434
|
routes.push(route);
|
|
3220
2435
|
}
|
|
3221
2436
|
function recordParam(target, fnName, meta) {
|
|
3222
|
-
const carrier =
|
|
2437
|
+
const carrier = carrierOf(target);
|
|
3223
2438
|
const buffer = ownParamBuffer(carrier);
|
|
3224
2439
|
(buffer[fnName] ??= []).push(meta);
|
|
3225
2440
|
const routes = carrier[ROUTES];
|
|
@@ -3232,7 +2447,7 @@ function recordParam(target, fnName, meta) {
|
|
|
3232
2447
|
}
|
|
3233
2448
|
}
|
|
3234
2449
|
function recordThrows(target, fnName, throws) {
|
|
3235
|
-
const carrier =
|
|
2450
|
+
const carrier = carrierOf(target);
|
|
3236
2451
|
const routes = carrier[ROUTES];
|
|
3237
2452
|
const route = routes?.find((r) => r.fnName === fnName);
|
|
3238
2453
|
if (route) {
|
|
@@ -3246,7 +2461,7 @@ function recordThrows(target, fnName, throws) {
|
|
|
3246
2461
|
if (throwsBuffer) throwsBuffer[fnName] = throws;
|
|
3247
2462
|
}
|
|
3248
2463
|
function getRoutes(ctor) {
|
|
3249
|
-
const carrier =
|
|
2464
|
+
const carrier = carrierOf(ctor);
|
|
3250
2465
|
const routes = carrier[ROUTES] ?? [];
|
|
3251
2466
|
const returnBuffer = carrier[RETURN_BUFFER];
|
|
3252
2467
|
if (returnBuffer) {
|
|
@@ -3355,12 +2570,12 @@ var Query = makeMethodDecorator("QUERY");
|
|
|
3355
2570
|
// src/decorators/webhook.ts
|
|
3356
2571
|
var WEBHOOK_META = /* @__PURE__ */ Symbol.for("palbase.backend.webhookMeta");
|
|
3357
2572
|
var WEBHOOK_EVENTS = /* @__PURE__ */ Symbol.for("palbase.backend.webhookEvents");
|
|
3358
|
-
function
|
|
2573
|
+
function carrierOf2(ctor) {
|
|
3359
2574
|
return ctor;
|
|
3360
2575
|
}
|
|
3361
2576
|
function Webhook(options) {
|
|
3362
2577
|
return function(ctor) {
|
|
3363
|
-
const carrier =
|
|
2578
|
+
const carrier = carrierOf2(ctor);
|
|
3364
2579
|
Object.defineProperty(carrier, WEBHOOK_META, {
|
|
3365
2580
|
value: options,
|
|
3366
2581
|
enumerable: false,
|
|
@@ -3378,7 +2593,7 @@ function Webhook(options) {
|
|
|
3378
2593
|
}
|
|
3379
2594
|
function On(event) {
|
|
3380
2595
|
return function(target, fnName) {
|
|
3381
|
-
const carrier =
|
|
2596
|
+
const carrier = carrierOf2(target.constructor);
|
|
3382
2597
|
const existing = carrier[WEBHOOK_EVENTS];
|
|
3383
2598
|
const entries = existing ? [...existing] : [];
|
|
3384
2599
|
entries.push({ event, fnName: String(fnName) });
|
|
@@ -3391,7 +2606,7 @@ function On(event) {
|
|
|
3391
2606
|
};
|
|
3392
2607
|
}
|
|
3393
2608
|
function getWebhookConfig(ctor) {
|
|
3394
|
-
const carrier =
|
|
2609
|
+
const carrier = carrierOf2(ctor);
|
|
3395
2610
|
const meta = carrier[WEBHOOK_META];
|
|
3396
2611
|
const entries = carrier[WEBHOOK_EVENTS] ?? [];
|
|
3397
2612
|
if (!meta) {
|
|
@@ -3526,22 +2741,14 @@ function validateUploadConfigShape(c) {
|
|
|
3526
2741
|
if (c === null || typeof c !== "object") {
|
|
3527
2742
|
throw new Error("@Upload config must be an object { bucket, pathTemplate, ... }");
|
|
3528
2743
|
}
|
|
3529
|
-
|
|
2744
|
+
const bucket = c.bucket;
|
|
2745
|
+
if (typeof bucket !== "string" || bucket.length === 0) {
|
|
3530
2746
|
throw new Error("@Upload config.bucket must be a non-empty bucket name");
|
|
3531
2747
|
}
|
|
3532
2748
|
if (typeof c.pathTemplate !== "string" || c.pathTemplate.length === 0) {
|
|
3533
2749
|
throw new Error("@Upload config.pathTemplate must be a non-empty key template");
|
|
3534
2750
|
}
|
|
3535
2751
|
}
|
|
3536
|
-
function validateUploadAgainstStorage(uploadConfig, storage, routeLabel) {
|
|
3537
|
-
const def = storage.buckets[uploadConfig.bucket];
|
|
3538
|
-
if (def === void 0) {
|
|
3539
|
-
const known = Object.keys(storage.buckets);
|
|
3540
|
-
throw new Error(
|
|
3541
|
-
`@Upload route ${routeLabel} targets bucket "${uploadConfig.bucket}" which is not declared in defineStorage(...). ` + (known.length ? `Known buckets: ${known.join(", ")}.` : "No buckets are declared.")
|
|
3542
|
-
);
|
|
3543
|
-
}
|
|
3544
|
-
}
|
|
3545
2752
|
|
|
3546
2753
|
// src/decorators/params.ts
|
|
3547
2754
|
function makeParamDecorator(kind, extra) {
|
|
@@ -3590,6 +2797,93 @@ function defineMiddleware(fn) {
|
|
|
3590
2797
|
return fn;
|
|
3591
2798
|
}
|
|
3592
2799
|
|
|
2800
|
+
// src/errors.ts
|
|
2801
|
+
var HTTP_ERROR_BRAND = /* @__PURE__ */ Symbol.for("palbase.backend.httpError");
|
|
2802
|
+
var HttpError = class extends Error {
|
|
2803
|
+
status;
|
|
2804
|
+
error;
|
|
2805
|
+
errorDescription;
|
|
2806
|
+
data;
|
|
2807
|
+
/** See {@link HTTP_ERROR_BRAND} — how the engine recognises this across SDK copies. */
|
|
2808
|
+
[HTTP_ERROR_BRAND] = true;
|
|
2809
|
+
constructor(status, error, errorDescription, data) {
|
|
2810
|
+
super(errorDescription);
|
|
2811
|
+
this.name = "HttpError";
|
|
2812
|
+
this.status = status;
|
|
2813
|
+
this.error = error;
|
|
2814
|
+
this.errorDescription = errorDescription;
|
|
2815
|
+
if (data !== void 0) {
|
|
2816
|
+
this.data = data;
|
|
2817
|
+
}
|
|
2818
|
+
}
|
|
2819
|
+
/**
|
|
2820
|
+
* Serialize to the standard Palbase error response format.
|
|
2821
|
+
* The `requestId` is injected by the runtime layer from the request context.
|
|
2822
|
+
* When called without arguments (e.g. JSON.stringify), request_id is omitted.
|
|
2823
|
+
* When `data` is set, it is appended as a strict-superset field.
|
|
2824
|
+
*/
|
|
2825
|
+
toJSON(requestId) {
|
|
2826
|
+
const result = {
|
|
2827
|
+
error: this.error,
|
|
2828
|
+
error_description: this.errorDescription,
|
|
2829
|
+
status: this.status
|
|
2830
|
+
};
|
|
2831
|
+
if (requestId) {
|
|
2832
|
+
result.request_id = requestId;
|
|
2833
|
+
}
|
|
2834
|
+
if (this.data !== void 0) {
|
|
2835
|
+
result.data = this.data;
|
|
2836
|
+
}
|
|
2837
|
+
return result;
|
|
2838
|
+
}
|
|
2839
|
+
};
|
|
2840
|
+
var PalError = class extends HttpError {
|
|
2841
|
+
constructor(status, code, description, data) {
|
|
2842
|
+
super(status, code, description, data);
|
|
2843
|
+
this.name = "PalError";
|
|
2844
|
+
}
|
|
2845
|
+
};
|
|
2846
|
+
var NamedHttpError = class extends HttpError {
|
|
2847
|
+
constructor(status, defaultCode, name, message, code, data) {
|
|
2848
|
+
super(status, code ?? defaultCode, message ?? defaultMessage(name), data);
|
|
2849
|
+
this.name = name;
|
|
2850
|
+
}
|
|
2851
|
+
};
|
|
2852
|
+
function defaultMessage(name) {
|
|
2853
|
+
const spaced = name.replace(/([a-z0-9])([A-Z])/g, "$1 $2");
|
|
2854
|
+
return spaced.charAt(0).toUpperCase() + spaced.slice(1).toLowerCase();
|
|
2855
|
+
}
|
|
2856
|
+
var BadRequest = class extends NamedHttpError {
|
|
2857
|
+
constructor(data, message) {
|
|
2858
|
+
super(400, "bad_request", "BadRequest", message, void 0, data);
|
|
2859
|
+
}
|
|
2860
|
+
};
|
|
2861
|
+
var Unauthorized = class extends NamedHttpError {
|
|
2862
|
+
constructor(message, code, data) {
|
|
2863
|
+
super(401, "unauthorized", "Unauthorized", message, code, data);
|
|
2864
|
+
}
|
|
2865
|
+
};
|
|
2866
|
+
var Forbidden = class extends NamedHttpError {
|
|
2867
|
+
constructor(message, code, data) {
|
|
2868
|
+
super(403, "forbidden", "Forbidden", message, code, data);
|
|
2869
|
+
}
|
|
2870
|
+
};
|
|
2871
|
+
var NotFound = class extends NamedHttpError {
|
|
2872
|
+
constructor(message, code, data) {
|
|
2873
|
+
super(404, "not_found", "NotFound", message, code, data);
|
|
2874
|
+
}
|
|
2875
|
+
};
|
|
2876
|
+
var Conflict = class extends NamedHttpError {
|
|
2877
|
+
constructor(message, code, data) {
|
|
2878
|
+
super(409, "conflict", "Conflict", message, code, data);
|
|
2879
|
+
}
|
|
2880
|
+
};
|
|
2881
|
+
var TooManyRequests = class extends NamedHttpError {
|
|
2882
|
+
constructor(data, message) {
|
|
2883
|
+
super(429, "too_many_requests", "TooManyRequests", message, void 0, data);
|
|
2884
|
+
}
|
|
2885
|
+
};
|
|
2886
|
+
|
|
3593
2887
|
// src/error-registry.ts
|
|
3594
2888
|
var import_zod_to_openapi = require("@asteasolutions/zod-to-openapi");
|
|
3595
2889
|
var import_zod = require("zod");
|
|
@@ -3599,27 +2893,7 @@ var BUILTIN_DATA_SCHEMAS = {
|
|
|
3599
2893
|
bad_request: import_zod.z.object({
|
|
3600
2894
|
fields: import_zod.z.array(import_zod.z.object({ field: import_zod.z.string(), message: import_zod.z.string() }))
|
|
3601
2895
|
}),
|
|
3602
|
-
too_many_requests: import_zod.z.object({ retryAfter: import_zod.z.number().int() })
|
|
3603
|
-
// Purchases (@RequireEntitlement / @Spend). Their payloads are what make a
|
|
3604
|
-
// paywall renderable on the client: WHICH entitlement was missing, and for a
|
|
3605
|
-
// spend the real ceiling and reset time rather than a guessed retry delay.
|
|
3606
|
-
entitlement_required: import_zod.z.object({ entitlement: import_zod.z.string().nullable() }),
|
|
3607
|
-
quota_exceeded: import_zod.z.object({
|
|
3608
|
-
key: import_zod.z.string(),
|
|
3609
|
-
scope: import_zod.z.string(),
|
|
3610
|
-
window: import_zod.z.string(),
|
|
3611
|
-
used: import_zod.z.number().int(),
|
|
3612
|
-
reserved: import_zod.z.number().int(),
|
|
3613
|
-
max: import_zod.z.number().int(),
|
|
3614
|
-
remaining: import_zod.z.number().int(),
|
|
3615
|
-
resetAt: import_zod.z.string()
|
|
3616
|
-
}),
|
|
3617
|
-
credit_insufficient: import_zod.z.object({
|
|
3618
|
-
key: import_zod.z.string(),
|
|
3619
|
-
balance: import_zod.z.number().int(),
|
|
3620
|
-
reserved: import_zod.z.number().int(),
|
|
3621
|
-
remaining: import_zod.z.number().int()
|
|
3622
|
-
})
|
|
2896
|
+
too_many_requests: import_zod.z.object({ retryAfter: import_zod.z.number().int() })
|
|
3623
2897
|
};
|
|
3624
2898
|
function getErrorRegistry() {
|
|
3625
2899
|
const g = globalThis;
|
|
@@ -3631,13 +2905,7 @@ function getErrorRegistry() {
|
|
|
3631
2905
|
["forbidden", 403, "Forbidden"],
|
|
3632
2906
|
["not_found", 404, "NotFound"],
|
|
3633
2907
|
["conflict", 409, "Conflict"],
|
|
3634
|
-
["too_many_requests", 429, "TooManyRequests"]
|
|
3635
|
-
// Thrown by the purchases decorators, so they are pre-seeded like the
|
|
3636
|
-
// other built-ins: a project never declares them, but every gated or
|
|
3637
|
-
// metered route must surface them as TYPED errors on the client.
|
|
3638
|
-
["entitlement_required", 403, "EntitlementRequired"],
|
|
3639
|
-
["quota_exceeded", 429, "QuotaExceeded"],
|
|
3640
|
-
["credit_insufficient", 429, "CreditInsufficient"]
|
|
2908
|
+
["too_many_requests", 429, "TooManyRequests"]
|
|
3641
2909
|
]) {
|
|
3642
2910
|
const dataSchema = BUILTIN_DATA_SCHEMAS[code];
|
|
3643
2911
|
m.set(code, {
|
|
@@ -3658,8 +2926,8 @@ function defineError(code, status, dataSchema) {
|
|
|
3658
2926
|
`defineError: status for "${code}" must be a 4xx/5xx integer, got ${status} \u2014 error responses must not clobber success responses in the project spec.`
|
|
3659
2927
|
);
|
|
3660
2928
|
}
|
|
3661
|
-
const
|
|
3662
|
-
const existing =
|
|
2929
|
+
const registry2 = getErrorRegistry();
|
|
2930
|
+
const existing = registry2.get(code);
|
|
3663
2931
|
const className = defaultClassName(code);
|
|
3664
2932
|
const dataDigest = dataSchema ? digestOf(code, dataSchema) : void 0;
|
|
3665
2933
|
if (existing) {
|
|
@@ -3688,7 +2956,7 @@ function defineError(code, status, dataSchema) {
|
|
|
3688
2956
|
};
|
|
3689
2957
|
const cls = dataSchema ? makeWithData(dataSchema) : makeWithoutData();
|
|
3690
2958
|
Object.defineProperty(cls, "name", { value: className });
|
|
3691
|
-
|
|
2959
|
+
registry2.set(code, {
|
|
3692
2960
|
code,
|
|
3693
2961
|
status,
|
|
3694
2962
|
className,
|
|
@@ -3847,52 +3115,6 @@ function getJobConfig(ctor) {
|
|
|
3847
3115
|
return { schedule: meta.schedule, timeout, retry, handler: run };
|
|
3848
3116
|
}
|
|
3849
3117
|
|
|
3850
|
-
// src/resource.ts
|
|
3851
|
-
var Resource = class {
|
|
3852
|
-
/** The env-var names this resource needs. Optional; omit for none. */
|
|
3853
|
-
static secrets;
|
|
3854
|
-
};
|
|
3855
|
-
function hasSecrets(value) {
|
|
3856
|
-
if (typeof value !== "object" && typeof value !== "function") return false;
|
|
3857
|
-
const secrets = value.secrets;
|
|
3858
|
-
return secrets === void 0 || Array.isArray(secrets);
|
|
3859
|
-
}
|
|
3860
|
-
var registry2 = [];
|
|
3861
|
-
function __registerResource(resource) {
|
|
3862
|
-
registry2.push({ resource, booted: false });
|
|
3863
|
-
}
|
|
3864
|
-
function declaredSecrets(resource) {
|
|
3865
|
-
const ctor = resource.constructor;
|
|
3866
|
-
return hasSecrets(ctor) ? ctor.secrets ?? [] : [];
|
|
3867
|
-
}
|
|
3868
|
-
async function __runResourceBoot(envMap) {
|
|
3869
|
-
for (const entry of registry2) {
|
|
3870
|
-
if (entry.booted) continue;
|
|
3871
|
-
const secrets = declaredSecrets(entry.resource);
|
|
3872
|
-
const env = {};
|
|
3873
|
-
for (const name of secrets) {
|
|
3874
|
-
const value = envMap[name];
|
|
3875
|
-
if (value === void 0) {
|
|
3876
|
-
throw new Error(
|
|
3877
|
-
`Resource ${entry.resource.constructor.name} requires secret "${name}" but it is not set. Set it with \`palbase secret set ${name} ...\` (or in Studio) and redeploy.`
|
|
3878
|
-
);
|
|
3879
|
-
}
|
|
3880
|
-
env[name] = value;
|
|
3881
|
-
}
|
|
3882
|
-
await entry.resource.init(env);
|
|
3883
|
-
entry.booted = true;
|
|
3884
|
-
}
|
|
3885
|
-
}
|
|
3886
|
-
async function __shutdownResources() {
|
|
3887
|
-
for (let i = registry2.length - 1; i >= 0; i -= 1) {
|
|
3888
|
-
const entry = registry2[i];
|
|
3889
|
-
if (entry.booted && entry.resource.shutdown) {
|
|
3890
|
-
await entry.resource.shutdown();
|
|
3891
|
-
}
|
|
3892
|
-
}
|
|
3893
|
-
registry2.length = 0;
|
|
3894
|
-
}
|
|
3895
|
-
|
|
3896
3118
|
// src/index.ts
|
|
3897
3119
|
var import_zod2 = require("zod");
|
|
3898
3120
|
// Annotate the CommonJS export names for ESM import in node:
|
|
@@ -3903,17 +3125,11 @@ var import_zod2 = require("zod");
|
|
|
3903
3125
|
Client,
|
|
3904
3126
|
Conflict,
|
|
3905
3127
|
Controller,
|
|
3906
|
-
DEFAULT_TEMPLATE_LOCALE,
|
|
3907
3128
|
Database,
|
|
3908
3129
|
Delete,
|
|
3909
3130
|
Deny,
|
|
3910
3131
|
Documents,
|
|
3911
|
-
EGRESS_CONFIG_KIND,
|
|
3912
|
-
EGRESS_TIMEOUT_DEFAULT_MS,
|
|
3913
|
-
EGRESS_TIMEOUT_MAX_MS,
|
|
3914
|
-
EGRESS_TIMEOUT_MIN_MS,
|
|
3915
3132
|
EXTENSION_DEPENDENCIES,
|
|
3916
|
-
FLAGS_CONFIG_KIND,
|
|
3917
3133
|
Flags,
|
|
3918
3134
|
Forbidden,
|
|
3919
3135
|
Get,
|
|
@@ -3922,35 +3138,25 @@ var import_zod2 = require("zod");
|
|
|
3922
3138
|
HttpError,
|
|
3923
3139
|
Job,
|
|
3924
3140
|
Log,
|
|
3925
|
-
NOTIFICATIONS_CONFIG_KIND,
|
|
3926
3141
|
NotFound,
|
|
3927
3142
|
Notifications,
|
|
3928
3143
|
On,
|
|
3929
3144
|
OptionalUser,
|
|
3930
3145
|
PALBASE_EXTENSIONS,
|
|
3931
|
-
PROVIDER_CATALOG,
|
|
3932
3146
|
PalError,
|
|
3933
3147
|
PalbaseModuleError,
|
|
3934
3148
|
Param,
|
|
3935
3149
|
Patch,
|
|
3936
3150
|
PolicyBuilder,
|
|
3937
3151
|
Post,
|
|
3938
|
-
Purchases,
|
|
3939
3152
|
Put,
|
|
3940
3153
|
Query,
|
|
3941
3154
|
QueryParams,
|
|
3942
|
-
RESERVED_SECRET_PREFIX,
|
|
3943
3155
|
Realtime,
|
|
3944
3156
|
Req,
|
|
3945
3157
|
RequestId,
|
|
3946
|
-
RequireEntitlement,
|
|
3947
|
-
Resource,
|
|
3948
|
-
SECRETS_CONFIG_KIND,
|
|
3949
|
-
STORAGE_CONFIG_KIND,
|
|
3950
3158
|
Secrets,
|
|
3951
|
-
Spend,
|
|
3952
3159
|
Storage,
|
|
3953
|
-
TEST_USERS_CONFIG_KIND,
|
|
3954
3160
|
TooManyRequests,
|
|
3955
3161
|
TraceId,
|
|
3956
3162
|
TxPlanError,
|
|
@@ -3961,33 +3167,19 @@ var import_zod2 = require("zod");
|
|
|
3961
3167
|
User,
|
|
3962
3168
|
Webhook,
|
|
3963
3169
|
__getRuntime,
|
|
3964
|
-
__registerResource,
|
|
3965
3170
|
__requestALS,
|
|
3966
3171
|
__resetRegisteredControllers,
|
|
3967
|
-
__runResourceBoot,
|
|
3968
3172
|
__runWithRuntime,
|
|
3969
3173
|
__setRuntime,
|
|
3970
|
-
__shutdownResources,
|
|
3971
3174
|
bigint,
|
|
3972
3175
|
boolean,
|
|
3973
|
-
bucket,
|
|
3974
3176
|
buildModuleClients,
|
|
3975
|
-
buildProvider,
|
|
3976
3177
|
dec,
|
|
3977
3178
|
defineChannels,
|
|
3978
|
-
defineEgress,
|
|
3979
3179
|
defineError,
|
|
3980
|
-
defineFlags,
|
|
3981
3180
|
defineMiddleware,
|
|
3982
|
-
defineNotifications,
|
|
3983
3181
|
defineSchema,
|
|
3984
|
-
defineSecrets,
|
|
3985
|
-
defineStorage,
|
|
3986
|
-
defineTestUsers,
|
|
3987
|
-
entitlementFor,
|
|
3988
3182
|
enumType,
|
|
3989
|
-
extractVariables,
|
|
3990
|
-
flag,
|
|
3991
3183
|
getErrorRegistry,
|
|
3992
3184
|
getHookConfig,
|
|
3993
3185
|
getJobConfig,
|
|
@@ -4000,26 +3192,20 @@ var import_zod2 = require("zod");
|
|
|
4000
3192
|
jsonb,
|
|
4001
3193
|
makeEnvDts,
|
|
4002
3194
|
makeHttpClient,
|
|
4003
|
-
|
|
3195
|
+
makeStackDts,
|
|
4004
3196
|
makeTypedDB,
|
|
4005
3197
|
now,
|
|
4006
3198
|
numeric,
|
|
4007
3199
|
openai,
|
|
4008
3200
|
ownerOnly,
|
|
4009
|
-
parseFileSizeLimit,
|
|
4010
3201
|
policy,
|
|
4011
3202
|
publicChannel,
|
|
4012
3203
|
raw,
|
|
4013
3204
|
recordThrows,
|
|
4014
|
-
reservedSecretKey,
|
|
4015
|
-
secret,
|
|
4016
|
-
spendFor,
|
|
4017
|
-
testUser,
|
|
4018
3205
|
text,
|
|
4019
3206
|
timestamp,
|
|
4020
3207
|
toSchemaJSON,
|
|
4021
3208
|
uuid,
|
|
4022
|
-
validateUploadAgainstStorage,
|
|
4023
3209
|
vector,
|
|
4024
3210
|
z
|
|
4025
3211
|
});
|