@palbase/backend 22.0.1 → 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 +764 -25
- package/dist/bin/palbase-backend.cjs.map +1 -1
- package/dist/bin/palbase-backend.js +4 -4
- package/dist/{chunk-POYAFBLF.js → chunk-FSGSB42K.js} +13 -6
- package/dist/chunk-FSGSB42K.js.map +1 -0
- package/dist/{chunk-SSGAMC26.js → chunk-HAF67F2H.js} +114 -5
- 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-QYOHMVUW.js → chunk-REZU6UKT.js} +755 -24
- 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 +117 -6
- 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 +7 -3
- package/dist/{endpoint-B0LpZixz.d.cts → endpoint-BavvbW4P.d.ts} +116 -14
- package/dist/{endpoint-B0LpZixz.d.ts → endpoint-i8TTCohk.d.cts} +116 -14
- package/dist/engine/index.cjs +764 -25
- 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-g-EzitI-.d.ts → index-B3jmmItD.d.ts} +203 -7
- package/dist/{index-BCNtlG1w.d.ts → index-B7YBEG5w.d.ts} +117 -93
- package/dist/{index-BGSCWlUa.d.cts → index-Bmvx1EvJ.d.cts} +203 -7
- package/dist/{index-B4W6d2VJ.d.cts → index-E7OscPJT.d.cts} +117 -93
- package/dist/index.cjs +509 -1085
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +203 -1133
- package/dist/index.d.ts +203 -1133
- package/dist/index.js +288 -900
- 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-3BLYv4si.d.ts → registry-C3H2uPeZ.d.cts} +13 -169
- package/dist/{registry-Cw0YEYCg.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/endpoints.md +1 -1
- package/docs/errors.md +9 -0
- package/docs/llms-full.txt +17 -261
- package/docs/llms.txt +0 -2
- package/package.json +7 -7
- package/template/package.json +1 -1
- package/dist/chunk-N32VDWKH.js.map +0 -1
- package/dist/chunk-POYAFBLF.js.map +0 -1
- package/dist/chunk-QMVK4X3V.js.map +0 -1
- package/dist/chunk-QYOHMVUW.js.map +0 -1
- package/dist/chunk-SSGAMC26.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,32 +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
80
|
defineError: () => defineError,
|
|
102
|
-
defineFlags: () => defineFlags,
|
|
103
81
|
defineMiddleware: () => defineMiddleware,
|
|
104
|
-
defineNotifications: () => defineNotifications,
|
|
105
82
|
defineSchema: () => defineSchema,
|
|
106
|
-
defineSecrets: () => defineSecrets,
|
|
107
|
-
defineStorage: () => defineStorage,
|
|
108
|
-
defineTestUsers: () => defineTestUsers,
|
|
109
|
-
entitlementFor: () => entitlementFor,
|
|
110
83
|
enumType: () => enumType,
|
|
111
|
-
extractVariables: () => extractVariables,
|
|
112
|
-
flag: () => flag,
|
|
113
84
|
getErrorRegistry: () => getErrorRegistry,
|
|
114
85
|
getHookConfig: () => getHookConfig,
|
|
115
86
|
getJobConfig: () => getJobConfig,
|
|
@@ -122,23 +93,21 @@ __export(src_exports, {
|
|
|
122
93
|
jsonb: () => jsonb,
|
|
123
94
|
makeEnvDts: () => makeEnvDts,
|
|
124
95
|
makeHttpClient: () => makeHttpClient,
|
|
125
|
-
|
|
96
|
+
makeStackDts: () => makeStackDts,
|
|
126
97
|
makeTypedDB: () => makeTypedDB,
|
|
127
98
|
now: () => now,
|
|
128
99
|
numeric: () => numeric,
|
|
129
|
-
|
|
100
|
+
openai: () => openai,
|
|
101
|
+
ownerOnly: () => ownerOnly,
|
|
130
102
|
policy: () => policy,
|
|
103
|
+
publicChannel: () => publicChannel,
|
|
131
104
|
raw: () => raw,
|
|
132
105
|
recordThrows: () => recordThrows,
|
|
133
|
-
reservedSecretKey: () => reservedSecretKey,
|
|
134
|
-
secret: () => secret,
|
|
135
|
-
spendFor: () => spendFor,
|
|
136
|
-
testUser: () => testUser,
|
|
137
106
|
text: () => text,
|
|
138
107
|
timestamp: () => timestamp,
|
|
139
108
|
toSchemaJSON: () => toSchemaJSON,
|
|
140
109
|
uuid: () => uuid,
|
|
141
|
-
|
|
110
|
+
vector: () => vector,
|
|
142
111
|
z: () => import_zod2.z
|
|
143
112
|
});
|
|
144
113
|
module.exports = __toCommonJS(src_exports);
|
|
@@ -595,7 +564,11 @@ function makeTablesAccessor(ops) {
|
|
|
595
564
|
update: (id, data) => ops().update(name, id, data),
|
|
596
565
|
delete: (id) => ops().delete(name, id),
|
|
597
566
|
findById: (id) => ops().findById(name, id),
|
|
598
|
-
findMany: (query) => ops().findMany(name, query)
|
|
567
|
+
findMany: (query) => ops().findMany(name, query),
|
|
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)
|
|
599
572
|
};
|
|
600
573
|
}
|
|
601
574
|
}
|
|
@@ -604,16 +577,21 @@ function makeTablesAccessor(ops) {
|
|
|
604
577
|
}
|
|
605
578
|
var rawDatabase = makeServiceProxy("Database");
|
|
606
579
|
function makeTypedSurface(raw2) {
|
|
580
|
+
const reco = raw2;
|
|
607
581
|
const ops = {
|
|
608
582
|
query: (sql, params) => raw2.query(sql, params),
|
|
609
583
|
insert: (table, data) => raw2.insert(table, data),
|
|
610
584
|
update: (table, id, data) => raw2.update(table, id, data),
|
|
611
585
|
delete: (table, id) => raw2.delete(table, id),
|
|
612
586
|
findById: (table, id) => raw2.findById(table, id),
|
|
613
|
-
findMany: (table, query) => raw2.findMany(table, query)
|
|
587
|
+
findMany: (table, query) => raw2.findMany(table, query),
|
|
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)
|
|
614
592
|
};
|
|
615
593
|
return Object.assign(ops, {
|
|
616
|
-
tables: makeTablesAccessor(() =>
|
|
594
|
+
tables: makeTablesAccessor(() => reco),
|
|
617
595
|
transaction(fn) {
|
|
618
596
|
const builder = new TxPlanBuilder();
|
|
619
597
|
return runTxPlan(raw2, makeTxTablesAccessor(builder), builder, fn);
|
|
@@ -676,7 +654,6 @@ var Cache = makeServiceProxy("Cache");
|
|
|
676
654
|
var Secrets = makeServiceProxy("Secrets");
|
|
677
655
|
var Log = makeServiceProxy("Log");
|
|
678
656
|
var Notifications = makeServiceProxy("Notifications");
|
|
679
|
-
var Purchases = makeServiceProxy("Purchases");
|
|
680
657
|
var rawFlags = makeServiceProxy("Flags");
|
|
681
658
|
var Flags = Object.assign(
|
|
682
659
|
{
|
|
@@ -720,250 +697,6 @@ var Flags = Object.assign(
|
|
|
720
697
|
);
|
|
721
698
|
var Realtime = makeServiceProxy("Realtime");
|
|
722
699
|
|
|
723
|
-
// src/errors.ts
|
|
724
|
-
var HTTP_ERROR_BRAND = /* @__PURE__ */ Symbol.for("palbase.backend.httpError");
|
|
725
|
-
var HttpError = class extends Error {
|
|
726
|
-
status;
|
|
727
|
-
error;
|
|
728
|
-
errorDescription;
|
|
729
|
-
data;
|
|
730
|
-
/** See {@link HTTP_ERROR_BRAND} — how the engine recognises this across SDK copies. */
|
|
731
|
-
[HTTP_ERROR_BRAND] = true;
|
|
732
|
-
constructor(status, error, errorDescription, data) {
|
|
733
|
-
super(errorDescription);
|
|
734
|
-
this.name = "HttpError";
|
|
735
|
-
this.status = status;
|
|
736
|
-
this.error = error;
|
|
737
|
-
this.errorDescription = errorDescription;
|
|
738
|
-
if (data !== void 0) {
|
|
739
|
-
this.data = data;
|
|
740
|
-
}
|
|
741
|
-
}
|
|
742
|
-
/**
|
|
743
|
-
* Serialize to the standard Palbase error response format.
|
|
744
|
-
* The `requestId` is injected by the runtime layer from the request context.
|
|
745
|
-
* When called without arguments (e.g. JSON.stringify), request_id is omitted.
|
|
746
|
-
* When `data` is set, it is appended as a strict-superset field.
|
|
747
|
-
*/
|
|
748
|
-
toJSON(requestId) {
|
|
749
|
-
const result = {
|
|
750
|
-
error: this.error,
|
|
751
|
-
error_description: this.errorDescription,
|
|
752
|
-
status: this.status
|
|
753
|
-
};
|
|
754
|
-
if (requestId) {
|
|
755
|
-
result.request_id = requestId;
|
|
756
|
-
}
|
|
757
|
-
if (this.data !== void 0) {
|
|
758
|
-
result.data = this.data;
|
|
759
|
-
}
|
|
760
|
-
return result;
|
|
761
|
-
}
|
|
762
|
-
};
|
|
763
|
-
var PalError = class extends HttpError {
|
|
764
|
-
constructor(status, code, description, data) {
|
|
765
|
-
super(status, code, description, data);
|
|
766
|
-
this.name = "PalError";
|
|
767
|
-
}
|
|
768
|
-
};
|
|
769
|
-
var NamedHttpError = class extends HttpError {
|
|
770
|
-
constructor(status, defaultCode, name, message, code, data) {
|
|
771
|
-
super(status, code ?? defaultCode, message ?? defaultMessage(name), data);
|
|
772
|
-
this.name = name;
|
|
773
|
-
}
|
|
774
|
-
};
|
|
775
|
-
function defaultMessage(name) {
|
|
776
|
-
const spaced = name.replace(/([a-z0-9])([A-Z])/g, "$1 $2");
|
|
777
|
-
return spaced.charAt(0).toUpperCase() + spaced.slice(1).toLowerCase();
|
|
778
|
-
}
|
|
779
|
-
var BadRequest = class extends NamedHttpError {
|
|
780
|
-
constructor(data, message) {
|
|
781
|
-
super(400, "bad_request", "BadRequest", message, void 0, data);
|
|
782
|
-
}
|
|
783
|
-
};
|
|
784
|
-
var Unauthorized = class extends NamedHttpError {
|
|
785
|
-
constructor(message, code, data) {
|
|
786
|
-
super(401, "unauthorized", "Unauthorized", message, code, data);
|
|
787
|
-
}
|
|
788
|
-
};
|
|
789
|
-
var Forbidden = class extends NamedHttpError {
|
|
790
|
-
constructor(message, code, data) {
|
|
791
|
-
super(403, "forbidden", "Forbidden", message, code, data);
|
|
792
|
-
}
|
|
793
|
-
};
|
|
794
|
-
var NotFound = class extends NamedHttpError {
|
|
795
|
-
constructor(message, code, data) {
|
|
796
|
-
super(404, "not_found", "NotFound", message, code, data);
|
|
797
|
-
}
|
|
798
|
-
};
|
|
799
|
-
var Conflict = class extends NamedHttpError {
|
|
800
|
-
constructor(message, code, data) {
|
|
801
|
-
super(409, "conflict", "Conflict", message, code, data);
|
|
802
|
-
}
|
|
803
|
-
};
|
|
804
|
-
var TooManyRequests = class extends NamedHttpError {
|
|
805
|
-
constructor(data, message) {
|
|
806
|
-
super(429, "too_many_requests", "TooManyRequests", message, void 0, data);
|
|
807
|
-
}
|
|
808
|
-
};
|
|
809
|
-
|
|
810
|
-
// src/purchases/errors.ts
|
|
811
|
-
function asPalstoreError(err) {
|
|
812
|
-
if (!(err instanceof Error)) return null;
|
|
813
|
-
const e = err;
|
|
814
|
-
if (typeof e.code !== "string" || typeof e.status !== "number") return null;
|
|
815
|
-
return { code: e.code, status: e.status, message: e.message };
|
|
816
|
-
}
|
|
817
|
-
function toHttpError(err) {
|
|
818
|
-
if (err instanceof HttpError) return err;
|
|
819
|
-
const p = asPalstoreError(err);
|
|
820
|
-
if (!p) return err;
|
|
821
|
-
switch (p.code) {
|
|
822
|
-
case "entitlement_required": {
|
|
823
|
-
const entitlement = err.entitlement ?? null;
|
|
824
|
-
return new PalError(403, "entitlement_required", p.message, { entitlement });
|
|
825
|
-
}
|
|
826
|
-
case "quota_exceeded":
|
|
827
|
-
case "credit_insufficient": {
|
|
828
|
-
const state = err.state;
|
|
829
|
-
return new PalError(429, p.code, p.message, state);
|
|
830
|
-
}
|
|
831
|
-
default:
|
|
832
|
-
return err;
|
|
833
|
-
}
|
|
834
|
-
}
|
|
835
|
-
|
|
836
|
-
// src/purchases/registry.ts
|
|
837
|
-
var ENTITLEMENTS = /* @__PURE__ */ Symbol.for("palbase.backend.purchases.entitlements");
|
|
838
|
-
var SPENDS = /* @__PURE__ */ Symbol.for("palbase.backend.purchases.spends");
|
|
839
|
-
function carrierOf(target) {
|
|
840
|
-
return typeof target === "function" ? target : target.constructor ?? target;
|
|
841
|
-
}
|
|
842
|
-
function own(carrier, slot) {
|
|
843
|
-
if (!Object.prototype.hasOwnProperty.call(carrier, slot)) {
|
|
844
|
-
carrier[slot] = {};
|
|
845
|
-
}
|
|
846
|
-
return carrier[slot];
|
|
847
|
-
}
|
|
848
|
-
function recordEntitlement(target, fnName, key) {
|
|
849
|
-
own(carrierOf(target), ENTITLEMENTS)[fnName] = key;
|
|
850
|
-
}
|
|
851
|
-
function recordSpend(target, fnName, meta) {
|
|
852
|
-
own(carrierOf(target), SPENDS)[fnName] = meta;
|
|
853
|
-
}
|
|
854
|
-
function entitlementFor(ctor, fnName) {
|
|
855
|
-
return carrierOf(ctor)[ENTITLEMENTS]?.[fnName];
|
|
856
|
-
}
|
|
857
|
-
function spendFor(ctor, fnName) {
|
|
858
|
-
return carrierOf(ctor)[SPENDS]?.[fnName];
|
|
859
|
-
}
|
|
860
|
-
|
|
861
|
-
// src/purchases/subject.ts
|
|
862
|
-
var subjectByRequest = /* @__PURE__ */ new WeakMap();
|
|
863
|
-
function storeEnv() {
|
|
864
|
-
return process.env.PALSTORE_STORE_ENV === "sandbox" ? "sandbox" : "production";
|
|
865
|
-
}
|
|
866
|
-
async function currentSubjectId() {
|
|
867
|
-
const store = __requestALS.getStore();
|
|
868
|
-
if (!store) {
|
|
869
|
-
throw new Error(
|
|
870
|
-
"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."
|
|
871
|
-
);
|
|
872
|
-
}
|
|
873
|
-
const memoized = subjectByRequest.get(store);
|
|
874
|
-
if (memoized) return memoized;
|
|
875
|
-
const userId = store.userId;
|
|
876
|
-
if (!userId) {
|
|
877
|
-
throw new Unauthorized(
|
|
878
|
-
"This endpoint requires an authenticated user: purchases entitlements and quota are per-user, and the subject is resolved from the request's identity."
|
|
879
|
-
);
|
|
880
|
-
}
|
|
881
|
-
const resolving = Purchases.resolveSubject({ userRef: userId, storeEnv: storeEnv() }).then(
|
|
882
|
-
(r) => r.subjectId
|
|
883
|
-
);
|
|
884
|
-
subjectByRequest.set(store, resolving);
|
|
885
|
-
resolving.catch(() => subjectByRequest.delete(store));
|
|
886
|
-
return resolving;
|
|
887
|
-
}
|
|
888
|
-
|
|
889
|
-
// src/purchases/decorators.ts
|
|
890
|
-
var WRAPPED = /* @__PURE__ */ Symbol.for("palbase.backend.purchases.wrapped");
|
|
891
|
-
function idempotencyKeyFor(key) {
|
|
892
|
-
const store = __requestALS.getStore();
|
|
893
|
-
const base = store?.idempotencyKey || store?.requestId;
|
|
894
|
-
if (!base) {
|
|
895
|
-
throw new Error(
|
|
896
|
-
"@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."
|
|
897
|
-
);
|
|
898
|
-
}
|
|
899
|
-
return `${base}:${key}`;
|
|
900
|
-
}
|
|
901
|
-
function ensureWrapper(target, fnName, descriptor) {
|
|
902
|
-
const original = descriptor.value;
|
|
903
|
-
if (typeof original !== "function") {
|
|
904
|
-
throw new Error(
|
|
905
|
-
`@RequireEntitlement/@Spend can only decorate a method; "${fnName}" is not one.`
|
|
906
|
-
);
|
|
907
|
-
}
|
|
908
|
-
if (original[WRAPPED]) return;
|
|
909
|
-
const wrapper = async function(...args) {
|
|
910
|
-
const ctor = this.constructor;
|
|
911
|
-
const entitlement = entitlementFor(ctor, fnName);
|
|
912
|
-
const spend = spendFor(ctor, fnName);
|
|
913
|
-
let subjectId;
|
|
914
|
-
try {
|
|
915
|
-
subjectId = await currentSubjectId();
|
|
916
|
-
} catch (err) {
|
|
917
|
-
throw toHttpError(err);
|
|
918
|
-
}
|
|
919
|
-
if (entitlement !== void 0) {
|
|
920
|
-
try {
|
|
921
|
-
await Purchases.require(subjectId, entitlement);
|
|
922
|
-
} catch (err) {
|
|
923
|
-
throw toHttpError(err);
|
|
924
|
-
}
|
|
925
|
-
}
|
|
926
|
-
if (spend === void 0) {
|
|
927
|
-
return original.apply(this, args);
|
|
928
|
-
}
|
|
929
|
-
let handlerFailure;
|
|
930
|
-
try {
|
|
931
|
-
return await Purchases.withSpend(
|
|
932
|
-
subjectId,
|
|
933
|
-
spend.key,
|
|
934
|
-
{ count: spend.count, idempotencyKey: idempotencyKeyFor(spend.key) },
|
|
935
|
-
async () => {
|
|
936
|
-
try {
|
|
937
|
-
return await original.apply(this, args);
|
|
938
|
-
} catch (err) {
|
|
939
|
-
handlerFailure = { err };
|
|
940
|
-
throw err;
|
|
941
|
-
}
|
|
942
|
-
}
|
|
943
|
-
);
|
|
944
|
-
} catch (err) {
|
|
945
|
-
if (handlerFailure !== void 0 && handlerFailure.err === err) throw err;
|
|
946
|
-
throw toHttpError(err);
|
|
947
|
-
}
|
|
948
|
-
};
|
|
949
|
-
wrapper[WRAPPED] = true;
|
|
950
|
-
descriptor.value = wrapper;
|
|
951
|
-
}
|
|
952
|
-
function RequireEntitlement(key) {
|
|
953
|
-
return function(target, propertyKey, descriptor) {
|
|
954
|
-
const fnName = String(propertyKey);
|
|
955
|
-
recordEntitlement(target, fnName, key);
|
|
956
|
-
ensureWrapper(target, fnName, descriptor);
|
|
957
|
-
};
|
|
958
|
-
}
|
|
959
|
-
function Spend(key) {
|
|
960
|
-
return function(target, propertyKey, descriptor) {
|
|
961
|
-
const fnName = String(propertyKey);
|
|
962
|
-
recordSpend(target, fnName, { key, count: 1 });
|
|
963
|
-
ensureWrapper(target, fnName, descriptor);
|
|
964
|
-
};
|
|
965
|
-
}
|
|
966
|
-
|
|
967
700
|
// src/clients/http.ts
|
|
968
701
|
var PalbaseModuleError = class extends Error {
|
|
969
702
|
code;
|
|
@@ -1309,12 +1042,12 @@ function buildFlagsClient(http, cfg) {
|
|
|
1309
1042
|
return {
|
|
1310
1043
|
data: Object.keys(values).map((name) => {
|
|
1311
1044
|
const value = values[name];
|
|
1312
|
-
const
|
|
1045
|
+
const flag = {
|
|
1313
1046
|
name,
|
|
1314
1047
|
enabled: flagEnabled(value)
|
|
1315
1048
|
};
|
|
1316
|
-
if (typeof value === "string")
|
|
1317
|
-
return
|
|
1049
|
+
if (typeof value === "string") flag.variant = { name: value };
|
|
1050
|
+
return flag;
|
|
1318
1051
|
}),
|
|
1319
1052
|
error: null,
|
|
1320
1053
|
status: res.status
|
|
@@ -1542,18 +1275,99 @@ function buildNotificationsClient(http) {
|
|
|
1542
1275
|
|
|
1543
1276
|
// src/clients/realtime.ts
|
|
1544
1277
|
var import_node_crypto = require("crypto");
|
|
1545
|
-
function mintToken(
|
|
1278
|
+
function mintToken(secret) {
|
|
1546
1279
|
const now2 = Math.floor(Date.now() / 1e3);
|
|
1547
1280
|
const header = Buffer.from(JSON.stringify({ alg: "HS256", typ: "JWT" })).toString("base64url");
|
|
1548
1281
|
const payload = Buffer.from(
|
|
1549
1282
|
JSON.stringify({ iss: "backend-runtime", role: "service_role", iat: now2, exp: now2 + 60 })
|
|
1550
1283
|
).toString("base64url");
|
|
1551
|
-
const sig = (0, import_node_crypto.createHmac)("sha256",
|
|
1284
|
+
const sig = (0, import_node_crypto.createHmac)("sha256", secret).update(`${header}.${payload}`).digest("base64url");
|
|
1552
1285
|
return `${header}.${payload}.${sig}`;
|
|
1553
1286
|
}
|
|
1554
1287
|
function buildRealtimeClient(cfg) {
|
|
1555
1288
|
const url = `${cfg.baseUrl}/realtime/api/broadcast`;
|
|
1289
|
+
const stateUrl = `${cfg.baseUrl}/realtime/api/state`;
|
|
1290
|
+
async function writeState(topic, key, value, del) {
|
|
1291
|
+
if (typeof topic !== "string" || topic.length === 0) {
|
|
1292
|
+
return {
|
|
1293
|
+
data: null,
|
|
1294
|
+
error: new PalbaseModuleError("invalid_argument", "topic must be a non-empty string", 400)
|
|
1295
|
+
};
|
|
1296
|
+
}
|
|
1297
|
+
if (typeof key !== "string" || key.length === 0) {
|
|
1298
|
+
return {
|
|
1299
|
+
data: null,
|
|
1300
|
+
error: new PalbaseModuleError("invalid_argument", "key must be a non-empty string", 400)
|
|
1301
|
+
};
|
|
1302
|
+
}
|
|
1303
|
+
if (!cfg.apiJwtSecret) {
|
|
1304
|
+
return {
|
|
1305
|
+
data: null,
|
|
1306
|
+
error: new PalbaseModuleError(
|
|
1307
|
+
"realtime_unconfigured",
|
|
1308
|
+
"Realtime.state is unavailable: realtime is not provisioned for this Environment (PALBASE_REALTIME_API_JWT_SECRET unset).",
|
|
1309
|
+
503
|
|
1310
|
+
)
|
|
1311
|
+
};
|
|
1312
|
+
}
|
|
1313
|
+
let token;
|
|
1314
|
+
try {
|
|
1315
|
+
token = mintToken(cfg.apiJwtSecret);
|
|
1316
|
+
} catch (err) {
|
|
1317
|
+
return {
|
|
1318
|
+
data: null,
|
|
1319
|
+
error: new PalbaseModuleError(
|
|
1320
|
+
"realtime_token_error",
|
|
1321
|
+
err instanceof Error ? err.message : "token mint failed",
|
|
1322
|
+
500
|
|
1323
|
+
)
|
|
1324
|
+
};
|
|
1325
|
+
}
|
|
1326
|
+
const op = del ? { topic, key, del: true } : { topic, key, value: value ?? {} };
|
|
1327
|
+
const doFetch = cfg.fetchImpl ?? globalThis.fetch;
|
|
1328
|
+
let response;
|
|
1329
|
+
try {
|
|
1330
|
+
response = await doFetch(stateUrl, {
|
|
1331
|
+
method: "POST",
|
|
1332
|
+
headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}` },
|
|
1333
|
+
body: JSON.stringify({ ops: [op] })
|
|
1334
|
+
});
|
|
1335
|
+
} catch (err) {
|
|
1336
|
+
return {
|
|
1337
|
+
data: null,
|
|
1338
|
+
error: new PalbaseModuleError(
|
|
1339
|
+
"network_error",
|
|
1340
|
+
err instanceof Error ? err.message : "state request failed",
|
|
1341
|
+
0
|
|
1342
|
+
)
|
|
1343
|
+
};
|
|
1344
|
+
}
|
|
1345
|
+
if (response.status === 202 || response.status === 200) {
|
|
1346
|
+
return { data: void 0, error: null };
|
|
1347
|
+
}
|
|
1348
|
+
const detail = await response.text().catch(() => "");
|
|
1349
|
+
return {
|
|
1350
|
+
data: null,
|
|
1351
|
+
error: new PalbaseModuleError(
|
|
1352
|
+
"realtime_state_failed",
|
|
1353
|
+
`realtime state write returned ${response.status}${detail ? `: ${detail}` : ""}`,
|
|
1354
|
+
response.status
|
|
1355
|
+
)
|
|
1356
|
+
};
|
|
1357
|
+
}
|
|
1556
1358
|
return {
|
|
1359
|
+
state: {
|
|
1360
|
+
/** Write one DURABLE entry on a channel's shared state.
|
|
1361
|
+
*
|
|
1362
|
+
* The difference from broadcast is WHO it reaches: a broadcast reaches
|
|
1363
|
+
* whoever is listening at that instant, while state is also handed to
|
|
1364
|
+
* whoever joins afterwards. "This value is now X" wants the second — a
|
|
1365
|
+
* client connecting a second later should not have to wait for the next
|
|
1366
|
+
* change to learn X. */
|
|
1367
|
+
set: (topic, key, value) => writeState(topic, key, value, false),
|
|
1368
|
+
/** Remove one entry. */
|
|
1369
|
+
clear: (topic, key) => writeState(topic, key, void 0, true)
|
|
1370
|
+
},
|
|
1557
1371
|
async broadcast(channel, event, payload) {
|
|
1558
1372
|
if (typeof channel !== "string" || channel.length === 0) {
|
|
1559
1373
|
return {
|
|
@@ -1638,11 +1452,11 @@ function validateStoragePath(p) {
|
|
|
1638
1452
|
);
|
|
1639
1453
|
}
|
|
1640
1454
|
}
|
|
1641
|
-
function toFileObject(
|
|
1455
|
+
function toFileObject(bucket, row) {
|
|
1642
1456
|
return {
|
|
1643
1457
|
name: row.path ?? "",
|
|
1644
1458
|
path: row.path ?? "",
|
|
1645
|
-
bucket: row.bucket ??
|
|
1459
|
+
bucket: row.bucket ?? bucket,
|
|
1646
1460
|
size: typeof row.size === "number" ? row.size : 0,
|
|
1647
1461
|
contentType: row.contentType ?? "",
|
|
1648
1462
|
checksum: row.checksum ?? "",
|
|
@@ -1758,35 +1572,6 @@ function buildModuleClients(cfg) {
|
|
|
1758
1572
|
};
|
|
1759
1573
|
}
|
|
1760
1574
|
|
|
1761
|
-
// src/purchases/keys-gen.ts
|
|
1762
|
-
function liveKeys(entries) {
|
|
1763
|
-
return Object.entries(entries ?? {}).filter(([, def]) => def?.removed !== true).map(([key]) => key).sort();
|
|
1764
|
-
}
|
|
1765
|
-
function memberName(key) {
|
|
1766
|
-
return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(key) ? key : JSON.stringify(key);
|
|
1767
|
-
}
|
|
1768
|
-
function members(keys) {
|
|
1769
|
-
if (keys.length === 0) return "";
|
|
1770
|
-
return `
|
|
1771
|
-
${keys.map((k) => ` ${memberName(k)}: true;`).join("\n")}
|
|
1772
|
-
`;
|
|
1773
|
-
}
|
|
1774
|
-
function makePurchasesDts(manifest) {
|
|
1775
|
-
const entitlements = liveKeys(manifest.entitlements);
|
|
1776
|
-
const limits = liveKeys({ ...manifest.limits, ...manifest.credits });
|
|
1777
|
-
return `// palbase-purchases.d.ts \u2014 GENERATED by @palbase/backend. Do not edit.
|
|
1778
|
-
// Source: the project's palstore catalog revision.
|
|
1779
|
-
|
|
1780
|
-
declare module "@palbase/backend/purchases" {
|
|
1781
|
-
interface Entitlements {${members(entitlements)}}
|
|
1782
|
-
|
|
1783
|
-
interface Limits {${members(limits)}}
|
|
1784
|
-
}
|
|
1785
|
-
|
|
1786
|
-
export {};
|
|
1787
|
-
`;
|
|
1788
|
-
}
|
|
1789
|
-
|
|
1790
1575
|
// src/db/policy.ts
|
|
1791
1576
|
var PolicyBuilder = class {
|
|
1792
1577
|
_def;
|
|
@@ -1861,8 +1646,82 @@ function defineSchema(input) {
|
|
|
1861
1646
|
if (table.raw !== void 0 && table.raw.length > 0) tableDef.raw = table.raw.slice();
|
|
1862
1647
|
if (table.checks !== void 0 && table.checks.length > 0) tableDef.checks = table.checks.slice();
|
|
1863
1648
|
if (table.indexes !== void 0 && table.indexes.length > 0) tableDef.indexes = table.indexes.slice();
|
|
1649
|
+
if (table.search !== void 0) {
|
|
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
|
+
);
|
|
1656
|
+
}
|
|
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
|
+
}
|
|
1688
|
+
}
|
|
1689
|
+
}
|
|
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;
|
|
1697
|
+
}
|
|
1864
1698
|
tables[name] = tableDef;
|
|
1865
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
|
+
}
|
|
1866
1725
|
const extensions = [...new Set(input.extensions ?? [])];
|
|
1867
1726
|
return { tables, extensions };
|
|
1868
1727
|
}
|
|
@@ -1889,6 +1748,7 @@ function columnToJSON(column) {
|
|
|
1889
1748
|
if (def.enumName !== void 0) out.enumName = def.enumName;
|
|
1890
1749
|
if (def.enumValues !== void 0) out.enumValues = [...def.enumValues];
|
|
1891
1750
|
if (def.unique === true) out.unique = true;
|
|
1751
|
+
if (def.dimensions !== void 0) out.dimensions = def.dimensions;
|
|
1892
1752
|
return out;
|
|
1893
1753
|
}
|
|
1894
1754
|
function policyToJSON(policy2) {
|
|
@@ -1904,6 +1764,75 @@ function policyToJSON(policy2) {
|
|
|
1904
1764
|
permissive: policy2.permissive !== false
|
|
1905
1765
|
};
|
|
1906
1766
|
}
|
|
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
|
+
}
|
|
1804
|
+
const out = {};
|
|
1805
|
+
if (Array.isArray(search.text) && search.text.length > 0) out.text = { columns: [...search.text] };
|
|
1806
|
+
const legs = search.vector === void 0 ? [] : Array.isArray(search.vector) ? search.vector : [search.vector];
|
|
1807
|
+
if (legs.length > 0) {
|
|
1808
|
+
out.vector = legs.map((leg) => {
|
|
1809
|
+
const v = { metric: leg.metric ?? "cosine" };
|
|
1810
|
+
if (leg.column !== void 0) v.column = leg.column;
|
|
1811
|
+
if (leg.staleness !== void 0) v.staleness = leg.staleness;
|
|
1812
|
+
if (leg.model !== void 0) {
|
|
1813
|
+
v.embed = {
|
|
1814
|
+
provider: leg.model.provider,
|
|
1815
|
+
model: leg.model.model,
|
|
1816
|
+
from: [...leg.from ?? []],
|
|
1817
|
+
...leg.model.apiKeyName !== void 0 ? { apiKeyName: leg.model.apiKeyName } : {},
|
|
1818
|
+
...leg.model.dimensions !== void 0 ? { dimensions: leg.model.dimensions } : {},
|
|
1819
|
+
...leg.model.baseURL !== void 0 ? { baseURL: leg.model.baseURL } : {}
|
|
1820
|
+
};
|
|
1821
|
+
}
|
|
1822
|
+
return v;
|
|
1823
|
+
});
|
|
1824
|
+
}
|
|
1825
|
+
commonSearchFields(search, out);
|
|
1826
|
+
return out;
|
|
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
|
+
}
|
|
1907
1836
|
function tableToJSON(table) {
|
|
1908
1837
|
const columns = {};
|
|
1909
1838
|
for (const [name, column] of Object.entries(table.columns)) {
|
|
@@ -1939,6 +1868,14 @@ function tableToJSON(table) {
|
|
|
1939
1868
|
if (table.indexes !== void 0 && table.indexes.length > 0) {
|
|
1940
1869
|
out.indexes = table.indexes.map((i) => ({ name: i.name, columns: [...i.columns] }));
|
|
1941
1870
|
}
|
|
1871
|
+
if (table.search !== void 0) {
|
|
1872
|
+
const vectorColumn = Object.entries(columns).find(([, c]) => c.dimensions !== void 0)?.[0];
|
|
1873
|
+
const sj = searchToJSON(table.search, vectorColumn);
|
|
1874
|
+
if (sj.text !== void 0 || sj.vector !== void 0) out.search = sj;
|
|
1875
|
+
}
|
|
1876
|
+
if (table.memory !== void 0) {
|
|
1877
|
+
out.memory = memoryToJSON(table.memory);
|
|
1878
|
+
}
|
|
1942
1879
|
return out;
|
|
1943
1880
|
}
|
|
1944
1881
|
function toSchemaJSON(schema) {
|
|
@@ -1950,6 +1887,63 @@ function toSchemaJSON(schema) {
|
|
|
1950
1887
|
return { tables, extensions: [...new Set(schema.extensions ?? [])] };
|
|
1951
1888
|
}
|
|
1952
1889
|
|
|
1890
|
+
// src/channels.ts
|
|
1891
|
+
var CHANNELS = /* @__PURE__ */ Symbol.for("palbase.backend.channels");
|
|
1892
|
+
function ownerOnly() {
|
|
1893
|
+
return { kind: "owner" };
|
|
1894
|
+
}
|
|
1895
|
+
function publicChannel(opts = {}) {
|
|
1896
|
+
return { kind: "public", publish: opts.publish ?? false, state: opts.state };
|
|
1897
|
+
}
|
|
1898
|
+
function paramCount(pattern) {
|
|
1899
|
+
return pattern.split(":").filter((s) => s.startsWith("{") && s.endsWith("}")).length;
|
|
1900
|
+
}
|
|
1901
|
+
function defineChannels(map) {
|
|
1902
|
+
const entries = [];
|
|
1903
|
+
for (const [pattern, entry] of Object.entries(map)) {
|
|
1904
|
+
if ("kind" in entry && entry.kind === "owner") {
|
|
1905
|
+
if (paramCount(pattern) !== 1) {
|
|
1906
|
+
throw new Error(
|
|
1907
|
+
`defineChannels: ownerOnly() pattern "${pattern}" must carry exactly one {param} (its value is compared to the token subject)`
|
|
1908
|
+
);
|
|
1909
|
+
}
|
|
1910
|
+
entries.push({ pattern, kind: "owner" });
|
|
1911
|
+
} else if ("kind" in entry && entry.kind === "public") {
|
|
1912
|
+
entries.push({ pattern, kind: "public", publish: entry.publish, state: entry.state });
|
|
1913
|
+
} else {
|
|
1914
|
+
const c = entry;
|
|
1915
|
+
entries.push({ pattern, kind: "custom", authorize: c.authorize, handler: c.handler });
|
|
1916
|
+
}
|
|
1917
|
+
}
|
|
1918
|
+
const def = { entries };
|
|
1919
|
+
const carrier = globalThis;
|
|
1920
|
+
const existing = carrier[CHANNELS];
|
|
1921
|
+
if (existing && !sameDeclaration(existing, def)) {
|
|
1922
|
+
throw new Error(
|
|
1923
|
+
`defineChannels: channels were already declared (${existing.entries.length} pattern(s): ${existing.entries.map((e) => e.pattern).join(", ")}). A project declares its channels ONCE \u2014 a second call would overwrite the first, and channels nobody declared are refused.`
|
|
1924
|
+
);
|
|
1925
|
+
}
|
|
1926
|
+
carrier[CHANNELS] = def;
|
|
1927
|
+
return def;
|
|
1928
|
+
}
|
|
1929
|
+
function wireRow(e) {
|
|
1930
|
+
return JSON.stringify([
|
|
1931
|
+
e.pattern,
|
|
1932
|
+
e.kind,
|
|
1933
|
+
e.publish ?? false,
|
|
1934
|
+
e.state?.read ?? false,
|
|
1935
|
+
e.state?.write ?? false,
|
|
1936
|
+
// The handler is compared by SOURCE, like authorize: two evaluations of one
|
|
1937
|
+
// module give the same text, while different logic gives different text.
|
|
1938
|
+
e.handler ? e.handler.toString() : "",
|
|
1939
|
+
e.authorize ? e.authorize.toString() : ""
|
|
1940
|
+
]);
|
|
1941
|
+
}
|
|
1942
|
+
function sameDeclaration(a, b) {
|
|
1943
|
+
if (a.entries.length !== b.entries.length) return false;
|
|
1944
|
+
return a.entries.every((x, i) => wireRow(x) === wireRow(b.entries[i]));
|
|
1945
|
+
}
|
|
1946
|
+
|
|
1953
1947
|
// src/db/extensions.ts
|
|
1954
1948
|
var PALBASE_EXTENSIONS = [
|
|
1955
1949
|
// Search & text
|
|
@@ -1963,8 +1957,6 @@ var PALBASE_EXTENSIONS = [
|
|
|
1963
1957
|
"citext",
|
|
1964
1958
|
// case-insensitive text type
|
|
1965
1959
|
// Geospatial / location
|
|
1966
|
-
"postgis",
|
|
1967
|
-
// geospatial types + queries (maps, "near me")
|
|
1968
1960
|
"cube",
|
|
1969
1961
|
// multi-dimensional cubes (dependency of earthdistance)
|
|
1970
1962
|
"earthdistance",
|
|
@@ -1979,8 +1971,6 @@ var PALBASE_EXTENSIONS = [
|
|
|
1979
1971
|
// GiST operator classes for scalar types — needed for EXCLUDE
|
|
1980
1972
|
// constraints that mix "=" with a range/&& overlap (e.g. no-double-booking).
|
|
1981
1973
|
// Scheduling
|
|
1982
|
-
"pg_cron",
|
|
1983
|
-
// schedule jobs inside the database
|
|
1984
1974
|
// Crypto / ids (also installed by default; listable for explicitness)
|
|
1985
1975
|
"pgcrypto",
|
|
1986
1976
|
// cryptographic functions (hashing, encryption)
|
|
@@ -1995,6 +1985,11 @@ function isPalbaseExtension(name) {
|
|
|
1995
1985
|
}
|
|
1996
1986
|
|
|
1997
1987
|
// src/db/columns.ts
|
|
1988
|
+
function refuseOnVector(def, modifier) {
|
|
1989
|
+
if (def.type === "vector") {
|
|
1990
|
+
throw new Error(`vector column: .${modifier}() is not supported (FR-002 \u2014 allowed: nullable()/notNull())`);
|
|
1991
|
+
}
|
|
1992
|
+
}
|
|
1998
1993
|
var ColumnBuilder = class _ColumnBuilder {
|
|
1999
1994
|
_def;
|
|
2000
1995
|
constructor(type, existingDef) {
|
|
@@ -2006,6 +2001,7 @@ var ColumnBuilder = class _ColumnBuilder {
|
|
|
2006
2001
|
}
|
|
2007
2002
|
/** Mark this column as the primary key. */
|
|
2008
2003
|
primaryKey() {
|
|
2004
|
+
refuseOnVector(this._def, "primaryKey");
|
|
2009
2005
|
this._def.primaryKey = true;
|
|
2010
2006
|
return new _ColumnBuilder(this._def.type, this._def);
|
|
2011
2007
|
}
|
|
@@ -2021,16 +2017,19 @@ var ColumnBuilder = class _ColumnBuilder {
|
|
|
2021
2017
|
}
|
|
2022
2018
|
/** Set a default value. */
|
|
2023
2019
|
default(value) {
|
|
2020
|
+
refuseOnVector(this._def, "default");
|
|
2024
2021
|
this._def.defaultValue = value;
|
|
2025
2022
|
return new _ColumnBuilder(this._def.type, this._def);
|
|
2026
2023
|
}
|
|
2027
2024
|
/** UUID: generate a random default (gen_random_uuid()). */
|
|
2028
2025
|
defaultRandom() {
|
|
2026
|
+
refuseOnVector(this._def, "defaultRandom");
|
|
2029
2027
|
this._def.defaultRandom = true;
|
|
2030
2028
|
return new _ColumnBuilder(this._def.type, this._def);
|
|
2031
2029
|
}
|
|
2032
2030
|
/** Timestamp: default to now(). */
|
|
2033
2031
|
defaultNow() {
|
|
2032
|
+
refuseOnVector(this._def, "defaultNow");
|
|
2034
2033
|
this._def.defaultNow = true;
|
|
2035
2034
|
return new _ColumnBuilder(this._def.type, this._def);
|
|
2036
2035
|
}
|
|
@@ -2051,6 +2050,7 @@ var ColumnBuilder = class _ColumnBuilder {
|
|
|
2051
2050
|
return this;
|
|
2052
2051
|
}
|
|
2053
2052
|
references(table, column) {
|
|
2053
|
+
refuseOnVector(this._def, "references");
|
|
2054
2054
|
this._def.references = { table, column };
|
|
2055
2055
|
return new _ColumnBuilder(this._def.type, this._def);
|
|
2056
2056
|
}
|
|
@@ -2075,6 +2075,7 @@ var ColumnBuilder = class _ColumnBuilder {
|
|
|
2075
2075
|
* as the real boundary; this signature is the compile-time DX mirror.
|
|
2076
2076
|
*/
|
|
2077
2077
|
referencesAuthUser(onDelete) {
|
|
2078
|
+
refuseOnVector(this._def, "referencesAuthUser");
|
|
2078
2079
|
this._def.references = { table: "auth.users", column: "id" };
|
|
2079
2080
|
this._def.onDeleteAction = onDelete;
|
|
2080
2081
|
return new _ColumnBuilder(this._def.type, this._def);
|
|
@@ -2098,6 +2099,7 @@ var ColumnBuilder = class _ColumnBuilder {
|
|
|
2098
2099
|
* boundary; this signature is the compile-time DX mirror.
|
|
2099
2100
|
*/
|
|
2100
2101
|
referencesInstallation(onDelete) {
|
|
2102
|
+
refuseOnVector(this._def, "referencesInstallation");
|
|
2101
2103
|
this._def.references = { table: "auth.installations", column: "id" };
|
|
2102
2104
|
this._def.onDeleteAction = onDelete;
|
|
2103
2105
|
return new _ColumnBuilder(this._def.type, this._def);
|
|
@@ -2109,6 +2111,7 @@ var ColumnBuilder = class _ColumnBuilder {
|
|
|
2109
2111
|
}
|
|
2110
2112
|
/** Add a single-column UNIQUE constraint. */
|
|
2111
2113
|
unique() {
|
|
2114
|
+
refuseOnVector(this._def, "unique");
|
|
2112
2115
|
this._def.unique = true;
|
|
2113
2116
|
return new _ColumnBuilder(this._def.type, this._def);
|
|
2114
2117
|
}
|
|
@@ -2143,12 +2146,36 @@ function enumType(name, values) {
|
|
|
2143
2146
|
builder._def.enumValues = [...values];
|
|
2144
2147
|
return builder;
|
|
2145
2148
|
}
|
|
2149
|
+
function vector(dimensions) {
|
|
2150
|
+
if (!Number.isInteger(dimensions) || dimensions < 1 || dimensions > 2e3) {
|
|
2151
|
+
throw new Error(`vector(): dimensions must be an integer in [1, 2000], got ${String(dimensions)}`);
|
|
2152
|
+
}
|
|
2153
|
+
const b = new ColumnBuilder("vector");
|
|
2154
|
+
b._def.dimensions = dimensions;
|
|
2155
|
+
return b;
|
|
2156
|
+
}
|
|
2146
2157
|
|
|
2147
2158
|
// src/db/raw.ts
|
|
2148
2159
|
function raw(name, up, opts) {
|
|
2149
2160
|
return { name, up, ...opts?.down != null ? { down: opts.down } : {} };
|
|
2150
2161
|
}
|
|
2151
2162
|
|
|
2163
|
+
// src/db/embedding.ts
|
|
2164
|
+
var openai = {
|
|
2165
|
+
embedding(model, opts) {
|
|
2166
|
+
return {
|
|
2167
|
+
provider: "openai",
|
|
2168
|
+
model,
|
|
2169
|
+
apiKeyName: opts?.apiKeyName ?? "OPENAI_API_KEY",
|
|
2170
|
+
...opts?.dimensions !== void 0 ? { dimensions: opts.dimensions } : {},
|
|
2171
|
+
...opts?.baseURL !== void 0 ? { baseURL: opts.baseURL } : {}
|
|
2172
|
+
};
|
|
2173
|
+
},
|
|
2174
|
+
chat(model) {
|
|
2175
|
+
return { provider: "openai", model };
|
|
2176
|
+
}
|
|
2177
|
+
};
|
|
2178
|
+
|
|
2152
2179
|
// src/db/typed-db.ts
|
|
2153
2180
|
function makeTypedTable(name, raw2) {
|
|
2154
2181
|
return {
|
|
@@ -2208,6 +2235,11 @@ function baseTsType(def) {
|
|
|
2208
2235
|
return "boolean";
|
|
2209
2236
|
case "jsonb":
|
|
2210
2237
|
return "unknown";
|
|
2238
|
+
// FR-008: the authoring type of a pgvector column. The default `unknown`
|
|
2239
|
+
// branch below STAYS — an unknown wire type is refused on the Go side
|
|
2240
|
+
// (FR-005), never papered over here.
|
|
2241
|
+
case "vector":
|
|
2242
|
+
return "number[]";
|
|
2211
2243
|
case "enum": {
|
|
2212
2244
|
const values = def.enumValues ?? [];
|
|
2213
2245
|
if (values.length === 0) return "string";
|
|
@@ -2241,10 +2273,10 @@ function buildChildEdges(schema) {
|
|
|
2241
2273
|
const ref = builder._def.references;
|
|
2242
2274
|
if (ref === void 0) continue;
|
|
2243
2275
|
if (ref.table === childName) continue;
|
|
2244
|
-
const
|
|
2245
|
-
if (
|
|
2246
|
-
if (
|
|
2247
|
-
|
|
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 });
|
|
2248
2280
|
}
|
|
2249
2281
|
}
|
|
2250
2282
|
return edges;
|
|
@@ -2289,6 +2321,10 @@ function tableBlock(table, children, indent) {
|
|
|
2289
2321
|
`${indent} };`,
|
|
2290
2322
|
`${indent} owner: ${owner === null ? "null" : JSON.stringify(owner)};`,
|
|
2291
2323
|
`${indent} children: {${childEntries === "" ? "" : ` ${childEntries} `}};`,
|
|
2324
|
+
// searchable: vector kolonu YA DA search beyanı → EnvTypedTable'da search()
|
|
2325
|
+
// üyesini açan yapısal bayrak (FR-013). Yokken satır hiç üretilmez ki
|
|
2326
|
+
// mevcut şemaların d.ts'i bayt-aynı kalsın.
|
|
2327
|
+
...cols.some(([, b]) => b._def.type === "vector") || table.search !== void 0 ? [`${indent} searchable: true;`] : [],
|
|
2292
2328
|
`${indent}};`
|
|
2293
2329
|
].join("\n");
|
|
2294
2330
|
}
|
|
@@ -2314,638 +2350,50 @@ export {};
|
|
|
2314
2350
|
`;
|
|
2315
2351
|
}
|
|
2316
2352
|
|
|
2317
|
-
// src/
|
|
2318
|
-
|
|
2319
|
-
|
|
2320
|
-
var VARIANT_FITS = ["cover", "contain", "inside"];
|
|
2321
|
-
function normalizeVariants(input) {
|
|
2322
|
-
if (input === null || typeof input !== "object" || Array.isArray(input)) {
|
|
2323
|
-
throw new Error("bucket variants must be an object of { <name>: { width?, height?, format? } }");
|
|
2324
|
-
}
|
|
2325
|
-
const out = {};
|
|
2326
|
-
for (const [name, spec] of Object.entries(input)) {
|
|
2327
|
-
if (!/^[a-z0-9][a-z0-9_-]*$/i.test(name)) {
|
|
2328
|
-
throw new Error(
|
|
2329
|
-
`bucket variant name "${name}" must be alphanumeric with optional - or _ (e.g. "thumb", "card_2x")`
|
|
2330
|
-
);
|
|
2331
|
-
}
|
|
2332
|
-
if (spec === null || typeof spec !== "object" || Array.isArray(spec)) {
|
|
2333
|
-
throw new Error(`bucket variant "${name}" must be an object like { width: 100, height: 100 }`);
|
|
2334
|
-
}
|
|
2335
|
-
for (const dim of ["width", "height"]) {
|
|
2336
|
-
const value = spec[dim];
|
|
2337
|
-
if (value === void 0) continue;
|
|
2338
|
-
if (typeof value !== "number" || !Number.isInteger(value) || value <= 0) {
|
|
2339
|
-
throw new Error(`bucket variant "${name}" ${dim} must be a positive integer, got ${String(value)}`);
|
|
2340
|
-
}
|
|
2341
|
-
}
|
|
2342
|
-
if (spec.width === void 0 && spec.height === void 0) {
|
|
2343
|
-
throw new Error(`bucket variant "${name}" must set width and/or height`);
|
|
2344
|
-
}
|
|
2345
|
-
if (spec.format !== void 0 && !VARIANT_FORMATS.includes(spec.format)) {
|
|
2346
|
-
throw new Error(
|
|
2347
|
-
`bucket variant "${name}" format must be one of ${VARIANT_FORMATS.join(", ")}, got "${String(spec.format)}"`
|
|
2348
|
-
);
|
|
2349
|
-
}
|
|
2350
|
-
if (spec.fit !== void 0 && !VARIANT_FITS.includes(spec.fit)) {
|
|
2351
|
-
throw new Error(
|
|
2352
|
-
`bucket variant "${name}" fit must be one of ${VARIANT_FITS.join(", ")}, got "${String(spec.fit)}"`
|
|
2353
|
-
);
|
|
2354
|
-
}
|
|
2355
|
-
if (spec.quality !== void 0) {
|
|
2356
|
-
const q = spec.quality;
|
|
2357
|
-
if (typeof q !== "number" || !Number.isInteger(q) || q < 1 || q > 100) {
|
|
2358
|
-
throw new Error(`bucket variant "${name}" quality must be an integer 1\u2013100, got ${String(q)}`);
|
|
2359
|
-
}
|
|
2360
|
-
}
|
|
2361
|
-
if (spec.fit === "cover" && (spec.width === void 0 || spec.height === void 0)) {
|
|
2362
|
-
throw new Error(`bucket variant "${name}" uses fit "cover", which needs BOTH width and height`);
|
|
2363
|
-
}
|
|
2364
|
-
out[name] = {
|
|
2365
|
-
...spec.width !== void 0 ? { width: spec.width } : {},
|
|
2366
|
-
...spec.height !== void 0 ? { height: spec.height } : {},
|
|
2367
|
-
...spec.fit !== void 0 ? { fit: spec.fit } : {},
|
|
2368
|
-
...spec.format !== void 0 ? { format: spec.format } : {},
|
|
2369
|
-
...spec.quality !== void 0 ? { quality: spec.quality } : {}
|
|
2370
|
-
};
|
|
2371
|
-
}
|
|
2372
|
-
return out;
|
|
2353
|
+
// src/stack-gen.ts
|
|
2354
|
+
function liveNames(names) {
|
|
2355
|
+
return [...new Set(names)].sort();
|
|
2373
2356
|
}
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
kb: 1024,
|
|
2377
|
-
mb: 1024 ** 2,
|
|
2378
|
-
gb: 1024 ** 3,
|
|
2379
|
-
tb: 1024 ** 4
|
|
2380
|
-
};
|
|
2381
|
-
function parseFileSizeLimit(input) {
|
|
2382
|
-
if (typeof input === "number") {
|
|
2383
|
-
if (!Number.isFinite(input) || input < 0 || !Number.isInteger(input)) {
|
|
2384
|
-
throw new Error(
|
|
2385
|
-
`bucket fileSizeLimit must be a non-negative integer number of bytes, got ${input}`
|
|
2386
|
-
);
|
|
2387
|
-
}
|
|
2388
|
-
return input;
|
|
2389
|
-
}
|
|
2390
|
-
const trimmed = input.trim();
|
|
2391
|
-
const match = /^(\d+(?:\.\d+)?)\s*([a-zA-Z]+)?$/.exec(trimmed);
|
|
2392
|
-
if (!match) {
|
|
2393
|
-
throw new Error(
|
|
2394
|
-
`bucket fileSizeLimit string must be "<number><unit>" like "5MB" or "1GB", got "${input}"`
|
|
2395
|
-
);
|
|
2396
|
-
}
|
|
2397
|
-
const value = Number.parseFloat(match[1]);
|
|
2398
|
-
const unit = (match[2] ?? "b").toLowerCase();
|
|
2399
|
-
const multiplier = UNIT_BYTES[unit];
|
|
2400
|
-
if (multiplier === void 0) {
|
|
2401
|
-
throw new Error(
|
|
2402
|
-
`bucket fileSizeLimit has an unknown unit "${match[2]}" \u2014 use B, KB, MB, GB, or TB (e.g. "5MB")`
|
|
2403
|
-
);
|
|
2404
|
-
}
|
|
2405
|
-
const bytes = value * multiplier;
|
|
2406
|
-
if (!Number.isFinite(bytes) || bytes < 0) {
|
|
2407
|
-
throw new Error(`bucket fileSizeLimit resolved to an invalid byte count: ${bytes}`);
|
|
2408
|
-
}
|
|
2409
|
-
return Math.round(bytes);
|
|
2357
|
+
function memberName(name) {
|
|
2358
|
+
return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(name) ? name : JSON.stringify(name);
|
|
2410
2359
|
}
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
if (!Array.isArray(opts.allowedMimeTypes)) {
|
|
2417
|
-
throw new Error("bucket allowedMimeTypes must be an array of MIME-type strings");
|
|
2418
|
-
}
|
|
2419
|
-
for (const mime of opts.allowedMimeTypes) {
|
|
2420
|
-
if (typeof mime !== "string" || !MIME_RE.test(mime.trim())) {
|
|
2421
|
-
throw new Error(
|
|
2422
|
-
`bucket allowedMimeTypes entry "${mime}" is not a valid MIME type (expected "type/subtype", e.g. "image/png")`
|
|
2423
|
-
);
|
|
2424
|
-
}
|
|
2425
|
-
}
|
|
2426
|
-
allowedMimeTypes = [...new Set(opts.allowedMimeTypes.map((m) => m.trim()))];
|
|
2427
|
-
}
|
|
2428
|
-
return {
|
|
2429
|
-
public: opts.public ?? false,
|
|
2430
|
-
fileSizeLimit,
|
|
2431
|
-
allowedMimeTypes,
|
|
2432
|
-
variants: opts.variants === void 0 ? null : normalizeVariants(opts.variants)
|
|
2433
|
-
};
|
|
2360
|
+
function members(names) {
|
|
2361
|
+
if (names.length === 0) return "";
|
|
2362
|
+
return `
|
|
2363
|
+
${names.map((n) => ` ${memberName(n)}: true;`).join("\n")}
|
|
2364
|
+
`;
|
|
2434
2365
|
}
|
|
2435
|
-
function
|
|
2436
|
-
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
const
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
|
|
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
|
+
`;
|
|
2449
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.
|
|
2450
2386
|
|
|
2451
|
-
|
|
2452
|
-
|
|
2453
|
-
var EGRESS_TIMEOUT_MIN_MS = 1e3;
|
|
2454
|
-
var EGRESS_TIMEOUT_MAX_MS = 3e5;
|
|
2455
|
-
var EGRESS_TIMEOUT_DEFAULT_MS = 3e4;
|
|
2456
|
-
function defineEgress(input) {
|
|
2457
|
-
if (input === null || typeof input !== "object" || !Array.isArray(input.hosts)) {
|
|
2458
|
-
throw new Error("defineEgress expects { hosts: string[] }");
|
|
2459
|
-
}
|
|
2460
|
-
const hosts = [];
|
|
2461
|
-
for (const h of input.hosts) {
|
|
2462
|
-
if (typeof h !== "string" || h.trim().length === 0) {
|
|
2463
|
-
throw new Error("defineEgress: every host must be a non-empty string");
|
|
2464
|
-
}
|
|
2465
|
-
hosts.push(h.trim().toLowerCase());
|
|
2466
|
-
}
|
|
2467
|
-
if (input.timeoutMs === void 0) {
|
|
2468
|
-
return { __config: EGRESS_CONFIG_KIND, hosts };
|
|
2469
|
-
}
|
|
2470
|
-
const timeoutMs = input.timeoutMs;
|
|
2471
|
-
if (typeof timeoutMs !== "number" || !Number.isInteger(timeoutMs)) {
|
|
2472
|
-
throw new Error("defineEgress: timeoutMs must be an integer number of milliseconds");
|
|
2473
|
-
}
|
|
2474
|
-
if (timeoutMs < EGRESS_TIMEOUT_MIN_MS || timeoutMs > EGRESS_TIMEOUT_MAX_MS) {
|
|
2475
|
-
throw new Error(
|
|
2476
|
-
`defineEgress: timeoutMs must be between ${EGRESS_TIMEOUT_MIN_MS} and ${EGRESS_TIMEOUT_MAX_MS} (got ${timeoutMs})`
|
|
2477
|
-
);
|
|
2478
|
-
}
|
|
2479
|
-
return { __config: EGRESS_CONFIG_KIND, hosts, timeoutMs };
|
|
2480
|
-
}
|
|
2387
|
+
declare module "@palbase/backend/stack" {
|
|
2388
|
+
interface Secrets {${members(secrets)}}
|
|
2481
2389
|
|
|
2482
|
-
|
|
2483
|
-
var NOTIFICATIONS_CONFIG_KIND = "notifications";
|
|
2484
|
-
var RESERVED_SECRET_PREFIX = "PB_NOTIFICATIONS";
|
|
2485
|
-
var PROVIDER_CATALOG = {
|
|
2486
|
-
apns: {
|
|
2487
|
-
channel: "push",
|
|
2488
|
-
required: ["teamId", "keyId", "bundleId"],
|
|
2489
|
-
optional: ["isProduction"],
|
|
2490
|
-
secrets: ["p8"]
|
|
2491
|
-
},
|
|
2492
|
-
fcm: {
|
|
2493
|
-
channel: "push",
|
|
2494
|
-
required: [],
|
|
2495
|
-
optional: [],
|
|
2496
|
-
secrets: ["serviceAccount"]
|
|
2497
|
-
},
|
|
2498
|
-
sendgrid: {
|
|
2499
|
-
channel: "email",
|
|
2500
|
-
required: ["fromDomain"],
|
|
2501
|
-
optional: [],
|
|
2502
|
-
secrets: ["apiKey"]
|
|
2503
|
-
},
|
|
2504
|
-
ses: {
|
|
2505
|
-
channel: "email",
|
|
2506
|
-
required: ["region", "accessKeyId", "fromDomain"],
|
|
2507
|
-
optional: [],
|
|
2508
|
-
secrets: ["secretAccessKey"]
|
|
2509
|
-
},
|
|
2510
|
-
smtp: {
|
|
2511
|
-
channel: "email",
|
|
2512
|
-
required: ["host", "port", "fromEmail"],
|
|
2513
|
-
optional: ["username", "useStarttls"],
|
|
2514
|
-
secrets: ["password"]
|
|
2515
|
-
},
|
|
2516
|
-
acs: {
|
|
2517
|
-
channel: "email",
|
|
2518
|
-
required: ["fromEmail"],
|
|
2519
|
-
optional: ["fromName"],
|
|
2520
|
-
secrets: ["connectionString"]
|
|
2521
|
-
},
|
|
2522
|
-
twilio: {
|
|
2523
|
-
channel: "sms",
|
|
2524
|
-
// account_sid required; one of from_number / messaging_service_sid required
|
|
2525
|
-
// (enforced by the refine in buildProvider, not by the flat `required` list).
|
|
2526
|
-
required: ["accountSid"],
|
|
2527
|
-
optional: ["fromNumber", "messagingServiceSid"],
|
|
2528
|
-
secrets: ["authToken"]
|
|
2529
|
-
}
|
|
2530
|
-
};
|
|
2531
|
-
var DEFAULT_TEMPLATE_LOCALE = "en";
|
|
2532
|
-
function extractVariables(...sources) {
|
|
2533
|
-
const found = /* @__PURE__ */ new Set();
|
|
2534
|
-
for (const src of sources) {
|
|
2535
|
-
if (!src) continue;
|
|
2536
|
-
for (const match of src.matchAll(/\{\{\{?\s*([^{}]+?)\s*\}?\}\}/g)) {
|
|
2537
|
-
const token = (match[1] ?? "").trim();
|
|
2538
|
-
if (token === "" || "#/^!>&".includes(token.charAt(0)) || token === "else") continue;
|
|
2539
|
-
if (/\s/.test(token)) continue;
|
|
2540
|
-
const root = token.split(".")[0] ?? "";
|
|
2541
|
-
if (root === "" || root === "this" || root.startsWith("@")) continue;
|
|
2542
|
-
found.add(root);
|
|
2543
|
-
}
|
|
2544
|
-
}
|
|
2545
|
-
return [...found].sort();
|
|
2546
|
-
}
|
|
2547
|
-
function buildEmailTemplate(slug, opts) {
|
|
2548
|
-
const row = (locale, c) => {
|
|
2549
|
-
if (!c.subject) throw new Error(`notifications: email template "${slug}" (${locale}) needs a subject`);
|
|
2550
|
-
if (!c.html) throw new Error(`notifications: email template "${slug}" (${locale}) needs an html body`);
|
|
2551
|
-
const def = {
|
|
2552
|
-
slug,
|
|
2553
|
-
locale,
|
|
2554
|
-
subject: c.subject,
|
|
2555
|
-
html_body: c.html,
|
|
2556
|
-
variables: c.variables ? [...c.variables] : extractVariables(c.subject, c.html, c.text)
|
|
2557
|
-
};
|
|
2558
|
-
if (c.text !== void 0) def.text_body = c.text;
|
|
2559
|
-
return def;
|
|
2560
|
-
};
|
|
2561
|
-
const rows = [row(opts.locale ?? DEFAULT_TEMPLATE_LOCALE, opts)];
|
|
2562
|
-
for (const [locale, content] of Object.entries(opts.locales ?? {})) {
|
|
2563
|
-
rows.push(row(locale, content));
|
|
2564
|
-
}
|
|
2565
|
-
return rows;
|
|
2566
|
-
}
|
|
2567
|
-
function buildSmsTemplate(slug, opts) {
|
|
2568
|
-
const row = (locale, c) => {
|
|
2569
|
-
if (!c.body) throw new Error(`notifications: sms template "${slug}" (${locale}) needs a body`);
|
|
2570
|
-
return {
|
|
2571
|
-
slug,
|
|
2572
|
-
locale,
|
|
2573
|
-
body: c.body,
|
|
2574
|
-
variables: c.variables ? [...c.variables] : extractVariables(c.body)
|
|
2575
|
-
};
|
|
2576
|
-
};
|
|
2577
|
-
const rows = [row(opts.locale ?? DEFAULT_TEMPLATE_LOCALE, opts)];
|
|
2578
|
-
for (const [locale, content] of Object.entries(opts.locales ?? {})) {
|
|
2579
|
-
rows.push(row(locale, content));
|
|
2580
|
-
}
|
|
2581
|
-
return rows;
|
|
2582
|
-
}
|
|
2583
|
-
function buildTemplates(input) {
|
|
2584
|
-
const out = { email: [], sms: [] };
|
|
2585
|
-
const seen = /* @__PURE__ */ new Set();
|
|
2586
|
-
const claim = (channel, slug, locale) => {
|
|
2587
|
-
const key = `${channel}/${slug}/${locale}`;
|
|
2588
|
-
if (seen.has(key)) {
|
|
2589
|
-
throw new Error(`notifications: ${channel} template "${slug}" declares locale "${locale}" twice`);
|
|
2590
|
-
}
|
|
2591
|
-
seen.add(key);
|
|
2592
|
-
};
|
|
2593
|
-
for (const [slug, opts] of Object.entries(input.email ?? {})) {
|
|
2594
|
-
for (const row of buildEmailTemplate(slug, opts)) {
|
|
2595
|
-
claim("email", row.slug, row.locale);
|
|
2596
|
-
out.email.push(row);
|
|
2597
|
-
}
|
|
2598
|
-
}
|
|
2599
|
-
for (const [slug, opts] of Object.entries(input.sms ?? {})) {
|
|
2600
|
-
for (const row of buildSmsTemplate(slug, opts)) {
|
|
2601
|
-
claim("sms", row.slug, row.locale);
|
|
2602
|
-
out.sms.push(row);
|
|
2603
|
-
}
|
|
2604
|
-
}
|
|
2605
|
-
return out;
|
|
2606
|
-
}
|
|
2607
|
-
function buildProvider(name, opts) {
|
|
2608
|
-
const entry = PROVIDER_CATALOG[name];
|
|
2609
|
-
const src = { ...opts };
|
|
2610
|
-
const enabled = src.enabled === void 0 ? false : Boolean(src.enabled);
|
|
2611
|
-
if (!enabled) {
|
|
2612
|
-
return { enabled: false };
|
|
2613
|
-
}
|
|
2614
|
-
const def = { enabled: true };
|
|
2615
|
-
for (const field of entry.required) {
|
|
2616
|
-
const value = src[field];
|
|
2617
|
-
if (value === void 0 || value === null || value === "") {
|
|
2618
|
-
throw new Error(
|
|
2619
|
-
`notifications: provider "${name}" is enabled but missing required field "${field}"`
|
|
2620
|
-
);
|
|
2621
|
-
}
|
|
2622
|
-
def[field] = value;
|
|
2623
|
-
}
|
|
2624
|
-
for (const field of entry.optional) {
|
|
2625
|
-
if (src[field] !== void 0) {
|
|
2626
|
-
def[field] = src[field];
|
|
2627
|
-
}
|
|
2628
|
-
}
|
|
2629
|
-
if (name === "twilio") {
|
|
2630
|
-
const hasFrom = Boolean(def.fromNumber);
|
|
2631
|
-
const hasMsg = Boolean(def.messagingServiceSid);
|
|
2632
|
-
if (!hasFrom && !hasMsg) {
|
|
2633
|
-
throw new Error(
|
|
2634
|
-
'notifications: provider "twilio" requires one of "fromNumber" or "messagingServiceSid"'
|
|
2635
|
-
);
|
|
2636
|
-
}
|
|
2637
|
-
}
|
|
2638
|
-
return def;
|
|
2639
|
-
}
|
|
2640
|
-
function defineNotifications(input) {
|
|
2641
|
-
if (input === null || typeof input !== "object") {
|
|
2642
|
-
throw new Error("defineNotifications expects { push?, email?, sms? }");
|
|
2643
|
-
}
|
|
2644
|
-
const config = {
|
|
2645
|
-
__config: NOTIFICATIONS_CONFIG_KIND,
|
|
2646
|
-
push: {},
|
|
2647
|
-
email: {},
|
|
2648
|
-
sms: {},
|
|
2649
|
-
templates: buildTemplates(input.templates ?? {})
|
|
2650
|
-
};
|
|
2651
|
-
const push = input.push ?? {};
|
|
2652
|
-
if (push.apns !== void 0) config.push.apns = buildProvider("apns", push.apns);
|
|
2653
|
-
if (push.fcm !== void 0) config.push.fcm = buildProvider("fcm", push.fcm);
|
|
2654
|
-
const email = input.email ?? {};
|
|
2655
|
-
if (email.sendgrid !== void 0) config.email.sendgrid = buildProvider("sendgrid", email.sendgrid);
|
|
2656
|
-
if (email.ses !== void 0) config.email.ses = buildProvider("ses", email.ses);
|
|
2657
|
-
if (email.smtp !== void 0) config.email.smtp = buildProvider("smtp", email.smtp);
|
|
2658
|
-
if (email.acs !== void 0) config.email.acs = buildProvider("acs", email.acs);
|
|
2659
|
-
const sms = input.sms ?? {};
|
|
2660
|
-
if (sms.twilio !== void 0) config.sms.twilio = buildProvider("twilio", sms.twilio);
|
|
2661
|
-
return config;
|
|
2662
|
-
}
|
|
2663
|
-
function reservedSecretKey(provider, secretField) {
|
|
2664
|
-
return `${RESERVED_SECRET_PREFIX}_${camelToUpperSnake(provider)}_${camelToUpperSnake(secretField)}`;
|
|
2665
|
-
}
|
|
2666
|
-
function camelToUpperSnake(s) {
|
|
2667
|
-
return s.replace(/([a-z0-9])([A-Z])/g, "$1_$2").toUpperCase();
|
|
2668
|
-
}
|
|
2390
|
+
interface Flags {${members(flags)}}
|
|
2669
2391
|
|
|
2670
|
-
|
|
2671
|
-
var TEST_USERS_CONFIG_KIND = "test-users";
|
|
2672
|
-
var TEST_USER_NAME_RE = /^[a-zA-Z][a-zA-Z0-9_]*$/;
|
|
2673
|
-
var EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
2674
|
-
var E164_RE = /^\+[1-9]\d{1,14}$/;
|
|
2675
|
-
var OTP_LENGTH = 6;
|
|
2676
|
-
var OTP_RE = /^\d{6}$/;
|
|
2677
|
-
var MIN_PASSWORD_LENGTH = 8;
|
|
2678
|
-
function isPlainObject2(value) {
|
|
2679
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2680
|
-
}
|
|
2681
|
-
function normalizeSeed(seed, userName) {
|
|
2682
|
-
if (seed === void 0) return {};
|
|
2683
|
-
if (!isPlainObject2(seed)) {
|
|
2684
|
-
throw new Error(`testUser ${JSON.stringify(userName)}: seed must be { <table>: [ {...} ] }`);
|
|
2685
|
-
}
|
|
2686
|
-
const out = {};
|
|
2687
|
-
for (const [table, rows] of Object.entries(seed)) {
|
|
2688
|
-
if (!Array.isArray(rows)) {
|
|
2689
|
-
throw new Error(
|
|
2690
|
-
`testUser ${JSON.stringify(userName)}: seed.${table} must be an array of rows`
|
|
2691
|
-
);
|
|
2692
|
-
}
|
|
2693
|
-
if (rows.length === 0) {
|
|
2694
|
-
throw new Error(
|
|
2695
|
-
`testUser ${JSON.stringify(userName)}: seed.${table} is empty \u2014 remove it or add a row`
|
|
2696
|
-
);
|
|
2697
|
-
}
|
|
2698
|
-
for (const row of rows) {
|
|
2699
|
-
if (!isPlainObject2(row)) {
|
|
2700
|
-
throw new Error(
|
|
2701
|
-
`testUser ${JSON.stringify(userName)}: every row in seed.${table} must be an object`
|
|
2702
|
-
);
|
|
2703
|
-
}
|
|
2704
|
-
}
|
|
2705
|
-
out[table] = rows;
|
|
2706
|
-
}
|
|
2707
|
-
return out;
|
|
2708
|
-
}
|
|
2709
|
-
function testUser(opts = {}) {
|
|
2710
|
-
if (!isPlainObject2(opts)) {
|
|
2711
|
-
throw new Error("testUser() expects { email?, password?, phone?, otp?, seed? }");
|
|
2712
|
-
}
|
|
2713
|
-
const hasEmail = opts.email !== void 0;
|
|
2714
|
-
const hasPassword = opts.password !== void 0;
|
|
2715
|
-
if (hasEmail !== hasPassword) {
|
|
2716
|
-
throw new Error(
|
|
2717
|
-
"testUser() needs email AND password together (a fixture with stable credentials) or neither (a template the server generates credentials for)"
|
|
2718
|
-
);
|
|
2719
|
-
}
|
|
2720
|
-
let email = null;
|
|
2721
|
-
let password = null;
|
|
2722
|
-
if (hasEmail) {
|
|
2723
|
-
if (typeof opts.email !== "string" || !EMAIL_RE.test(opts.email)) {
|
|
2724
|
-
throw new Error(`testUser email ${JSON.stringify(opts.email)} is not a valid address`);
|
|
2725
|
-
}
|
|
2726
|
-
if (typeof opts.password !== "string" || opts.password.length < MIN_PASSWORD_LENGTH) {
|
|
2727
|
-
throw new Error(
|
|
2728
|
-
`testUser password must be at least ${MIN_PASSWORD_LENGTH} characters (got ${typeof opts.password === "string" ? opts.password.length : 0})`
|
|
2729
|
-
);
|
|
2730
|
-
}
|
|
2731
|
-
email = opts.email;
|
|
2732
|
-
password = opts.password;
|
|
2733
|
-
}
|
|
2734
|
-
const hasPhone = opts.phone !== void 0;
|
|
2735
|
-
const hasOTP = opts.otp !== void 0;
|
|
2736
|
-
if (hasPhone !== hasOTP) {
|
|
2737
|
-
throw new Error(
|
|
2738
|
-
"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"
|
|
2739
|
-
);
|
|
2740
|
-
}
|
|
2741
|
-
let phone = null;
|
|
2742
|
-
let otp = null;
|
|
2743
|
-
if (hasPhone) {
|
|
2744
|
-
if (typeof opts.phone !== "string" || !E164_RE.test(opts.phone)) {
|
|
2745
|
-
throw new Error(
|
|
2746
|
-
`testUser phone ${JSON.stringify(opts.phone)} must be E.164, e.g. "+905551112233"`
|
|
2747
|
-
);
|
|
2748
|
-
}
|
|
2749
|
-
if (typeof opts.otp !== "string" || !OTP_RE.test(opts.otp)) {
|
|
2750
|
-
throw new Error(
|
|
2751
|
-
`testUser otp ${JSON.stringify(opts.otp)} must be ${OTP_LENGTH} digits`
|
|
2752
|
-
);
|
|
2753
|
-
}
|
|
2754
|
-
phone = opts.phone;
|
|
2755
|
-
otp = opts.otp;
|
|
2756
|
-
}
|
|
2757
|
-
return { email, password, phone, otp, seed: normalizeSeed(opts.seed, email ?? "<template>") };
|
|
2758
|
-
}
|
|
2759
|
-
function defineTestUsers(input) {
|
|
2760
|
-
if (!isPlainObject2(input) || !isPlainObject2(input.users)) {
|
|
2761
|
-
throw new Error("defineTestUsers expects { users: { <name>: testUser({...}) } }");
|
|
2762
|
-
}
|
|
2763
|
-
const users = {};
|
|
2764
|
-
const emails = /* @__PURE__ */ new Map();
|
|
2765
|
-
for (const name of Object.keys(input.users)) {
|
|
2766
|
-
if (!TEST_USER_NAME_RE.test(name)) {
|
|
2767
|
-
throw new Error(
|
|
2768
|
-
`test user name ${JSON.stringify(name)} is invalid \u2014 must start with a letter and contain only letters, digits, and underscores`
|
|
2769
|
-
);
|
|
2770
|
-
}
|
|
2771
|
-
const def = input.users[name];
|
|
2772
|
-
if (def === void 0) continue;
|
|
2773
|
-
if (def.email !== null) {
|
|
2774
|
-
const lower = def.email.toLowerCase();
|
|
2775
|
-
const already = emails.get(lower);
|
|
2776
|
-
if (already !== void 0) {
|
|
2777
|
-
throw new Error(
|
|
2778
|
-
`test users ${JSON.stringify(already)} and ${JSON.stringify(name)} declare the same e-mail ${JSON.stringify(def.email)}`
|
|
2779
|
-
);
|
|
2780
|
-
}
|
|
2781
|
-
emails.set(lower, name);
|
|
2782
|
-
}
|
|
2783
|
-
users[name] = def;
|
|
2784
|
-
}
|
|
2785
|
-
return { __config: TEST_USERS_CONFIG_KIND, users };
|
|
2392
|
+
interface Buckets {${bucketMembers(names.buckets)}}
|
|
2786
2393
|
}
|
|
2787
2394
|
|
|
2788
|
-
|
|
2789
|
-
|
|
2790
|
-
var FLAG_KEY_RE = /^[a-zA-Z][a-zA-Z0-9_]*$/;
|
|
2791
|
-
function valueMatchesType(type, value) {
|
|
2792
|
-
switch (type) {
|
|
2793
|
-
case "boolean":
|
|
2794
|
-
return typeof value === "boolean";
|
|
2795
|
-
case "number":
|
|
2796
|
-
return typeof value === "number" && Number.isFinite(value);
|
|
2797
|
-
case "string":
|
|
2798
|
-
return typeof value === "string";
|
|
2799
|
-
case "json":
|
|
2800
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2801
|
-
}
|
|
2802
|
-
}
|
|
2803
|
-
function flag(opts) {
|
|
2804
|
-
if (opts === null || typeof opts !== "object") {
|
|
2805
|
-
throw new Error("flag() expects { type, default, variants?, description? }");
|
|
2806
|
-
}
|
|
2807
|
-
const { type } = opts;
|
|
2808
|
-
if (type !== "boolean" && type !== "number" && type !== "string" && type !== "json") {
|
|
2809
|
-
throw new Error(`flag type must be "boolean", "number", "string", or "json", got ${JSON.stringify(type)}`);
|
|
2810
|
-
}
|
|
2811
|
-
if (!valueMatchesType(type, opts.default)) {
|
|
2812
|
-
throw new Error(
|
|
2813
|
-
`flag default ${JSON.stringify(opts.default)} does not match type "${type}"`
|
|
2814
|
-
);
|
|
2815
|
-
}
|
|
2816
|
-
let variants = null;
|
|
2817
|
-
if (opts.variants !== void 0) {
|
|
2818
|
-
if (opts.type !== "string") {
|
|
2819
|
-
throw new Error(`flag variants are only valid for type "string" (got type "${type}")`);
|
|
2820
|
-
}
|
|
2821
|
-
if (!Array.isArray(opts.variants)) {
|
|
2822
|
-
throw new Error("flag variants must be an array of strings");
|
|
2823
|
-
}
|
|
2824
|
-
for (const v of opts.variants) {
|
|
2825
|
-
if (typeof v !== "string" || v.length === 0) {
|
|
2826
|
-
throw new Error(`flag variant ${JSON.stringify(v)} must be a non-empty string`);
|
|
2827
|
-
}
|
|
2828
|
-
}
|
|
2829
|
-
variants = [...new Set(opts.variants)];
|
|
2830
|
-
if (variants.length === 0) {
|
|
2831
|
-
throw new Error("flag variants must be a non-empty list when supplied");
|
|
2832
|
-
}
|
|
2833
|
-
if (!variants.includes(opts.default)) {
|
|
2834
|
-
throw new Error(
|
|
2835
|
-
`flag default ${JSON.stringify(opts.default)} is not one of its variants [${variants.map((v) => JSON.stringify(v)).join(", ")}]`
|
|
2836
|
-
);
|
|
2837
|
-
}
|
|
2838
|
-
}
|
|
2839
|
-
let conditions = null;
|
|
2840
|
-
if (opts.conditions !== void 0) {
|
|
2841
|
-
if (!Array.isArray(opts.conditions)) {
|
|
2842
|
-
throw new Error("flag conditions must be an array of { when, value }");
|
|
2843
|
-
}
|
|
2844
|
-
const compiled = [];
|
|
2845
|
-
for (const [i, branch] of opts.conditions.entries()) {
|
|
2846
|
-
if (branch === null || typeof branch !== "object") {
|
|
2847
|
-
throw new Error(`flag condition ${i} must be an object { when, value }`);
|
|
2848
|
-
}
|
|
2849
|
-
if (typeof branch.when !== "string" || branch.when.trim().length === 0) {
|
|
2850
|
-
throw new Error(`flag condition ${i} needs a non-empty "when" expression`);
|
|
2851
|
-
}
|
|
2852
|
-
if (!valueMatchesType(type, branch.value)) {
|
|
2853
|
-
throw new Error(
|
|
2854
|
-
`flag condition ${i} value ${JSON.stringify(branch.value)} does not match type "${type}"`
|
|
2855
|
-
);
|
|
2856
|
-
}
|
|
2857
|
-
if (variants !== null && !variants.includes(branch.value)) {
|
|
2858
|
-
throw new Error(
|
|
2859
|
-
`flag condition ${i} value ${JSON.stringify(branch.value)} is not one of its variants [${variants.map((v) => JSON.stringify(v)).join(", ")}]`
|
|
2860
|
-
);
|
|
2861
|
-
}
|
|
2862
|
-
compiled.push({ when: branch.when.trim(), value: branch.value });
|
|
2863
|
-
}
|
|
2864
|
-
conditions = compiled.length > 0 ? compiled : null;
|
|
2865
|
-
}
|
|
2866
|
-
let description = null;
|
|
2867
|
-
if (opts.description !== void 0) {
|
|
2868
|
-
if (typeof opts.description !== "string") {
|
|
2869
|
-
throw new Error("flag description must be a string");
|
|
2870
|
-
}
|
|
2871
|
-
const trimmed = opts.description.trim();
|
|
2872
|
-
description = trimmed.length > 0 ? trimmed : null;
|
|
2873
|
-
}
|
|
2874
|
-
return {
|
|
2875
|
-
type,
|
|
2876
|
-
default: opts.default,
|
|
2877
|
-
variants,
|
|
2878
|
-
conditions,
|
|
2879
|
-
description
|
|
2880
|
-
};
|
|
2881
|
-
}
|
|
2882
|
-
function defineFlags(input) {
|
|
2883
|
-
if (input === null || typeof input !== "object" || typeof input.flags !== "object") {
|
|
2884
|
-
throw new Error("defineFlags expects { flags: { <key>: flag({...}) } }");
|
|
2885
|
-
}
|
|
2886
|
-
const flags = {};
|
|
2887
|
-
for (const key of Object.keys(input.flags)) {
|
|
2888
|
-
if (!FLAG_KEY_RE.test(key)) {
|
|
2889
|
-
throw new Error(
|
|
2890
|
-
`flag key ${JSON.stringify(key)} is invalid \u2014 must start with a letter and contain only letters, digits, and underscores`
|
|
2891
|
-
);
|
|
2892
|
-
}
|
|
2893
|
-
const def = input.flags[key];
|
|
2894
|
-
if (def === void 0) continue;
|
|
2895
|
-
flags[key] = def;
|
|
2896
|
-
}
|
|
2897
|
-
return { __config: FLAGS_CONFIG_KIND, flags };
|
|
2898
|
-
}
|
|
2899
|
-
|
|
2900
|
-
// src/config/secrets.ts
|
|
2901
|
-
var SECRETS_CONFIG_KIND = "secrets";
|
|
2902
|
-
var SECRET_NAME_RE = /^[A-Z][A-Z0-9_]*$/;
|
|
2903
|
-
function secret(name, opts = {}) {
|
|
2904
|
-
if (typeof name !== "string" || !SECRET_NAME_RE.test(name)) {
|
|
2905
|
-
throw new Error(
|
|
2906
|
-
`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")`
|
|
2907
|
-
);
|
|
2908
|
-
}
|
|
2909
|
-
if (opts === null || typeof opts !== "object") {
|
|
2910
|
-
throw new Error(`secret("${name}") expects { required?, description? }`);
|
|
2911
|
-
}
|
|
2912
|
-
let required = true;
|
|
2913
|
-
if (opts.required !== void 0) {
|
|
2914
|
-
if (typeof opts.required !== "boolean") {
|
|
2915
|
-
throw new Error(`secret("${name}") required must be a boolean`);
|
|
2916
|
-
}
|
|
2917
|
-
required = opts.required;
|
|
2918
|
-
}
|
|
2919
|
-
let description = null;
|
|
2920
|
-
if (opts.description !== void 0) {
|
|
2921
|
-
if (typeof opts.description !== "string") {
|
|
2922
|
-
throw new Error(`secret("${name}") description must be a string`);
|
|
2923
|
-
}
|
|
2924
|
-
const trimmed = opts.description.trim();
|
|
2925
|
-
description = trimmed.length > 0 ? trimmed : null;
|
|
2926
|
-
}
|
|
2927
|
-
return { name, required, description };
|
|
2928
|
-
}
|
|
2929
|
-
function defineSecrets(input) {
|
|
2930
|
-
if (input === null || typeof input !== "object" || !Array.isArray(input.secrets)) {
|
|
2931
|
-
throw new Error('defineSecrets expects { secrets: [secret("NAME"), ...] }');
|
|
2932
|
-
}
|
|
2933
|
-
const byName = /* @__PURE__ */ new Map();
|
|
2934
|
-
for (const def of input.secrets) {
|
|
2935
|
-
if (def === null || typeof def !== "object" || typeof def.name !== "string" || typeof def.required !== "boolean") {
|
|
2936
|
-
throw new Error(
|
|
2937
|
-
`defineSecrets: every entry must come from secret() \u2014 got ${JSON.stringify(def)}`
|
|
2938
|
-
);
|
|
2939
|
-
}
|
|
2940
|
-
if (byName.has(def.name)) {
|
|
2941
|
-
throw new Error(
|
|
2942
|
-
`secret "${def.name}" is declared twice in config/secrets.ts \u2014 remove one; a name has exactly one requirement`
|
|
2943
|
-
);
|
|
2944
|
-
}
|
|
2945
|
-
byName.set(def.name, def);
|
|
2946
|
-
}
|
|
2947
|
-
const secrets = [...byName.values()].sort((a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0);
|
|
2948
|
-
return { __config: SECRETS_CONFIG_KIND, secrets };
|
|
2395
|
+
export {};
|
|
2396
|
+
`;
|
|
2949
2397
|
}
|
|
2950
2398
|
|
|
2951
2399
|
// src/decorators/registry.ts
|
|
@@ -2953,7 +2401,7 @@ var ROUTES = /* @__PURE__ */ Symbol.for("palbase.backend.routes");
|
|
|
2953
2401
|
var PARAM_BUFFER = /* @__PURE__ */ Symbol.for("palbase.backend.paramBuffer");
|
|
2954
2402
|
var RETURN_BUFFER = /* @__PURE__ */ Symbol.for("palbase.backend.returnBuffer");
|
|
2955
2403
|
var THROWS_BUFFER = /* @__PURE__ */ Symbol.for("palbase.backend.throwsBuffer");
|
|
2956
|
-
function
|
|
2404
|
+
function carrierOf(target) {
|
|
2957
2405
|
const ctor = typeof target === "function" ? target : target.constructor ?? target;
|
|
2958
2406
|
return ctor;
|
|
2959
2407
|
}
|
|
@@ -2970,7 +2418,7 @@ function ownParamBuffer(carrier) {
|
|
|
2970
2418
|
return carrier[PARAM_BUFFER];
|
|
2971
2419
|
}
|
|
2972
2420
|
function recordRoute(target, fnName, method, subpath, options) {
|
|
2973
|
-
const carrier =
|
|
2421
|
+
const carrier = carrierOf(target);
|
|
2974
2422
|
const routes = ownRoutes(carrier);
|
|
2975
2423
|
const buffer = ownParamBuffer(carrier);
|
|
2976
2424
|
const params = (buffer[fnName] ?? []).slice().sort((a, b) => a.index - b.index);
|
|
@@ -2986,7 +2434,7 @@ function recordRoute(target, fnName, method, subpath, options) {
|
|
|
2986
2434
|
routes.push(route);
|
|
2987
2435
|
}
|
|
2988
2436
|
function recordParam(target, fnName, meta) {
|
|
2989
|
-
const carrier =
|
|
2437
|
+
const carrier = carrierOf(target);
|
|
2990
2438
|
const buffer = ownParamBuffer(carrier);
|
|
2991
2439
|
(buffer[fnName] ??= []).push(meta);
|
|
2992
2440
|
const routes = carrier[ROUTES];
|
|
@@ -2999,7 +2447,7 @@ function recordParam(target, fnName, meta) {
|
|
|
2999
2447
|
}
|
|
3000
2448
|
}
|
|
3001
2449
|
function recordThrows(target, fnName, throws) {
|
|
3002
|
-
const carrier =
|
|
2450
|
+
const carrier = carrierOf(target);
|
|
3003
2451
|
const routes = carrier[ROUTES];
|
|
3004
2452
|
const route = routes?.find((r) => r.fnName === fnName);
|
|
3005
2453
|
if (route) {
|
|
@@ -3013,7 +2461,7 @@ function recordThrows(target, fnName, throws) {
|
|
|
3013
2461
|
if (throwsBuffer) throwsBuffer[fnName] = throws;
|
|
3014
2462
|
}
|
|
3015
2463
|
function getRoutes(ctor) {
|
|
3016
|
-
const carrier =
|
|
2464
|
+
const carrier = carrierOf(ctor);
|
|
3017
2465
|
const routes = carrier[ROUTES] ?? [];
|
|
3018
2466
|
const returnBuffer = carrier[RETURN_BUFFER];
|
|
3019
2467
|
if (returnBuffer) {
|
|
@@ -3122,12 +2570,12 @@ var Query = makeMethodDecorator("QUERY");
|
|
|
3122
2570
|
// src/decorators/webhook.ts
|
|
3123
2571
|
var WEBHOOK_META = /* @__PURE__ */ Symbol.for("palbase.backend.webhookMeta");
|
|
3124
2572
|
var WEBHOOK_EVENTS = /* @__PURE__ */ Symbol.for("palbase.backend.webhookEvents");
|
|
3125
|
-
function
|
|
2573
|
+
function carrierOf2(ctor) {
|
|
3126
2574
|
return ctor;
|
|
3127
2575
|
}
|
|
3128
2576
|
function Webhook(options) {
|
|
3129
2577
|
return function(ctor) {
|
|
3130
|
-
const carrier =
|
|
2578
|
+
const carrier = carrierOf2(ctor);
|
|
3131
2579
|
Object.defineProperty(carrier, WEBHOOK_META, {
|
|
3132
2580
|
value: options,
|
|
3133
2581
|
enumerable: false,
|
|
@@ -3145,7 +2593,7 @@ function Webhook(options) {
|
|
|
3145
2593
|
}
|
|
3146
2594
|
function On(event) {
|
|
3147
2595
|
return function(target, fnName) {
|
|
3148
|
-
const carrier =
|
|
2596
|
+
const carrier = carrierOf2(target.constructor);
|
|
3149
2597
|
const existing = carrier[WEBHOOK_EVENTS];
|
|
3150
2598
|
const entries = existing ? [...existing] : [];
|
|
3151
2599
|
entries.push({ event, fnName: String(fnName) });
|
|
@@ -3158,7 +2606,7 @@ function On(event) {
|
|
|
3158
2606
|
};
|
|
3159
2607
|
}
|
|
3160
2608
|
function getWebhookConfig(ctor) {
|
|
3161
|
-
const carrier =
|
|
2609
|
+
const carrier = carrierOf2(ctor);
|
|
3162
2610
|
const meta = carrier[WEBHOOK_META];
|
|
3163
2611
|
const entries = carrier[WEBHOOK_EVENTS] ?? [];
|
|
3164
2612
|
if (!meta) {
|
|
@@ -3293,22 +2741,14 @@ function validateUploadConfigShape(c) {
|
|
|
3293
2741
|
if (c === null || typeof c !== "object") {
|
|
3294
2742
|
throw new Error("@Upload config must be an object { bucket, pathTemplate, ... }");
|
|
3295
2743
|
}
|
|
3296
|
-
|
|
2744
|
+
const bucket = c.bucket;
|
|
2745
|
+
if (typeof bucket !== "string" || bucket.length === 0) {
|
|
3297
2746
|
throw new Error("@Upload config.bucket must be a non-empty bucket name");
|
|
3298
2747
|
}
|
|
3299
2748
|
if (typeof c.pathTemplate !== "string" || c.pathTemplate.length === 0) {
|
|
3300
2749
|
throw new Error("@Upload config.pathTemplate must be a non-empty key template");
|
|
3301
2750
|
}
|
|
3302
2751
|
}
|
|
3303
|
-
function validateUploadAgainstStorage(uploadConfig, storage, routeLabel) {
|
|
3304
|
-
const def = storage.buckets[uploadConfig.bucket];
|
|
3305
|
-
if (def === void 0) {
|
|
3306
|
-
const known = Object.keys(storage.buckets);
|
|
3307
|
-
throw new Error(
|
|
3308
|
-
`@Upload route ${routeLabel} targets bucket "${uploadConfig.bucket}" which is not declared in defineStorage(...). ` + (known.length ? `Known buckets: ${known.join(", ")}.` : "No buckets are declared.")
|
|
3309
|
-
);
|
|
3310
|
-
}
|
|
3311
|
-
}
|
|
3312
2752
|
|
|
3313
2753
|
// src/decorators/params.ts
|
|
3314
2754
|
function makeParamDecorator(kind, extra) {
|
|
@@ -3357,6 +2797,93 @@ function defineMiddleware(fn) {
|
|
|
3357
2797
|
return fn;
|
|
3358
2798
|
}
|
|
3359
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
|
+
|
|
3360
2887
|
// src/error-registry.ts
|
|
3361
2888
|
var import_zod_to_openapi = require("@asteasolutions/zod-to-openapi");
|
|
3362
2889
|
var import_zod = require("zod");
|
|
@@ -3366,27 +2893,7 @@ var BUILTIN_DATA_SCHEMAS = {
|
|
|
3366
2893
|
bad_request: import_zod.z.object({
|
|
3367
2894
|
fields: import_zod.z.array(import_zod.z.object({ field: import_zod.z.string(), message: import_zod.z.string() }))
|
|
3368
2895
|
}),
|
|
3369
|
-
too_many_requests: import_zod.z.object({ retryAfter: import_zod.z.number().int() })
|
|
3370
|
-
// Purchases (@RequireEntitlement / @Spend). Their payloads are what make a
|
|
3371
|
-
// paywall renderable on the client: WHICH entitlement was missing, and for a
|
|
3372
|
-
// spend the real ceiling and reset time rather than a guessed retry delay.
|
|
3373
|
-
entitlement_required: import_zod.z.object({ entitlement: import_zod.z.string().nullable() }),
|
|
3374
|
-
quota_exceeded: import_zod.z.object({
|
|
3375
|
-
key: import_zod.z.string(),
|
|
3376
|
-
scope: import_zod.z.string(),
|
|
3377
|
-
window: import_zod.z.string(),
|
|
3378
|
-
used: import_zod.z.number().int(),
|
|
3379
|
-
reserved: import_zod.z.number().int(),
|
|
3380
|
-
max: import_zod.z.number().int(),
|
|
3381
|
-
remaining: import_zod.z.number().int(),
|
|
3382
|
-
resetAt: import_zod.z.string()
|
|
3383
|
-
}),
|
|
3384
|
-
credit_insufficient: import_zod.z.object({
|
|
3385
|
-
key: import_zod.z.string(),
|
|
3386
|
-
balance: import_zod.z.number().int(),
|
|
3387
|
-
reserved: import_zod.z.number().int(),
|
|
3388
|
-
remaining: import_zod.z.number().int()
|
|
3389
|
-
})
|
|
2896
|
+
too_many_requests: import_zod.z.object({ retryAfter: import_zod.z.number().int() })
|
|
3390
2897
|
};
|
|
3391
2898
|
function getErrorRegistry() {
|
|
3392
2899
|
const g = globalThis;
|
|
@@ -3398,13 +2905,7 @@ function getErrorRegistry() {
|
|
|
3398
2905
|
["forbidden", 403, "Forbidden"],
|
|
3399
2906
|
["not_found", 404, "NotFound"],
|
|
3400
2907
|
["conflict", 409, "Conflict"],
|
|
3401
|
-
["too_many_requests", 429, "TooManyRequests"]
|
|
3402
|
-
// Thrown by the purchases decorators, so they are pre-seeded like the
|
|
3403
|
-
// other built-ins: a project never declares them, but every gated or
|
|
3404
|
-
// metered route must surface them as TYPED errors on the client.
|
|
3405
|
-
["entitlement_required", 403, "EntitlementRequired"],
|
|
3406
|
-
["quota_exceeded", 429, "QuotaExceeded"],
|
|
3407
|
-
["credit_insufficient", 429, "CreditInsufficient"]
|
|
2908
|
+
["too_many_requests", 429, "TooManyRequests"]
|
|
3408
2909
|
]) {
|
|
3409
2910
|
const dataSchema = BUILTIN_DATA_SCHEMAS[code];
|
|
3410
2911
|
m.set(code, {
|
|
@@ -3425,8 +2926,8 @@ function defineError(code, status, dataSchema) {
|
|
|
3425
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.`
|
|
3426
2927
|
);
|
|
3427
2928
|
}
|
|
3428
|
-
const
|
|
3429
|
-
const existing =
|
|
2929
|
+
const registry2 = getErrorRegistry();
|
|
2930
|
+
const existing = registry2.get(code);
|
|
3430
2931
|
const className = defaultClassName(code);
|
|
3431
2932
|
const dataDigest = dataSchema ? digestOf(code, dataSchema) : void 0;
|
|
3432
2933
|
if (existing) {
|
|
@@ -3455,7 +2956,7 @@ function defineError(code, status, dataSchema) {
|
|
|
3455
2956
|
};
|
|
3456
2957
|
const cls = dataSchema ? makeWithData(dataSchema) : makeWithoutData();
|
|
3457
2958
|
Object.defineProperty(cls, "name", { value: className });
|
|
3458
|
-
|
|
2959
|
+
registry2.set(code, {
|
|
3459
2960
|
code,
|
|
3460
2961
|
status,
|
|
3461
2962
|
className,
|
|
@@ -3614,52 +3115,6 @@ function getJobConfig(ctor) {
|
|
|
3614
3115
|
return { schedule: meta.schedule, timeout, retry, handler: run };
|
|
3615
3116
|
}
|
|
3616
3117
|
|
|
3617
|
-
// src/resource.ts
|
|
3618
|
-
var Resource = class {
|
|
3619
|
-
/** The env-var names this resource needs. Optional; omit for none. */
|
|
3620
|
-
static secrets;
|
|
3621
|
-
};
|
|
3622
|
-
function hasSecrets(value) {
|
|
3623
|
-
if (typeof value !== "object" && typeof value !== "function") return false;
|
|
3624
|
-
const secrets = value.secrets;
|
|
3625
|
-
return secrets === void 0 || Array.isArray(secrets);
|
|
3626
|
-
}
|
|
3627
|
-
var registry2 = [];
|
|
3628
|
-
function __registerResource(resource) {
|
|
3629
|
-
registry2.push({ resource, booted: false });
|
|
3630
|
-
}
|
|
3631
|
-
function declaredSecrets(resource) {
|
|
3632
|
-
const ctor = resource.constructor;
|
|
3633
|
-
return hasSecrets(ctor) ? ctor.secrets ?? [] : [];
|
|
3634
|
-
}
|
|
3635
|
-
async function __runResourceBoot(envMap) {
|
|
3636
|
-
for (const entry of registry2) {
|
|
3637
|
-
if (entry.booted) continue;
|
|
3638
|
-
const secrets = declaredSecrets(entry.resource);
|
|
3639
|
-
const env = {};
|
|
3640
|
-
for (const name of secrets) {
|
|
3641
|
-
const value = envMap[name];
|
|
3642
|
-
if (value === void 0) {
|
|
3643
|
-
throw new Error(
|
|
3644
|
-
`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.`
|
|
3645
|
-
);
|
|
3646
|
-
}
|
|
3647
|
-
env[name] = value;
|
|
3648
|
-
}
|
|
3649
|
-
await entry.resource.init(env);
|
|
3650
|
-
entry.booted = true;
|
|
3651
|
-
}
|
|
3652
|
-
}
|
|
3653
|
-
async function __shutdownResources() {
|
|
3654
|
-
for (let i = registry2.length - 1; i >= 0; i -= 1) {
|
|
3655
|
-
const entry = registry2[i];
|
|
3656
|
-
if (entry.booted && entry.resource.shutdown) {
|
|
3657
|
-
await entry.resource.shutdown();
|
|
3658
|
-
}
|
|
3659
|
-
}
|
|
3660
|
-
registry2.length = 0;
|
|
3661
|
-
}
|
|
3662
|
-
|
|
3663
3118
|
// src/index.ts
|
|
3664
3119
|
var import_zod2 = require("zod");
|
|
3665
3120
|
// Annotate the CommonJS export names for ESM import in node:
|
|
@@ -3670,17 +3125,11 @@ var import_zod2 = require("zod");
|
|
|
3670
3125
|
Client,
|
|
3671
3126
|
Conflict,
|
|
3672
3127
|
Controller,
|
|
3673
|
-
DEFAULT_TEMPLATE_LOCALE,
|
|
3674
3128
|
Database,
|
|
3675
3129
|
Delete,
|
|
3676
3130
|
Deny,
|
|
3677
3131
|
Documents,
|
|
3678
|
-
EGRESS_CONFIG_KIND,
|
|
3679
|
-
EGRESS_TIMEOUT_DEFAULT_MS,
|
|
3680
|
-
EGRESS_TIMEOUT_MAX_MS,
|
|
3681
|
-
EGRESS_TIMEOUT_MIN_MS,
|
|
3682
3132
|
EXTENSION_DEPENDENCIES,
|
|
3683
|
-
FLAGS_CONFIG_KIND,
|
|
3684
3133
|
Flags,
|
|
3685
3134
|
Forbidden,
|
|
3686
3135
|
Get,
|
|
@@ -3689,35 +3138,25 @@ var import_zod2 = require("zod");
|
|
|
3689
3138
|
HttpError,
|
|
3690
3139
|
Job,
|
|
3691
3140
|
Log,
|
|
3692
|
-
NOTIFICATIONS_CONFIG_KIND,
|
|
3693
3141
|
NotFound,
|
|
3694
3142
|
Notifications,
|
|
3695
3143
|
On,
|
|
3696
3144
|
OptionalUser,
|
|
3697
3145
|
PALBASE_EXTENSIONS,
|
|
3698
|
-
PROVIDER_CATALOG,
|
|
3699
3146
|
PalError,
|
|
3700
3147
|
PalbaseModuleError,
|
|
3701
3148
|
Param,
|
|
3702
3149
|
Patch,
|
|
3703
3150
|
PolicyBuilder,
|
|
3704
3151
|
Post,
|
|
3705
|
-
Purchases,
|
|
3706
3152
|
Put,
|
|
3707
3153
|
Query,
|
|
3708
3154
|
QueryParams,
|
|
3709
|
-
RESERVED_SECRET_PREFIX,
|
|
3710
3155
|
Realtime,
|
|
3711
3156
|
Req,
|
|
3712
3157
|
RequestId,
|
|
3713
|
-
RequireEntitlement,
|
|
3714
|
-
Resource,
|
|
3715
|
-
SECRETS_CONFIG_KIND,
|
|
3716
|
-
STORAGE_CONFIG_KIND,
|
|
3717
3158
|
Secrets,
|
|
3718
|
-
Spend,
|
|
3719
3159
|
Storage,
|
|
3720
|
-
TEST_USERS_CONFIG_KIND,
|
|
3721
3160
|
TooManyRequests,
|
|
3722
3161
|
TraceId,
|
|
3723
3162
|
TxPlanError,
|
|
@@ -3728,32 +3167,19 @@ var import_zod2 = require("zod");
|
|
|
3728
3167
|
User,
|
|
3729
3168
|
Webhook,
|
|
3730
3169
|
__getRuntime,
|
|
3731
|
-
__registerResource,
|
|
3732
3170
|
__requestALS,
|
|
3733
3171
|
__resetRegisteredControllers,
|
|
3734
|
-
__runResourceBoot,
|
|
3735
3172
|
__runWithRuntime,
|
|
3736
3173
|
__setRuntime,
|
|
3737
|
-
__shutdownResources,
|
|
3738
3174
|
bigint,
|
|
3739
3175
|
boolean,
|
|
3740
|
-
bucket,
|
|
3741
3176
|
buildModuleClients,
|
|
3742
|
-
buildProvider,
|
|
3743
3177
|
dec,
|
|
3744
|
-
|
|
3178
|
+
defineChannels,
|
|
3745
3179
|
defineError,
|
|
3746
|
-
defineFlags,
|
|
3747
3180
|
defineMiddleware,
|
|
3748
|
-
defineNotifications,
|
|
3749
3181
|
defineSchema,
|
|
3750
|
-
defineSecrets,
|
|
3751
|
-
defineStorage,
|
|
3752
|
-
defineTestUsers,
|
|
3753
|
-
entitlementFor,
|
|
3754
3182
|
enumType,
|
|
3755
|
-
extractVariables,
|
|
3756
|
-
flag,
|
|
3757
3183
|
getErrorRegistry,
|
|
3758
3184
|
getHookConfig,
|
|
3759
3185
|
getJobConfig,
|
|
@@ -3766,23 +3192,21 @@ var import_zod2 = require("zod");
|
|
|
3766
3192
|
jsonb,
|
|
3767
3193
|
makeEnvDts,
|
|
3768
3194
|
makeHttpClient,
|
|
3769
|
-
|
|
3195
|
+
makeStackDts,
|
|
3770
3196
|
makeTypedDB,
|
|
3771
3197
|
now,
|
|
3772
3198
|
numeric,
|
|
3773
|
-
|
|
3199
|
+
openai,
|
|
3200
|
+
ownerOnly,
|
|
3774
3201
|
policy,
|
|
3202
|
+
publicChannel,
|
|
3775
3203
|
raw,
|
|
3776
3204
|
recordThrows,
|
|
3777
|
-
reservedSecretKey,
|
|
3778
|
-
secret,
|
|
3779
|
-
spendFor,
|
|
3780
|
-
testUser,
|
|
3781
3205
|
text,
|
|
3782
3206
|
timestamp,
|
|
3783
3207
|
toSchemaJSON,
|
|
3784
3208
|
uuid,
|
|
3785
|
-
|
|
3209
|
+
vector,
|
|
3786
3210
|
z
|
|
3787
3211
|
});
|
|
3788
3212
|
//# sourceMappingURL=index.cjs.map
|