@palbase/backend 22.1.0 → 23.1.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 +750 -40
- package/dist/bin/palbase-backend.cjs.map +1 -1
- package/dist/bin/palbase-backend.js +5 -5
- package/dist/{chunk-YL4C5NRY.js → chunk-HQRJDARQ.js} +2 -2
- package/dist/{chunk-74XDEF5J.js → chunk-M5MCBWJI.js} +723 -37
- package/dist/chunk-M5MCBWJI.js.map +1 -0
- package/dist/{chunk-W5ODXPY3.js → chunk-NS5V43YQ.js} +14 -1
- package/dist/chunk-NS5V43YQ.js.map +1 -0
- package/dist/{chunk-SQC5EIWY.js → chunk-OHALWEOG.js} +19 -9
- package/dist/chunk-OHALWEOG.js.map +1 -0
- package/dist/{chunk-I3ON7MYF.js → chunk-PY7YJDCT.js} +129 -18
- package/dist/chunk-PY7YJDCT.js.map +1 -0
- package/dist/{chunk-N32VDWKH.js → chunk-R3KN6RHD.js} +4 -59
- package/dist/chunk-R3KN6RHD.js.map +1 -0
- package/dist/{chunk-QMVK4X3V.js → chunk-RCLNBJCM.js} +98 -98
- package/dist/chunk-RCLNBJCM.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 +140 -16
- 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 +2 -2
- package/dist/{endpoint-BVT6jcVW.d.cts → endpoint-CVWXh6oG.d.ts} +147 -15
- package/dist/{endpoint-BVT6jcVW.d.ts → endpoint-c9h5jriX.d.cts} +147 -15
- package/dist/engine/index.cjs +750 -40
- 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 +4 -4
- package/dist/{index-BS1gW4nV.d.cts → index-BZrJXnVh.d.ts} +142 -72
- package/dist/{index-BqCiHao8.d.cts → index-By8Dle5U.d.cts} +196 -21
- package/dist/{index-vwHoS0l2.d.ts → index-CwAJ7HEe.d.ts} +196 -21
- package/dist/{index-CCZqzych.d.ts → index-CxeQSfJP.d.cts} +142 -72
- package/dist/index.cjs +388 -1101
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +89 -1134
- package/dist/index.d.ts +89 -1134
- package/dist/index.js +133 -902
- package/dist/index.js.map +1 -1
- package/dist/openapi/index.cjs +32 -61
- package/dist/openapi/index.cjs.map +1 -1
- package/dist/openapi/index.d.cts +6 -2
- package/dist/openapi/index.d.ts +6 -2
- package/dist/openapi/index.js +34 -26
- package/dist/openapi/index.js.map +1 -1
- package/dist/{registry-Bsuf-orT.d.ts → registry-B3niOVYp.d.ts} +108 -170
- package/dist/{registry-BWttGlaT.d.cts → registry-CqPK2Qby.d.cts} +108 -170
- 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/dist/test/index.cjs +482 -9
- package/dist/test/index.cjs.map +1 -1
- package/dist/test/index.d.cts +35 -3
- package/dist/test/index.d.ts +35 -3
- package/dist/test/index.js +480 -8
- package/dist/test/index.js.map +1 -1
- package/docs/README.md +7 -6
- package/docs/llms-full.txt +7 -260
- package/docs/llms.txt +0 -2
- package/package.json +9 -8
- package/stager/return_types.js +23 -0
- package/template/package.json +1 -1
- package/dist/chunk-74XDEF5J.js.map +0 -1
- package/dist/chunk-I3ON7MYF.js.map +0 -1
- package/dist/chunk-N32VDWKH.js.map +0 -1
- package/dist/chunk-QMVK4X3V.js.map +0 -1
- package/dist/chunk-SQC5EIWY.js.map +0 -1
- package/dist/chunk-W5ODXPY3.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-HQRJDARQ.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,28 @@ __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
|
-
|
|
60
|
+
Signal: () => Signal,
|
|
61
|
+
Sse: () => Sse,
|
|
62
|
+
SseOut: () => SseOut,
|
|
75
63
|
Storage: () => Storage,
|
|
76
|
-
TEST_USERS_CONFIG_KIND: () => TEST_USERS_CONFIG_KIND,
|
|
77
64
|
TooManyRequests: () => TooManyRequests,
|
|
78
65
|
TraceId: () => TraceId,
|
|
79
66
|
TxPlanError: () => TxPlanError,
|
|
@@ -84,33 +71,19 @@ __export(src_exports, {
|
|
|
84
71
|
User: () => User,
|
|
85
72
|
Webhook: () => Webhook,
|
|
86
73
|
__getRuntime: () => __getRuntime,
|
|
87
|
-
__registerResource: () => __registerResource,
|
|
88
74
|
__requestALS: () => __requestALS,
|
|
89
75
|
__resetRegisteredControllers: () => __resetRegisteredControllers,
|
|
90
|
-
__runResourceBoot: () => __runResourceBoot,
|
|
91
76
|
__runWithRuntime: () => __runWithRuntime,
|
|
92
77
|
__setRuntime: () => __setRuntime,
|
|
93
|
-
__shutdownResources: () => __shutdownResources,
|
|
94
78
|
bigint: () => bigint,
|
|
95
79
|
boolean: () => boolean,
|
|
96
|
-
bucket: () => bucket,
|
|
97
80
|
buildModuleClients: () => buildModuleClients,
|
|
98
|
-
buildProvider: () => buildProvider,
|
|
99
81
|
dec: () => dec,
|
|
100
82
|
defineChannels: () => defineChannels,
|
|
101
|
-
defineEgress: () => defineEgress,
|
|
102
83
|
defineError: () => defineError,
|
|
103
|
-
defineFlags: () => defineFlags,
|
|
104
84
|
defineMiddleware: () => defineMiddleware,
|
|
105
|
-
defineNotifications: () => defineNotifications,
|
|
106
85
|
defineSchema: () => defineSchema,
|
|
107
|
-
defineSecrets: () => defineSecrets,
|
|
108
|
-
defineStorage: () => defineStorage,
|
|
109
|
-
defineTestUsers: () => defineTestUsers,
|
|
110
|
-
entitlementFor: () => entitlementFor,
|
|
111
86
|
enumType: () => enumType,
|
|
112
|
-
extractVariables: () => extractVariables,
|
|
113
|
-
flag: () => flag,
|
|
114
87
|
getErrorRegistry: () => getErrorRegistry,
|
|
115
88
|
getHookConfig: () => getHookConfig,
|
|
116
89
|
getJobConfig: () => getJobConfig,
|
|
@@ -123,26 +96,20 @@ __export(src_exports, {
|
|
|
123
96
|
jsonb: () => jsonb,
|
|
124
97
|
makeEnvDts: () => makeEnvDts,
|
|
125
98
|
makeHttpClient: () => makeHttpClient,
|
|
126
|
-
|
|
99
|
+
makeStackDts: () => makeStackDts,
|
|
127
100
|
makeTypedDB: () => makeTypedDB,
|
|
128
101
|
now: () => now,
|
|
129
102
|
numeric: () => numeric,
|
|
130
103
|
openai: () => openai,
|
|
131
104
|
ownerOnly: () => ownerOnly,
|
|
132
|
-
parseFileSizeLimit: () => parseFileSizeLimit,
|
|
133
105
|
policy: () => policy,
|
|
134
106
|
publicChannel: () => publicChannel,
|
|
135
107
|
raw: () => raw,
|
|
136
108
|
recordThrows: () => recordThrows,
|
|
137
|
-
reservedSecretKey: () => reservedSecretKey,
|
|
138
|
-
secret: () => secret,
|
|
139
|
-
spendFor: () => spendFor,
|
|
140
|
-
testUser: () => testUser,
|
|
141
109
|
text: () => text,
|
|
142
110
|
timestamp: () => timestamp,
|
|
143
111
|
toSchemaJSON: () => toSchemaJSON,
|
|
144
112
|
uuid: () => uuid,
|
|
145
|
-
validateUploadAgainstStorage: () => validateUploadAgainstStorage,
|
|
146
113
|
vector: () => vector,
|
|
147
114
|
z: () => import_zod2.z
|
|
148
115
|
});
|
|
@@ -390,6 +357,19 @@ var TxPlanBuilder = class {
|
|
|
390
357
|
}
|
|
391
358
|
return this.push({ op: "insert", table: name, values: encoded }, `${name}.insert()`);
|
|
392
359
|
},
|
|
360
|
+
upsert: (values, options) => {
|
|
361
|
+
const encoded = encodeMap(values, false);
|
|
362
|
+
if (Object.keys(encoded).length === 0) {
|
|
363
|
+
throw new TxPlanError(`${name}.upsert() needs at least one column`);
|
|
364
|
+
}
|
|
365
|
+
if (options.onConflict.length === 0) {
|
|
366
|
+
throw new TxPlanError(`${name}.upsert() needs at least one onConflict column`);
|
|
367
|
+
}
|
|
368
|
+
return this.push(
|
|
369
|
+
{ op: "upsert", table: name, values: encoded, onConflict: options.onConflict },
|
|
370
|
+
`${name}.upsert()`
|
|
371
|
+
);
|
|
372
|
+
},
|
|
393
373
|
insertMany: (rows) => {
|
|
394
374
|
if (rows.length === 0) {
|
|
395
375
|
return new TxRowsImpl(this, SKIPPED_OP, `${name}.insertMany()`);
|
|
@@ -600,8 +580,12 @@ function makeTablesAccessor(ops) {
|
|
|
600
580
|
update: (id, data) => ops().update(name, id, data),
|
|
601
581
|
delete: (id) => ops().delete(name, id),
|
|
602
582
|
findById: (id) => ops().findById(name, id),
|
|
603
|
-
findMany: (query) => ops().findMany(name, query),
|
|
604
|
-
|
|
583
|
+
findMany: (query, opts) => ops().findMany(name, query, opts),
|
|
584
|
+
upsert: (data, opts) => ops().upsert(name, data, opts),
|
|
585
|
+
search: (params) => ops().search(name, params),
|
|
586
|
+
similar: (id, params) => ops().similar(name, id, params),
|
|
587
|
+
recommend: (params) => ops().recommend(name, params),
|
|
588
|
+
supersede: (id, row) => ops().supersede(name, id, row)
|
|
605
589
|
};
|
|
606
590
|
}
|
|
607
591
|
}
|
|
@@ -610,17 +594,25 @@ function makeTablesAccessor(ops) {
|
|
|
610
594
|
}
|
|
611
595
|
var rawDatabase = makeServiceProxy("Database");
|
|
612
596
|
function makeTypedSurface(raw2) {
|
|
597
|
+
const reco = raw2;
|
|
613
598
|
const ops = {
|
|
614
599
|
query: (sql, params) => raw2.query(sql, params),
|
|
615
600
|
insert: (table, data) => raw2.insert(table, data),
|
|
616
601
|
update: (table, id, data) => raw2.update(table, id, data),
|
|
617
602
|
delete: (table, id) => raw2.delete(table, id),
|
|
618
603
|
findById: (table, id) => raw2.findById(table, id),
|
|
619
|
-
findMany: (table, query) => raw2.findMany(table, query),
|
|
620
|
-
|
|
604
|
+
findMany: (table, query, opts) => raw2.findMany(table, query, opts),
|
|
605
|
+
upsert: (table, data, opts) => raw2.upsert(table, data, opts),
|
|
606
|
+
search: (table, params) => raw2.search(table, params),
|
|
607
|
+
similar: (table, id, params) => reco.similar(table, id, params),
|
|
608
|
+
recommend: (table, params) => reco.recommend(table, params),
|
|
609
|
+
supersede: (table, id, row) => raw2.supersede(table, id, row)
|
|
621
610
|
};
|
|
622
611
|
return Object.assign(ops, {
|
|
623
|
-
|
|
612
|
+
// Both surfaces get it: a savepoint on the service transaction is as useful
|
|
613
|
+
// as one on the request's, and each is bound to its own connection.
|
|
614
|
+
attempt: (fn) => raw2.attempt(fn),
|
|
615
|
+
tables: makeTablesAccessor(() => reco),
|
|
624
616
|
transaction(fn) {
|
|
625
617
|
const builder = new TxPlanBuilder();
|
|
626
618
|
return runTxPlan(raw2, makeTxTablesAccessor(builder), builder, fn);
|
|
@@ -683,7 +675,6 @@ var Cache = makeServiceProxy("Cache");
|
|
|
683
675
|
var Secrets = makeServiceProxy("Secrets");
|
|
684
676
|
var Log = makeServiceProxy("Log");
|
|
685
677
|
var Notifications = makeServiceProxy("Notifications");
|
|
686
|
-
var Purchases = makeServiceProxy("Purchases");
|
|
687
678
|
var rawFlags = makeServiceProxy("Flags");
|
|
688
679
|
var Flags = Object.assign(
|
|
689
680
|
{
|
|
@@ -727,250 +718,6 @@ var Flags = Object.assign(
|
|
|
727
718
|
);
|
|
728
719
|
var Realtime = makeServiceProxy("Realtime");
|
|
729
720
|
|
|
730
|
-
// src/errors.ts
|
|
731
|
-
var HTTP_ERROR_BRAND = /* @__PURE__ */ Symbol.for("palbase.backend.httpError");
|
|
732
|
-
var HttpError = class extends Error {
|
|
733
|
-
status;
|
|
734
|
-
error;
|
|
735
|
-
errorDescription;
|
|
736
|
-
data;
|
|
737
|
-
/** See {@link HTTP_ERROR_BRAND} — how the engine recognises this across SDK copies. */
|
|
738
|
-
[HTTP_ERROR_BRAND] = true;
|
|
739
|
-
constructor(status, error, errorDescription, data) {
|
|
740
|
-
super(errorDescription);
|
|
741
|
-
this.name = "HttpError";
|
|
742
|
-
this.status = status;
|
|
743
|
-
this.error = error;
|
|
744
|
-
this.errorDescription = errorDescription;
|
|
745
|
-
if (data !== void 0) {
|
|
746
|
-
this.data = data;
|
|
747
|
-
}
|
|
748
|
-
}
|
|
749
|
-
/**
|
|
750
|
-
* Serialize to the standard Palbase error response format.
|
|
751
|
-
* The `requestId` is injected by the runtime layer from the request context.
|
|
752
|
-
* When called without arguments (e.g. JSON.stringify), request_id is omitted.
|
|
753
|
-
* When `data` is set, it is appended as a strict-superset field.
|
|
754
|
-
*/
|
|
755
|
-
toJSON(requestId) {
|
|
756
|
-
const result = {
|
|
757
|
-
error: this.error,
|
|
758
|
-
error_description: this.errorDescription,
|
|
759
|
-
status: this.status
|
|
760
|
-
};
|
|
761
|
-
if (requestId) {
|
|
762
|
-
result.request_id = requestId;
|
|
763
|
-
}
|
|
764
|
-
if (this.data !== void 0) {
|
|
765
|
-
result.data = this.data;
|
|
766
|
-
}
|
|
767
|
-
return result;
|
|
768
|
-
}
|
|
769
|
-
};
|
|
770
|
-
var PalError = class extends HttpError {
|
|
771
|
-
constructor(status, code, description, data) {
|
|
772
|
-
super(status, code, description, data);
|
|
773
|
-
this.name = "PalError";
|
|
774
|
-
}
|
|
775
|
-
};
|
|
776
|
-
var NamedHttpError = class extends HttpError {
|
|
777
|
-
constructor(status, defaultCode, name, message, code, data) {
|
|
778
|
-
super(status, code ?? defaultCode, message ?? defaultMessage(name), data);
|
|
779
|
-
this.name = name;
|
|
780
|
-
}
|
|
781
|
-
};
|
|
782
|
-
function defaultMessage(name) {
|
|
783
|
-
const spaced = name.replace(/([a-z0-9])([A-Z])/g, "$1 $2");
|
|
784
|
-
return spaced.charAt(0).toUpperCase() + spaced.slice(1).toLowerCase();
|
|
785
|
-
}
|
|
786
|
-
var BadRequest = class extends NamedHttpError {
|
|
787
|
-
constructor(data, message) {
|
|
788
|
-
super(400, "bad_request", "BadRequest", message, void 0, data);
|
|
789
|
-
}
|
|
790
|
-
};
|
|
791
|
-
var Unauthorized = class extends NamedHttpError {
|
|
792
|
-
constructor(message, code, data) {
|
|
793
|
-
super(401, "unauthorized", "Unauthorized", message, code, data);
|
|
794
|
-
}
|
|
795
|
-
};
|
|
796
|
-
var Forbidden = class extends NamedHttpError {
|
|
797
|
-
constructor(message, code, data) {
|
|
798
|
-
super(403, "forbidden", "Forbidden", message, code, data);
|
|
799
|
-
}
|
|
800
|
-
};
|
|
801
|
-
var NotFound = class extends NamedHttpError {
|
|
802
|
-
constructor(message, code, data) {
|
|
803
|
-
super(404, "not_found", "NotFound", message, code, data);
|
|
804
|
-
}
|
|
805
|
-
};
|
|
806
|
-
var Conflict = class extends NamedHttpError {
|
|
807
|
-
constructor(message, code, data) {
|
|
808
|
-
super(409, "conflict", "Conflict", message, code, data);
|
|
809
|
-
}
|
|
810
|
-
};
|
|
811
|
-
var TooManyRequests = class extends NamedHttpError {
|
|
812
|
-
constructor(data, message) {
|
|
813
|
-
super(429, "too_many_requests", "TooManyRequests", message, void 0, data);
|
|
814
|
-
}
|
|
815
|
-
};
|
|
816
|
-
|
|
817
|
-
// src/purchases/errors.ts
|
|
818
|
-
function asPalstoreError(err) {
|
|
819
|
-
if (!(err instanceof Error)) return null;
|
|
820
|
-
const e = err;
|
|
821
|
-
if (typeof e.code !== "string" || typeof e.status !== "number") return null;
|
|
822
|
-
return { code: e.code, status: e.status, message: e.message };
|
|
823
|
-
}
|
|
824
|
-
function toHttpError(err) {
|
|
825
|
-
if (err instanceof HttpError) return err;
|
|
826
|
-
const p = asPalstoreError(err);
|
|
827
|
-
if (!p) return err;
|
|
828
|
-
switch (p.code) {
|
|
829
|
-
case "entitlement_required": {
|
|
830
|
-
const entitlement = err.entitlement ?? null;
|
|
831
|
-
return new PalError(403, "entitlement_required", p.message, { entitlement });
|
|
832
|
-
}
|
|
833
|
-
case "quota_exceeded":
|
|
834
|
-
case "credit_insufficient": {
|
|
835
|
-
const state = err.state;
|
|
836
|
-
return new PalError(429, p.code, p.message, state);
|
|
837
|
-
}
|
|
838
|
-
default:
|
|
839
|
-
return err;
|
|
840
|
-
}
|
|
841
|
-
}
|
|
842
|
-
|
|
843
|
-
// src/purchases/registry.ts
|
|
844
|
-
var ENTITLEMENTS = /* @__PURE__ */ Symbol.for("palbase.backend.purchases.entitlements");
|
|
845
|
-
var SPENDS = /* @__PURE__ */ Symbol.for("palbase.backend.purchases.spends");
|
|
846
|
-
function carrierOf(target) {
|
|
847
|
-
return typeof target === "function" ? target : target.constructor ?? target;
|
|
848
|
-
}
|
|
849
|
-
function own(carrier, slot) {
|
|
850
|
-
if (!Object.prototype.hasOwnProperty.call(carrier, slot)) {
|
|
851
|
-
carrier[slot] = {};
|
|
852
|
-
}
|
|
853
|
-
return carrier[slot];
|
|
854
|
-
}
|
|
855
|
-
function recordEntitlement(target, fnName, key) {
|
|
856
|
-
own(carrierOf(target), ENTITLEMENTS)[fnName] = key;
|
|
857
|
-
}
|
|
858
|
-
function recordSpend(target, fnName, meta) {
|
|
859
|
-
own(carrierOf(target), SPENDS)[fnName] = meta;
|
|
860
|
-
}
|
|
861
|
-
function entitlementFor(ctor, fnName) {
|
|
862
|
-
return carrierOf(ctor)[ENTITLEMENTS]?.[fnName];
|
|
863
|
-
}
|
|
864
|
-
function spendFor(ctor, fnName) {
|
|
865
|
-
return carrierOf(ctor)[SPENDS]?.[fnName];
|
|
866
|
-
}
|
|
867
|
-
|
|
868
|
-
// src/purchases/subject.ts
|
|
869
|
-
var subjectByRequest = /* @__PURE__ */ new WeakMap();
|
|
870
|
-
function storeEnv() {
|
|
871
|
-
return process.env.PALSTORE_STORE_ENV === "sandbox" ? "sandbox" : "production";
|
|
872
|
-
}
|
|
873
|
-
async function currentSubjectId() {
|
|
874
|
-
const store = __requestALS.getStore();
|
|
875
|
-
if (!store) {
|
|
876
|
-
throw new Error(
|
|
877
|
-
"Purchases decorators used outside a request scope. @RequireEntitlement/@Spend resolve the subject from the request's authenticated user, so they only work inside an endpoint handler."
|
|
878
|
-
);
|
|
879
|
-
}
|
|
880
|
-
const memoized = subjectByRequest.get(store);
|
|
881
|
-
if (memoized) return memoized;
|
|
882
|
-
const userId = store.userId;
|
|
883
|
-
if (!userId) {
|
|
884
|
-
throw new Unauthorized(
|
|
885
|
-
"This endpoint requires an authenticated user: purchases entitlements and quota are per-user, and the subject is resolved from the request's identity."
|
|
886
|
-
);
|
|
887
|
-
}
|
|
888
|
-
const resolving = Purchases.resolveSubject({ userRef: userId, storeEnv: storeEnv() }).then(
|
|
889
|
-
(r) => r.subjectId
|
|
890
|
-
);
|
|
891
|
-
subjectByRequest.set(store, resolving);
|
|
892
|
-
resolving.catch(() => subjectByRequest.delete(store));
|
|
893
|
-
return resolving;
|
|
894
|
-
}
|
|
895
|
-
|
|
896
|
-
// src/purchases/decorators.ts
|
|
897
|
-
var WRAPPED = /* @__PURE__ */ Symbol.for("palbase.backend.purchases.wrapped");
|
|
898
|
-
function idempotencyKeyFor(key) {
|
|
899
|
-
const store = __requestALS.getStore();
|
|
900
|
-
const base = store?.idempotencyKey || store?.requestId;
|
|
901
|
-
if (!base) {
|
|
902
|
-
throw new Error(
|
|
903
|
-
"@Spend needs a request-scoped idempotency key, and the runtime supplied neither an Idempotency-Key header nor a request id. Spending without one would let a retried request charge the user twice."
|
|
904
|
-
);
|
|
905
|
-
}
|
|
906
|
-
return `${base}:${key}`;
|
|
907
|
-
}
|
|
908
|
-
function ensureWrapper(target, fnName, descriptor) {
|
|
909
|
-
const original = descriptor.value;
|
|
910
|
-
if (typeof original !== "function") {
|
|
911
|
-
throw new Error(
|
|
912
|
-
`@RequireEntitlement/@Spend can only decorate a method; "${fnName}" is not one.`
|
|
913
|
-
);
|
|
914
|
-
}
|
|
915
|
-
if (original[WRAPPED]) return;
|
|
916
|
-
const wrapper = async function(...args) {
|
|
917
|
-
const ctor = this.constructor;
|
|
918
|
-
const entitlement = entitlementFor(ctor, fnName);
|
|
919
|
-
const spend = spendFor(ctor, fnName);
|
|
920
|
-
let subjectId;
|
|
921
|
-
try {
|
|
922
|
-
subjectId = await currentSubjectId();
|
|
923
|
-
} catch (err) {
|
|
924
|
-
throw toHttpError(err);
|
|
925
|
-
}
|
|
926
|
-
if (entitlement !== void 0) {
|
|
927
|
-
try {
|
|
928
|
-
await Purchases.require(subjectId, entitlement);
|
|
929
|
-
} catch (err) {
|
|
930
|
-
throw toHttpError(err);
|
|
931
|
-
}
|
|
932
|
-
}
|
|
933
|
-
if (spend === void 0) {
|
|
934
|
-
return original.apply(this, args);
|
|
935
|
-
}
|
|
936
|
-
let handlerFailure;
|
|
937
|
-
try {
|
|
938
|
-
return await Purchases.withSpend(
|
|
939
|
-
subjectId,
|
|
940
|
-
spend.key,
|
|
941
|
-
{ count: spend.count, idempotencyKey: idempotencyKeyFor(spend.key) },
|
|
942
|
-
async () => {
|
|
943
|
-
try {
|
|
944
|
-
return await original.apply(this, args);
|
|
945
|
-
} catch (err) {
|
|
946
|
-
handlerFailure = { err };
|
|
947
|
-
throw err;
|
|
948
|
-
}
|
|
949
|
-
}
|
|
950
|
-
);
|
|
951
|
-
} catch (err) {
|
|
952
|
-
if (handlerFailure !== void 0 && handlerFailure.err === err) throw err;
|
|
953
|
-
throw toHttpError(err);
|
|
954
|
-
}
|
|
955
|
-
};
|
|
956
|
-
wrapper[WRAPPED] = true;
|
|
957
|
-
descriptor.value = wrapper;
|
|
958
|
-
}
|
|
959
|
-
function RequireEntitlement(key) {
|
|
960
|
-
return function(target, propertyKey, descriptor) {
|
|
961
|
-
const fnName = String(propertyKey);
|
|
962
|
-
recordEntitlement(target, fnName, key);
|
|
963
|
-
ensureWrapper(target, fnName, descriptor);
|
|
964
|
-
};
|
|
965
|
-
}
|
|
966
|
-
function Spend(key) {
|
|
967
|
-
return function(target, propertyKey, descriptor) {
|
|
968
|
-
const fnName = String(propertyKey);
|
|
969
|
-
recordSpend(target, fnName, { key, count: 1 });
|
|
970
|
-
ensureWrapper(target, fnName, descriptor);
|
|
971
|
-
};
|
|
972
|
-
}
|
|
973
|
-
|
|
974
721
|
// src/clients/http.ts
|
|
975
722
|
var PalbaseModuleError = class extends Error {
|
|
976
723
|
code;
|
|
@@ -1316,12 +1063,12 @@ function buildFlagsClient(http, cfg) {
|
|
|
1316
1063
|
return {
|
|
1317
1064
|
data: Object.keys(values).map((name) => {
|
|
1318
1065
|
const value = values[name];
|
|
1319
|
-
const
|
|
1066
|
+
const flag = {
|
|
1320
1067
|
name,
|
|
1321
1068
|
enabled: flagEnabled(value)
|
|
1322
1069
|
};
|
|
1323
|
-
if (typeof value === "string")
|
|
1324
|
-
return
|
|
1070
|
+
if (typeof value === "string") flag.variant = { name: value };
|
|
1071
|
+
return flag;
|
|
1325
1072
|
}),
|
|
1326
1073
|
error: null,
|
|
1327
1074
|
status: res.status
|
|
@@ -1549,13 +1296,13 @@ function buildNotificationsClient(http) {
|
|
|
1549
1296
|
|
|
1550
1297
|
// src/clients/realtime.ts
|
|
1551
1298
|
var import_node_crypto = require("crypto");
|
|
1552
|
-
function mintToken(
|
|
1299
|
+
function mintToken(secret) {
|
|
1553
1300
|
const now2 = Math.floor(Date.now() / 1e3);
|
|
1554
1301
|
const header = Buffer.from(JSON.stringify({ alg: "HS256", typ: "JWT" })).toString("base64url");
|
|
1555
1302
|
const payload = Buffer.from(
|
|
1556
1303
|
JSON.stringify({ iss: "backend-runtime", role: "service_role", iat: now2, exp: now2 + 60 })
|
|
1557
1304
|
).toString("base64url");
|
|
1558
|
-
const sig = (0, import_node_crypto.createHmac)("sha256",
|
|
1305
|
+
const sig = (0, import_node_crypto.createHmac)("sha256", secret).update(`${header}.${payload}`).digest("base64url");
|
|
1559
1306
|
return `${header}.${payload}.${sig}`;
|
|
1560
1307
|
}
|
|
1561
1308
|
function buildRealtimeClient(cfg) {
|
|
@@ -1726,11 +1473,11 @@ function validateStoragePath(p) {
|
|
|
1726
1473
|
);
|
|
1727
1474
|
}
|
|
1728
1475
|
}
|
|
1729
|
-
function toFileObject(
|
|
1476
|
+
function toFileObject(bucket, row) {
|
|
1730
1477
|
return {
|
|
1731
1478
|
name: row.path ?? "",
|
|
1732
1479
|
path: row.path ?? "",
|
|
1733
|
-
bucket: row.bucket ??
|
|
1480
|
+
bucket: row.bucket ?? bucket,
|
|
1734
1481
|
size: typeof row.size === "number" ? row.size : 0,
|
|
1735
1482
|
contentType: row.contentType ?? "",
|
|
1736
1483
|
checksum: row.checksum ?? "",
|
|
@@ -1846,36 +1593,10 @@ function buildModuleClients(cfg) {
|
|
|
1846
1593
|
};
|
|
1847
1594
|
}
|
|
1848
1595
|
|
|
1849
|
-
// src/purchases/keys-gen.ts
|
|
1850
|
-
function liveKeys(entries) {
|
|
1851
|
-
return Object.entries(entries ?? {}).filter(([, def]) => def?.removed !== true).map(([key]) => key).sort();
|
|
1852
|
-
}
|
|
1853
|
-
function memberName(key) {
|
|
1854
|
-
return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(key) ? key : JSON.stringify(key);
|
|
1855
|
-
}
|
|
1856
|
-
function members(keys) {
|
|
1857
|
-
if (keys.length === 0) return "";
|
|
1858
|
-
return `
|
|
1859
|
-
${keys.map((k) => ` ${memberName(k)}: true;`).join("\n")}
|
|
1860
|
-
`;
|
|
1861
|
-
}
|
|
1862
|
-
function makePurchasesDts(manifest) {
|
|
1863
|
-
const entitlements = liveKeys(manifest.entitlements);
|
|
1864
|
-
const limits = liveKeys({ ...manifest.limits, ...manifest.credits });
|
|
1865
|
-
return `// palbase-purchases.d.ts \u2014 GENERATED by @palbase/backend. Do not edit.
|
|
1866
|
-
// Source: the project's palstore catalog revision.
|
|
1867
|
-
|
|
1868
|
-
declare module "@palbase/backend/purchases" {
|
|
1869
|
-
interface Entitlements {${members(entitlements)}}
|
|
1870
|
-
|
|
1871
|
-
interface Limits {${members(limits)}}
|
|
1872
|
-
}
|
|
1873
|
-
|
|
1874
|
-
export {};
|
|
1875
|
-
`;
|
|
1876
|
-
}
|
|
1877
|
-
|
|
1878
1596
|
// src/db/policy.ts
|
|
1597
|
+
function quote(name) {
|
|
1598
|
+
return `"${name.replace(/"/g, '""')}"`;
|
|
1599
|
+
}
|
|
1879
1600
|
var PolicyBuilder = class {
|
|
1880
1601
|
_def;
|
|
1881
1602
|
constructor(name) {
|
|
@@ -1911,6 +1632,41 @@ var PolicyBuilder = class {
|
|
|
1911
1632
|
this._def.using = sqlExpr;
|
|
1912
1633
|
return this;
|
|
1913
1634
|
}
|
|
1635
|
+
/**
|
|
1636
|
+
* "Rows of THIS table whose owner the caller is a member of" — the membership
|
|
1637
|
+
* pattern, written so it cannot recurse.
|
|
1638
|
+
*
|
|
1639
|
+
* THE TRAP IT EXISTS FOR. Written by hand, membership policies point at each
|
|
1640
|
+
* other: `channels` is visible to members, so its policy reads
|
|
1641
|
+
* `channel_members`; `channel_members` is visible to members, so its policy
|
|
1642
|
+
* reads `channels`. Postgres refuses the pair at query time with `infinite
|
|
1643
|
+
* recursion detected in policy for relation ...`, and the error names the
|
|
1644
|
+
* relation but not the cycle. The way out is asymmetry — the MEMBERSHIP table
|
|
1645
|
+
* is protected by `user_id = auth.uid()` and nothing else, and every other
|
|
1646
|
+
* table subqueries INTO it. That shape was in the platform's own schema and
|
|
1647
|
+
* written down nowhere; a customer recovered it by reading that schema.
|
|
1648
|
+
*
|
|
1649
|
+
* `(select auth.uid())` rather than a bare call: the scalar subquery is
|
|
1650
|
+
* evaluated ONCE per statement instead of per row.
|
|
1651
|
+
*
|
|
1652
|
+
* @example
|
|
1653
|
+
* // channels: visible to members. The membership table gets the simple one.
|
|
1654
|
+
* policy("member_read").for("select").to("authenticated")
|
|
1655
|
+
* .memberOf("channel_members", "channel_id")
|
|
1656
|
+
* // → id IN (SELECT "channel_id" FROM "channel_members"
|
|
1657
|
+
* // WHERE "user_id" = (select auth.uid()))
|
|
1658
|
+
*/
|
|
1659
|
+
memberOf(membershipTable, foreignKey, options = {}) {
|
|
1660
|
+
if (this._def.using !== null) {
|
|
1661
|
+
throw new Error(
|
|
1662
|
+
`policy(${this._def.name}).memberOf(): this policy already has a using() expression. Write one or the other \u2014 memberOf IS the using expression.`
|
|
1663
|
+
);
|
|
1664
|
+
}
|
|
1665
|
+
const column = options.column ?? "id";
|
|
1666
|
+
const userColumn = options.userColumn ?? "user_id";
|
|
1667
|
+
this._def.using = `${quote(column)} IN (SELECT ${quote(foreignKey)} FROM ${quote(membershipTable)} WHERE ${quote(userColumn)} = (select auth.uid()))`;
|
|
1668
|
+
return this;
|
|
1669
|
+
}
|
|
1914
1670
|
/** Set the `WITH CHECK (...)` write-validation expression (raw SQL). */
|
|
1915
1671
|
withCheck(sqlExpr) {
|
|
1916
1672
|
this._def.withCheck = sqlExpr;
|
|
@@ -1950,27 +1706,81 @@ function defineSchema(input) {
|
|
|
1950
1706
|
if (table.checks !== void 0 && table.checks.length > 0) tableDef.checks = table.checks.slice();
|
|
1951
1707
|
if (table.indexes !== void 0 && table.indexes.length > 0) tableDef.indexes = table.indexes.slice();
|
|
1952
1708
|
if (table.search !== void 0) {
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
if (
|
|
1964
|
-
throw new Error(
|
|
1709
|
+
const s = table.search;
|
|
1710
|
+
if (s.from !== void 0) {
|
|
1711
|
+
if (s.vector !== void 0) {
|
|
1712
|
+
throw new Error(
|
|
1713
|
+
`table ${String(name)}: search beyan\u0131nda tek bi\xE7im kullan\u0131n \u2014 'from' (yeni) ile 'vector' (eski) birlikte olamaz`
|
|
1714
|
+
);
|
|
1715
|
+
}
|
|
1716
|
+
if (s.from.length === 0) {
|
|
1717
|
+
throw new Error(`table ${String(name)}: search.from bo\u015F olamaz`);
|
|
1718
|
+
}
|
|
1719
|
+
if (s.model === void 0) {
|
|
1720
|
+
throw new Error(
|
|
1721
|
+
`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)`
|
|
1722
|
+
);
|
|
1965
1723
|
}
|
|
1966
|
-
if (
|
|
1967
|
-
throw new Error(`table ${String(name)}: search.
|
|
1724
|
+
if (Array.isArray(s.text) && s.text.length === 0) {
|
|
1725
|
+
throw new Error(`table ${String(name)}: search.text bo\u015F dizi olamaz \u2014 FTS istemiyorsan text: false yaz\u0131n`);
|
|
1726
|
+
}
|
|
1727
|
+
} else {
|
|
1728
|
+
if (typeof s.text === "boolean") {
|
|
1729
|
+
throw new Error(`table ${String(name)}: text:${String(s.text)} yaln\u0131z yeni bi\xE7imde ('from' ile) ge\xE7erli`);
|
|
1730
|
+
}
|
|
1731
|
+
if (s.text === void 0 && s.vector === void 0) {
|
|
1732
|
+
throw new Error(
|
|
1733
|
+
`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`
|
|
1734
|
+
);
|
|
1735
|
+
}
|
|
1736
|
+
if (s.text !== void 0 && s.text.length === 0) {
|
|
1737
|
+
throw new Error(`table ${String(name)}: search.text bo\u015F olamaz \u2014 FTS kolu istemiyorsan alan\u0131 hi\xE7 yazma`);
|
|
1738
|
+
}
|
|
1739
|
+
const legs = s.vector === void 0 ? [] : Array.isArray(s.vector) ? s.vector : [s.vector];
|
|
1740
|
+
for (const leg of legs) {
|
|
1741
|
+
if (leg.model !== void 0 && (leg.from === void 0 || leg.from.length === 0)) {
|
|
1742
|
+
throw new Error(`table ${String(name)}: search.vector.model beyan edildi ama 'from' yok \u2014 embed kayna\u011F\u0131 kolonlar zorunlu (C-2)`);
|
|
1743
|
+
}
|
|
1744
|
+
if (leg.model === void 0 && leg.from !== void 0) {
|
|
1745
|
+
throw new Error(`table ${String(name)}: search.vector.from model'siz anlams\u0131z \u2014 auto-embed i\xE7in model verin`);
|
|
1746
|
+
}
|
|
1968
1747
|
}
|
|
1969
1748
|
}
|
|
1970
|
-
tableDef.search =
|
|
1749
|
+
tableDef.search = s;
|
|
1750
|
+
}
|
|
1751
|
+
if (table.memory !== void 0) {
|
|
1752
|
+
if (table.memory.from.length === 0) {
|
|
1753
|
+
throw new Error(`table ${String(name)}: memory.from bo\u015F olamaz`);
|
|
1754
|
+
}
|
|
1755
|
+
tableDef.memory = table.memory;
|
|
1971
1756
|
}
|
|
1972
1757
|
tables[name] = tableDef;
|
|
1973
1758
|
}
|
|
1759
|
+
for (const [name, def] of Object.entries(tables)) {
|
|
1760
|
+
const m = def.memory;
|
|
1761
|
+
if (m === void 0) continue;
|
|
1762
|
+
const target = Object.values(tables).find((t) => t.name === m.into);
|
|
1763
|
+
if (target === void 0) {
|
|
1764
|
+
throw new Error(`table ${name}: memory.into "${m.into}" \u015Femada declared de\u011Fil`);
|
|
1765
|
+
}
|
|
1766
|
+
const subject = m.subject ?? "owner";
|
|
1767
|
+
if (!(subject in def.columns)) {
|
|
1768
|
+
throw new Error(`table ${name}: memory.subject "${subject}" kolonu kaynak tabloda yok`);
|
|
1769
|
+
}
|
|
1770
|
+
if (!(subject in target.columns)) {
|
|
1771
|
+
throw new Error(`table ${name}: memory.subject "${subject}" kolonu hedef "${m.into}" tablosunda yok`);
|
|
1772
|
+
}
|
|
1773
|
+
const factCol = target.columns["fact"];
|
|
1774
|
+
const factDef = factCol !== void 0 && "_def" in factCol ? factCol._def : factCol;
|
|
1775
|
+
if (factDef === void 0 || factDef.type !== "text") {
|
|
1776
|
+
throw new Error(`table ${name}: memory.into "${m.into}" tablosunda "fact" (text) kolonu zorunlu`);
|
|
1777
|
+
}
|
|
1778
|
+
for (const c of m.from) {
|
|
1779
|
+
if (!(c in def.columns)) {
|
|
1780
|
+
throw new Error(`table ${name}: memory.from kolonu "${c}" kaynak tabloda yok`);
|
|
1781
|
+
}
|
|
1782
|
+
}
|
|
1783
|
+
}
|
|
1974
1784
|
const extensions = [...new Set(input.extensions ?? [])];
|
|
1975
1785
|
return { tables, extensions };
|
|
1976
1786
|
}
|
|
@@ -2013,9 +1823,45 @@ function policyToJSON(policy2) {
|
|
|
2013
1823
|
permissive: policy2.permissive !== false
|
|
2014
1824
|
};
|
|
2015
1825
|
}
|
|
2016
|
-
function
|
|
1826
|
+
function commonSearchFields(search, out) {
|
|
1827
|
+
if (search.synonyms !== void 0 && Object.keys(search.synonyms).length > 0) {
|
|
1828
|
+
out.synonyms = Object.fromEntries(
|
|
1829
|
+
Object.entries(search.synonyms).map(([word, alts]) => [word, [...alts]])
|
|
1830
|
+
);
|
|
1831
|
+
}
|
|
1832
|
+
if (search.validity === true) out.validity = true;
|
|
1833
|
+
}
|
|
1834
|
+
function searchToJSON(search, vectorColumn) {
|
|
1835
|
+
if (search.from !== void 0 && search.model !== void 0) {
|
|
1836
|
+
const out2 = {};
|
|
1837
|
+
const textCols = search.text === false ? void 0 : Array.isArray(search.text) && search.text.length > 0 ? search.text : search.from;
|
|
1838
|
+
if (textCols !== void 0) out2.text = { columns: [...textCols] };
|
|
1839
|
+
const v = { metric: search.metric ?? "cosine" };
|
|
1840
|
+
if (vectorColumn !== void 0) v.column = vectorColumn;
|
|
1841
|
+
v.embed = {
|
|
1842
|
+
provider: search.model.provider,
|
|
1843
|
+
model: search.model.model,
|
|
1844
|
+
from: [...search.from],
|
|
1845
|
+
...search.model.apiKeyName !== void 0 ? { apiKeyName: search.model.apiKeyName } : {},
|
|
1846
|
+
...search.model.dimensions !== void 0 ? { dimensions: search.model.dimensions } : {},
|
|
1847
|
+
...search.model.baseURL !== void 0 ? { baseURL: search.model.baseURL } : {}
|
|
1848
|
+
};
|
|
1849
|
+
if (search.staleness !== void 0) v.staleness = search.staleness;
|
|
1850
|
+
if (vectorColumn === void 0) {
|
|
1851
|
+
v.mode = "chunks";
|
|
1852
|
+
if (search.chunks !== void 0) {
|
|
1853
|
+
const c = {};
|
|
1854
|
+
if (search.chunks.size !== void 0 && search.chunks.size > 0) c.sizeChars = search.chunks.size;
|
|
1855
|
+
if (search.chunks.overlap !== void 0 && search.chunks.overlap > 0) c.overlapChars = search.chunks.overlap;
|
|
1856
|
+
if (Object.keys(c).length > 0) v.chunks = c;
|
|
1857
|
+
}
|
|
1858
|
+
}
|
|
1859
|
+
out2.vector = [v];
|
|
1860
|
+
commonSearchFields(search, out2);
|
|
1861
|
+
return out2;
|
|
1862
|
+
}
|
|
2017
1863
|
const out = {};
|
|
2018
|
-
if (search.text
|
|
1864
|
+
if (Array.isArray(search.text) && search.text.length > 0) out.text = { columns: [...search.text] };
|
|
2019
1865
|
const legs = search.vector === void 0 ? [] : Array.isArray(search.vector) ? search.vector : [search.vector];
|
|
2020
1866
|
if (legs.length > 0) {
|
|
2021
1867
|
out.vector = legs.map((leg) => {
|
|
@@ -2035,8 +1881,17 @@ function searchToJSON(search) {
|
|
|
2035
1881
|
return v;
|
|
2036
1882
|
});
|
|
2037
1883
|
}
|
|
1884
|
+
commonSearchFields(search, out);
|
|
2038
1885
|
return out;
|
|
2039
1886
|
}
|
|
1887
|
+
function memoryToJSON(m) {
|
|
1888
|
+
return {
|
|
1889
|
+
from: [...m.from],
|
|
1890
|
+
into: m.into,
|
|
1891
|
+
...m.subject !== void 0 ? { subject: m.subject } : {},
|
|
1892
|
+
extract: { provider: m.extract.provider, model: m.extract.model }
|
|
1893
|
+
};
|
|
1894
|
+
}
|
|
2040
1895
|
function tableToJSON(table) {
|
|
2041
1896
|
const columns = {};
|
|
2042
1897
|
for (const [name, column] of Object.entries(table.columns)) {
|
|
@@ -2073,9 +1928,13 @@ function tableToJSON(table) {
|
|
|
2073
1928
|
out.indexes = table.indexes.map((i) => ({ name: i.name, columns: [...i.columns] }));
|
|
2074
1929
|
}
|
|
2075
1930
|
if (table.search !== void 0) {
|
|
2076
|
-
const
|
|
1931
|
+
const vectorColumn = Object.entries(columns).find(([, c]) => c.dimensions !== void 0)?.[0];
|
|
1932
|
+
const sj = searchToJSON(table.search, vectorColumn);
|
|
2077
1933
|
if (sj.text !== void 0 || sj.vector !== void 0) out.search = sj;
|
|
2078
1934
|
}
|
|
1935
|
+
if (table.memory !== void 0) {
|
|
1936
|
+
out.memory = memoryToJSON(table.memory);
|
|
1937
|
+
}
|
|
2079
1938
|
return out;
|
|
2080
1939
|
}
|
|
2081
1940
|
function toSchemaJSON(schema) {
|
|
@@ -2233,6 +2092,21 @@ var ColumnBuilder = class _ColumnBuilder {
|
|
|
2233
2092
|
this._def.defaultNow = true;
|
|
2234
2093
|
return new _ColumnBuilder(this._def.type, this._def);
|
|
2235
2094
|
}
|
|
2095
|
+
/**
|
|
2096
|
+
* The DATABASE assigns this column's value — a trigger, a rule, an identity.
|
|
2097
|
+
*
|
|
2098
|
+
* The column becomes optional on INSERT (the author has nothing to send) while
|
|
2099
|
+
* the DDL stays free of a DEFAULT this schema would not honour. It is NOT
|
|
2100
|
+
* `default()`: that declares a value the schema promises to write.
|
|
2101
|
+
*
|
|
2102
|
+
* Naming: deliberately not `generated()`. Postgres has GENERATED columns and
|
|
2103
|
+
* they are a different thing; borrowing the word would send a reader — or a
|
|
2104
|
+
* model writing a schema — to the wrong feature.
|
|
2105
|
+
*/
|
|
2106
|
+
dbAssigned() {
|
|
2107
|
+
this._def.dbAssigned = true;
|
|
2108
|
+
return new _ColumnBuilder(this._def.type, this._def);
|
|
2109
|
+
}
|
|
2236
2110
|
/** Add a foreign key reference. */
|
|
2237
2111
|
/**
|
|
2238
2112
|
* Declares that this column used to be called `previous`.
|
|
@@ -2370,6 +2244,9 @@ var openai = {
|
|
|
2370
2244
|
...opts?.dimensions !== void 0 ? { dimensions: opts.dimensions } : {},
|
|
2371
2245
|
...opts?.baseURL !== void 0 ? { baseURL: opts.baseURL } : {}
|
|
2372
2246
|
};
|
|
2247
|
+
},
|
|
2248
|
+
chat(model) {
|
|
2249
|
+
return { provider: "openai", model };
|
|
2373
2250
|
}
|
|
2374
2251
|
};
|
|
2375
2252
|
|
|
@@ -2377,10 +2254,11 @@ var openai = {
|
|
|
2377
2254
|
function makeTypedTable(name, raw2) {
|
|
2378
2255
|
return {
|
|
2379
2256
|
insert: (data) => raw2.insert(name, data),
|
|
2257
|
+
upsert: (data, opts) => raw2.upsert(name, data, opts),
|
|
2380
2258
|
update: (id, data) => raw2.update(name, id, data),
|
|
2381
2259
|
delete: (id) => raw2.delete(name, id),
|
|
2382
2260
|
findById: (id) => raw2.findById(name, id),
|
|
2383
|
-
findMany: (query) => raw2.findMany(name, query)
|
|
2261
|
+
findMany: (query, opts) => raw2.findMany(name, query, opts)
|
|
2384
2262
|
};
|
|
2385
2263
|
}
|
|
2386
2264
|
function makeTypedDB(schema, raw2) {
|
|
@@ -2470,10 +2348,10 @@ function buildChildEdges(schema) {
|
|
|
2470
2348
|
const ref = builder._def.references;
|
|
2471
2349
|
if (ref === void 0) continue;
|
|
2472
2350
|
if (ref.table === childName) continue;
|
|
2473
|
-
const
|
|
2474
|
-
if (
|
|
2475
|
-
if (
|
|
2476
|
-
|
|
2351
|
+
const bucket = edges.get(ref.table);
|
|
2352
|
+
if (bucket === void 0) continue;
|
|
2353
|
+
if (bucket.some((e) => e.child === childName)) continue;
|
|
2354
|
+
bucket.push({ child: childName, column: col });
|
|
2477
2355
|
}
|
|
2478
2356
|
}
|
|
2479
2357
|
return edges;
|
|
@@ -2547,638 +2425,50 @@ export {};
|
|
|
2547
2425
|
`;
|
|
2548
2426
|
}
|
|
2549
2427
|
|
|
2550
|
-
// src/
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
var VARIANT_FITS = ["cover", "contain", "inside"];
|
|
2554
|
-
function normalizeVariants(input) {
|
|
2555
|
-
if (input === null || typeof input !== "object" || Array.isArray(input)) {
|
|
2556
|
-
throw new Error("bucket variants must be an object of { <name>: { width?, height?, format? } }");
|
|
2557
|
-
}
|
|
2558
|
-
const out = {};
|
|
2559
|
-
for (const [name, spec] of Object.entries(input)) {
|
|
2560
|
-
if (!/^[a-z0-9][a-z0-9_-]*$/i.test(name)) {
|
|
2561
|
-
throw new Error(
|
|
2562
|
-
`bucket variant name "${name}" must be alphanumeric with optional - or _ (e.g. "thumb", "card_2x")`
|
|
2563
|
-
);
|
|
2564
|
-
}
|
|
2565
|
-
if (spec === null || typeof spec !== "object" || Array.isArray(spec)) {
|
|
2566
|
-
throw new Error(`bucket variant "${name}" must be an object like { width: 100, height: 100 }`);
|
|
2567
|
-
}
|
|
2568
|
-
for (const dim of ["width", "height"]) {
|
|
2569
|
-
const value = spec[dim];
|
|
2570
|
-
if (value === void 0) continue;
|
|
2571
|
-
if (typeof value !== "number" || !Number.isInteger(value) || value <= 0) {
|
|
2572
|
-
throw new Error(`bucket variant "${name}" ${dim} must be a positive integer, got ${String(value)}`);
|
|
2573
|
-
}
|
|
2574
|
-
}
|
|
2575
|
-
if (spec.width === void 0 && spec.height === void 0) {
|
|
2576
|
-
throw new Error(`bucket variant "${name}" must set width and/or height`);
|
|
2577
|
-
}
|
|
2578
|
-
if (spec.format !== void 0 && !VARIANT_FORMATS.includes(spec.format)) {
|
|
2579
|
-
throw new Error(
|
|
2580
|
-
`bucket variant "${name}" format must be one of ${VARIANT_FORMATS.join(", ")}, got "${String(spec.format)}"`
|
|
2581
|
-
);
|
|
2582
|
-
}
|
|
2583
|
-
if (spec.fit !== void 0 && !VARIANT_FITS.includes(spec.fit)) {
|
|
2584
|
-
throw new Error(
|
|
2585
|
-
`bucket variant "${name}" fit must be one of ${VARIANT_FITS.join(", ")}, got "${String(spec.fit)}"`
|
|
2586
|
-
);
|
|
2587
|
-
}
|
|
2588
|
-
if (spec.quality !== void 0) {
|
|
2589
|
-
const q = spec.quality;
|
|
2590
|
-
if (typeof q !== "number" || !Number.isInteger(q) || q < 1 || q > 100) {
|
|
2591
|
-
throw new Error(`bucket variant "${name}" quality must be an integer 1\u2013100, got ${String(q)}`);
|
|
2592
|
-
}
|
|
2593
|
-
}
|
|
2594
|
-
if (spec.fit === "cover" && (spec.width === void 0 || spec.height === void 0)) {
|
|
2595
|
-
throw new Error(`bucket variant "${name}" uses fit "cover", which needs BOTH width and height`);
|
|
2596
|
-
}
|
|
2597
|
-
out[name] = {
|
|
2598
|
-
...spec.width !== void 0 ? { width: spec.width } : {},
|
|
2599
|
-
...spec.height !== void 0 ? { height: spec.height } : {},
|
|
2600
|
-
...spec.fit !== void 0 ? { fit: spec.fit } : {},
|
|
2601
|
-
...spec.format !== void 0 ? { format: spec.format } : {},
|
|
2602
|
-
...spec.quality !== void 0 ? { quality: spec.quality } : {}
|
|
2603
|
-
};
|
|
2604
|
-
}
|
|
2605
|
-
return out;
|
|
2428
|
+
// src/stack-gen.ts
|
|
2429
|
+
function liveNames(names) {
|
|
2430
|
+
return [...new Set(names)].sort();
|
|
2606
2431
|
}
|
|
2607
|
-
|
|
2608
|
-
|
|
2609
|
-
kb: 1024,
|
|
2610
|
-
mb: 1024 ** 2,
|
|
2611
|
-
gb: 1024 ** 3,
|
|
2612
|
-
tb: 1024 ** 4
|
|
2613
|
-
};
|
|
2614
|
-
function parseFileSizeLimit(input) {
|
|
2615
|
-
if (typeof input === "number") {
|
|
2616
|
-
if (!Number.isFinite(input) || input < 0 || !Number.isInteger(input)) {
|
|
2617
|
-
throw new Error(
|
|
2618
|
-
`bucket fileSizeLimit must be a non-negative integer number of bytes, got ${input}`
|
|
2619
|
-
);
|
|
2620
|
-
}
|
|
2621
|
-
return input;
|
|
2622
|
-
}
|
|
2623
|
-
const trimmed = input.trim();
|
|
2624
|
-
const match = /^(\d+(?:\.\d+)?)\s*([a-zA-Z]+)?$/.exec(trimmed);
|
|
2625
|
-
if (!match) {
|
|
2626
|
-
throw new Error(
|
|
2627
|
-
`bucket fileSizeLimit string must be "<number><unit>" like "5MB" or "1GB", got "${input}"`
|
|
2628
|
-
);
|
|
2629
|
-
}
|
|
2630
|
-
const value = Number.parseFloat(match[1]);
|
|
2631
|
-
const unit = (match[2] ?? "b").toLowerCase();
|
|
2632
|
-
const multiplier = UNIT_BYTES[unit];
|
|
2633
|
-
if (multiplier === void 0) {
|
|
2634
|
-
throw new Error(
|
|
2635
|
-
`bucket fileSizeLimit has an unknown unit "${match[2]}" \u2014 use B, KB, MB, GB, or TB (e.g. "5MB")`
|
|
2636
|
-
);
|
|
2637
|
-
}
|
|
2638
|
-
const bytes = value * multiplier;
|
|
2639
|
-
if (!Number.isFinite(bytes) || bytes < 0) {
|
|
2640
|
-
throw new Error(`bucket fileSizeLimit resolved to an invalid byte count: ${bytes}`);
|
|
2641
|
-
}
|
|
2642
|
-
return Math.round(bytes);
|
|
2432
|
+
function memberName(name) {
|
|
2433
|
+
return /^[A-Za-z_$][A-Za-z0-9_$]*$/.test(name) ? name : JSON.stringify(name);
|
|
2643
2434
|
}
|
|
2644
|
-
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
|
|
2648
|
-
|
|
2649
|
-
if (!Array.isArray(opts.allowedMimeTypes)) {
|
|
2650
|
-
throw new Error("bucket allowedMimeTypes must be an array of MIME-type strings");
|
|
2651
|
-
}
|
|
2652
|
-
for (const mime of opts.allowedMimeTypes) {
|
|
2653
|
-
if (typeof mime !== "string" || !MIME_RE.test(mime.trim())) {
|
|
2654
|
-
throw new Error(
|
|
2655
|
-
`bucket allowedMimeTypes entry "${mime}" is not a valid MIME type (expected "type/subtype", e.g. "image/png")`
|
|
2656
|
-
);
|
|
2657
|
-
}
|
|
2658
|
-
}
|
|
2659
|
-
allowedMimeTypes = [...new Set(opts.allowedMimeTypes.map((m) => m.trim()))];
|
|
2660
|
-
}
|
|
2661
|
-
return {
|
|
2662
|
-
public: opts.public ?? false,
|
|
2663
|
-
fileSizeLimit,
|
|
2664
|
-
allowedMimeTypes,
|
|
2665
|
-
variants: opts.variants === void 0 ? null : normalizeVariants(opts.variants)
|
|
2666
|
-
};
|
|
2435
|
+
function members(names) {
|
|
2436
|
+
if (names.length === 0) return "";
|
|
2437
|
+
return `
|
|
2438
|
+
${names.map((n) => ` ${memberName(n)}: true;`).join("\n")}
|
|
2439
|
+
`;
|
|
2667
2440
|
}
|
|
2668
|
-
function
|
|
2669
|
-
|
|
2670
|
-
|
|
2671
|
-
|
|
2672
|
-
const
|
|
2673
|
-
|
|
2674
|
-
|
|
2675
|
-
|
|
2676
|
-
|
|
2677
|
-
|
|
2678
|
-
|
|
2679
|
-
|
|
2680
|
-
|
|
2681
|
-
|
|
2682
|
-
}
|
|
2683
|
-
|
|
2684
|
-
// src/config/egress.ts
|
|
2685
|
-
var EGRESS_CONFIG_KIND = "egress";
|
|
2686
|
-
var EGRESS_TIMEOUT_MIN_MS = 1e3;
|
|
2687
|
-
var EGRESS_TIMEOUT_MAX_MS = 3e5;
|
|
2688
|
-
var EGRESS_TIMEOUT_DEFAULT_MS = 3e4;
|
|
2689
|
-
function defineEgress(input) {
|
|
2690
|
-
if (input === null || typeof input !== "object" || !Array.isArray(input.hosts)) {
|
|
2691
|
-
throw new Error("defineEgress expects { hosts: string[] }");
|
|
2692
|
-
}
|
|
2693
|
-
const hosts = [];
|
|
2694
|
-
for (const h of input.hosts) {
|
|
2695
|
-
if (typeof h !== "string" || h.trim().length === 0) {
|
|
2696
|
-
throw new Error("defineEgress: every host must be a non-empty string");
|
|
2697
|
-
}
|
|
2698
|
-
hosts.push(h.trim().toLowerCase());
|
|
2699
|
-
}
|
|
2700
|
-
if (input.timeoutMs === void 0) {
|
|
2701
|
-
return { __config: EGRESS_CONFIG_KIND, hosts };
|
|
2702
|
-
}
|
|
2703
|
-
const timeoutMs = input.timeoutMs;
|
|
2704
|
-
if (typeof timeoutMs !== "number" || !Number.isInteger(timeoutMs)) {
|
|
2705
|
-
throw new Error("defineEgress: timeoutMs must be an integer number of milliseconds");
|
|
2706
|
-
}
|
|
2707
|
-
if (timeoutMs < EGRESS_TIMEOUT_MIN_MS || timeoutMs > EGRESS_TIMEOUT_MAX_MS) {
|
|
2708
|
-
throw new Error(
|
|
2709
|
-
`defineEgress: timeoutMs must be between ${EGRESS_TIMEOUT_MIN_MS} and ${EGRESS_TIMEOUT_MAX_MS} (got ${timeoutMs})`
|
|
2710
|
-
);
|
|
2711
|
-
}
|
|
2712
|
-
return { __config: EGRESS_CONFIG_KIND, hosts, timeoutMs };
|
|
2441
|
+
function bucketMembers(buckets) {
|
|
2442
|
+
const normalized = buckets.map((b) => typeof b === "string" ? { name: b, variants: [] } : b);
|
|
2443
|
+
const byName = /* @__PURE__ */ new Map();
|
|
2444
|
+
for (const b of normalized) byName.set(b.name, b.variants);
|
|
2445
|
+
const names = [...byName.keys()].sort();
|
|
2446
|
+
if (names.length === 0) return "";
|
|
2447
|
+
const rows = names.map((name) => {
|
|
2448
|
+
const variants = [...new Set(byName.get(name) ?? [])].sort();
|
|
2449
|
+
const union = variants.length === 0 ? "never" : variants.map((v) => JSON.stringify(v)).join(" | ");
|
|
2450
|
+
return ` ${memberName(name)}: { variants: ${union} };`;
|
|
2451
|
+
});
|
|
2452
|
+
return `
|
|
2453
|
+
${rows.join("\n")}
|
|
2454
|
+
`;
|
|
2713
2455
|
}
|
|
2456
|
+
function makeStackDts(names) {
|
|
2457
|
+
const secrets = liveNames(names.secrets);
|
|
2458
|
+
const flags = liveNames(names.flags);
|
|
2459
|
+
return `// palbase-stack.d.ts \u2014 GENERATED by @palbase/backend. Do not edit.
|
|
2460
|
+
// Source: the linked environment's stack.
|
|
2714
2461
|
|
|
2715
|
-
|
|
2716
|
-
|
|
2717
|
-
var RESERVED_SECRET_PREFIX = "PB_NOTIFICATIONS";
|
|
2718
|
-
var PROVIDER_CATALOG = {
|
|
2719
|
-
apns: {
|
|
2720
|
-
channel: "push",
|
|
2721
|
-
required: ["teamId", "keyId", "bundleId"],
|
|
2722
|
-
optional: ["isProduction"],
|
|
2723
|
-
secrets: ["p8"]
|
|
2724
|
-
},
|
|
2725
|
-
fcm: {
|
|
2726
|
-
channel: "push",
|
|
2727
|
-
required: [],
|
|
2728
|
-
optional: [],
|
|
2729
|
-
secrets: ["serviceAccount"]
|
|
2730
|
-
},
|
|
2731
|
-
sendgrid: {
|
|
2732
|
-
channel: "email",
|
|
2733
|
-
required: ["fromDomain"],
|
|
2734
|
-
optional: [],
|
|
2735
|
-
secrets: ["apiKey"]
|
|
2736
|
-
},
|
|
2737
|
-
ses: {
|
|
2738
|
-
channel: "email",
|
|
2739
|
-
required: ["region", "accessKeyId", "fromDomain"],
|
|
2740
|
-
optional: [],
|
|
2741
|
-
secrets: ["secretAccessKey"]
|
|
2742
|
-
},
|
|
2743
|
-
smtp: {
|
|
2744
|
-
channel: "email",
|
|
2745
|
-
required: ["host", "port", "fromEmail"],
|
|
2746
|
-
optional: ["username", "useStarttls"],
|
|
2747
|
-
secrets: ["password"]
|
|
2748
|
-
},
|
|
2749
|
-
acs: {
|
|
2750
|
-
channel: "email",
|
|
2751
|
-
required: ["fromEmail"],
|
|
2752
|
-
optional: ["fromName"],
|
|
2753
|
-
secrets: ["connectionString"]
|
|
2754
|
-
},
|
|
2755
|
-
twilio: {
|
|
2756
|
-
channel: "sms",
|
|
2757
|
-
// account_sid required; one of from_number / messaging_service_sid required
|
|
2758
|
-
// (enforced by the refine in buildProvider, not by the flat `required` list).
|
|
2759
|
-
required: ["accountSid"],
|
|
2760
|
-
optional: ["fromNumber", "messagingServiceSid"],
|
|
2761
|
-
secrets: ["authToken"]
|
|
2762
|
-
}
|
|
2763
|
-
};
|
|
2764
|
-
var DEFAULT_TEMPLATE_LOCALE = "en";
|
|
2765
|
-
function extractVariables(...sources) {
|
|
2766
|
-
const found = /* @__PURE__ */ new Set();
|
|
2767
|
-
for (const src of sources) {
|
|
2768
|
-
if (!src) continue;
|
|
2769
|
-
for (const match of src.matchAll(/\{\{\{?\s*([^{}]+?)\s*\}?\}\}/g)) {
|
|
2770
|
-
const token = (match[1] ?? "").trim();
|
|
2771
|
-
if (token === "" || "#/^!>&".includes(token.charAt(0)) || token === "else") continue;
|
|
2772
|
-
if (/\s/.test(token)) continue;
|
|
2773
|
-
const root = token.split(".")[0] ?? "";
|
|
2774
|
-
if (root === "" || root === "this" || root.startsWith("@")) continue;
|
|
2775
|
-
found.add(root);
|
|
2776
|
-
}
|
|
2777
|
-
}
|
|
2778
|
-
return [...found].sort();
|
|
2779
|
-
}
|
|
2780
|
-
function buildEmailTemplate(slug, opts) {
|
|
2781
|
-
const row = (locale, c) => {
|
|
2782
|
-
if (!c.subject) throw new Error(`notifications: email template "${slug}" (${locale}) needs a subject`);
|
|
2783
|
-
if (!c.html) throw new Error(`notifications: email template "${slug}" (${locale}) needs an html body`);
|
|
2784
|
-
const def = {
|
|
2785
|
-
slug,
|
|
2786
|
-
locale,
|
|
2787
|
-
subject: c.subject,
|
|
2788
|
-
html_body: c.html,
|
|
2789
|
-
variables: c.variables ? [...c.variables] : extractVariables(c.subject, c.html, c.text)
|
|
2790
|
-
};
|
|
2791
|
-
if (c.text !== void 0) def.text_body = c.text;
|
|
2792
|
-
return def;
|
|
2793
|
-
};
|
|
2794
|
-
const rows = [row(opts.locale ?? DEFAULT_TEMPLATE_LOCALE, opts)];
|
|
2795
|
-
for (const [locale, content] of Object.entries(opts.locales ?? {})) {
|
|
2796
|
-
rows.push(row(locale, content));
|
|
2797
|
-
}
|
|
2798
|
-
return rows;
|
|
2799
|
-
}
|
|
2800
|
-
function buildSmsTemplate(slug, opts) {
|
|
2801
|
-
const row = (locale, c) => {
|
|
2802
|
-
if (!c.body) throw new Error(`notifications: sms template "${slug}" (${locale}) needs a body`);
|
|
2803
|
-
return {
|
|
2804
|
-
slug,
|
|
2805
|
-
locale,
|
|
2806
|
-
body: c.body,
|
|
2807
|
-
variables: c.variables ? [...c.variables] : extractVariables(c.body)
|
|
2808
|
-
};
|
|
2809
|
-
};
|
|
2810
|
-
const rows = [row(opts.locale ?? DEFAULT_TEMPLATE_LOCALE, opts)];
|
|
2811
|
-
for (const [locale, content] of Object.entries(opts.locales ?? {})) {
|
|
2812
|
-
rows.push(row(locale, content));
|
|
2813
|
-
}
|
|
2814
|
-
return rows;
|
|
2815
|
-
}
|
|
2816
|
-
function buildTemplates(input) {
|
|
2817
|
-
const out = { email: [], sms: [] };
|
|
2818
|
-
const seen = /* @__PURE__ */ new Set();
|
|
2819
|
-
const claim = (channel, slug, locale) => {
|
|
2820
|
-
const key = `${channel}/${slug}/${locale}`;
|
|
2821
|
-
if (seen.has(key)) {
|
|
2822
|
-
throw new Error(`notifications: ${channel} template "${slug}" declares locale "${locale}" twice`);
|
|
2823
|
-
}
|
|
2824
|
-
seen.add(key);
|
|
2825
|
-
};
|
|
2826
|
-
for (const [slug, opts] of Object.entries(input.email ?? {})) {
|
|
2827
|
-
for (const row of buildEmailTemplate(slug, opts)) {
|
|
2828
|
-
claim("email", row.slug, row.locale);
|
|
2829
|
-
out.email.push(row);
|
|
2830
|
-
}
|
|
2831
|
-
}
|
|
2832
|
-
for (const [slug, opts] of Object.entries(input.sms ?? {})) {
|
|
2833
|
-
for (const row of buildSmsTemplate(slug, opts)) {
|
|
2834
|
-
claim("sms", row.slug, row.locale);
|
|
2835
|
-
out.sms.push(row);
|
|
2836
|
-
}
|
|
2837
|
-
}
|
|
2838
|
-
return out;
|
|
2839
|
-
}
|
|
2840
|
-
function buildProvider(name, opts) {
|
|
2841
|
-
const entry = PROVIDER_CATALOG[name];
|
|
2842
|
-
const src = { ...opts };
|
|
2843
|
-
const enabled = src.enabled === void 0 ? false : Boolean(src.enabled);
|
|
2844
|
-
if (!enabled) {
|
|
2845
|
-
return { enabled: false };
|
|
2846
|
-
}
|
|
2847
|
-
const def = { enabled: true };
|
|
2848
|
-
for (const field of entry.required) {
|
|
2849
|
-
const value = src[field];
|
|
2850
|
-
if (value === void 0 || value === null || value === "") {
|
|
2851
|
-
throw new Error(
|
|
2852
|
-
`notifications: provider "${name}" is enabled but missing required field "${field}"`
|
|
2853
|
-
);
|
|
2854
|
-
}
|
|
2855
|
-
def[field] = value;
|
|
2856
|
-
}
|
|
2857
|
-
for (const field of entry.optional) {
|
|
2858
|
-
if (src[field] !== void 0) {
|
|
2859
|
-
def[field] = src[field];
|
|
2860
|
-
}
|
|
2861
|
-
}
|
|
2862
|
-
if (name === "twilio") {
|
|
2863
|
-
const hasFrom = Boolean(def.fromNumber);
|
|
2864
|
-
const hasMsg = Boolean(def.messagingServiceSid);
|
|
2865
|
-
if (!hasFrom && !hasMsg) {
|
|
2866
|
-
throw new Error(
|
|
2867
|
-
'notifications: provider "twilio" requires one of "fromNumber" or "messagingServiceSid"'
|
|
2868
|
-
);
|
|
2869
|
-
}
|
|
2870
|
-
}
|
|
2871
|
-
return def;
|
|
2872
|
-
}
|
|
2873
|
-
function defineNotifications(input) {
|
|
2874
|
-
if (input === null || typeof input !== "object") {
|
|
2875
|
-
throw new Error("defineNotifications expects { push?, email?, sms? }");
|
|
2876
|
-
}
|
|
2877
|
-
const config = {
|
|
2878
|
-
__config: NOTIFICATIONS_CONFIG_KIND,
|
|
2879
|
-
push: {},
|
|
2880
|
-
email: {},
|
|
2881
|
-
sms: {},
|
|
2882
|
-
templates: buildTemplates(input.templates ?? {})
|
|
2883
|
-
};
|
|
2884
|
-
const push = input.push ?? {};
|
|
2885
|
-
if (push.apns !== void 0) config.push.apns = buildProvider("apns", push.apns);
|
|
2886
|
-
if (push.fcm !== void 0) config.push.fcm = buildProvider("fcm", push.fcm);
|
|
2887
|
-
const email = input.email ?? {};
|
|
2888
|
-
if (email.sendgrid !== void 0) config.email.sendgrid = buildProvider("sendgrid", email.sendgrid);
|
|
2889
|
-
if (email.ses !== void 0) config.email.ses = buildProvider("ses", email.ses);
|
|
2890
|
-
if (email.smtp !== void 0) config.email.smtp = buildProvider("smtp", email.smtp);
|
|
2891
|
-
if (email.acs !== void 0) config.email.acs = buildProvider("acs", email.acs);
|
|
2892
|
-
const sms = input.sms ?? {};
|
|
2893
|
-
if (sms.twilio !== void 0) config.sms.twilio = buildProvider("twilio", sms.twilio);
|
|
2894
|
-
return config;
|
|
2895
|
-
}
|
|
2896
|
-
function reservedSecretKey(provider, secretField) {
|
|
2897
|
-
return `${RESERVED_SECRET_PREFIX}_${camelToUpperSnake(provider)}_${camelToUpperSnake(secretField)}`;
|
|
2898
|
-
}
|
|
2899
|
-
function camelToUpperSnake(s) {
|
|
2900
|
-
return s.replace(/([a-z0-9])([A-Z])/g, "$1_$2").toUpperCase();
|
|
2901
|
-
}
|
|
2462
|
+
declare module "@palbase/backend/stack" {
|
|
2463
|
+
interface Secrets {${members(secrets)}}
|
|
2902
2464
|
|
|
2903
|
-
|
|
2904
|
-
var TEST_USERS_CONFIG_KIND = "test-users";
|
|
2905
|
-
var TEST_USER_NAME_RE = /^[a-zA-Z][a-zA-Z0-9_]*$/;
|
|
2906
|
-
var EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
|
2907
|
-
var E164_RE = /^\+[1-9]\d{1,14}$/;
|
|
2908
|
-
var OTP_LENGTH = 6;
|
|
2909
|
-
var OTP_RE = /^\d{6}$/;
|
|
2910
|
-
var MIN_PASSWORD_LENGTH = 8;
|
|
2911
|
-
function isPlainObject2(value) {
|
|
2912
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2913
|
-
}
|
|
2914
|
-
function normalizeSeed(seed, userName) {
|
|
2915
|
-
if (seed === void 0) return {};
|
|
2916
|
-
if (!isPlainObject2(seed)) {
|
|
2917
|
-
throw new Error(`testUser ${JSON.stringify(userName)}: seed must be { <table>: [ {...} ] }`);
|
|
2918
|
-
}
|
|
2919
|
-
const out = {};
|
|
2920
|
-
for (const [table, rows] of Object.entries(seed)) {
|
|
2921
|
-
if (!Array.isArray(rows)) {
|
|
2922
|
-
throw new Error(
|
|
2923
|
-
`testUser ${JSON.stringify(userName)}: seed.${table} must be an array of rows`
|
|
2924
|
-
);
|
|
2925
|
-
}
|
|
2926
|
-
if (rows.length === 0) {
|
|
2927
|
-
throw new Error(
|
|
2928
|
-
`testUser ${JSON.stringify(userName)}: seed.${table} is empty \u2014 remove it or add a row`
|
|
2929
|
-
);
|
|
2930
|
-
}
|
|
2931
|
-
for (const row of rows) {
|
|
2932
|
-
if (!isPlainObject2(row)) {
|
|
2933
|
-
throw new Error(
|
|
2934
|
-
`testUser ${JSON.stringify(userName)}: every row in seed.${table} must be an object`
|
|
2935
|
-
);
|
|
2936
|
-
}
|
|
2937
|
-
}
|
|
2938
|
-
out[table] = rows;
|
|
2939
|
-
}
|
|
2940
|
-
return out;
|
|
2941
|
-
}
|
|
2942
|
-
function testUser(opts = {}) {
|
|
2943
|
-
if (!isPlainObject2(opts)) {
|
|
2944
|
-
throw new Error("testUser() expects { email?, password?, phone?, otp?, seed? }");
|
|
2945
|
-
}
|
|
2946
|
-
const hasEmail = opts.email !== void 0;
|
|
2947
|
-
const hasPassword = opts.password !== void 0;
|
|
2948
|
-
if (hasEmail !== hasPassword) {
|
|
2949
|
-
throw new Error(
|
|
2950
|
-
"testUser() needs email AND password together (a fixture with stable credentials) or neither (a template the server generates credentials for)"
|
|
2951
|
-
);
|
|
2952
|
-
}
|
|
2953
|
-
let email = null;
|
|
2954
|
-
let password = null;
|
|
2955
|
-
if (hasEmail) {
|
|
2956
|
-
if (typeof opts.email !== "string" || !EMAIL_RE.test(opts.email)) {
|
|
2957
|
-
throw new Error(`testUser email ${JSON.stringify(opts.email)} is not a valid address`);
|
|
2958
|
-
}
|
|
2959
|
-
if (typeof opts.password !== "string" || opts.password.length < MIN_PASSWORD_LENGTH) {
|
|
2960
|
-
throw new Error(
|
|
2961
|
-
`testUser password must be at least ${MIN_PASSWORD_LENGTH} characters (got ${typeof opts.password === "string" ? opts.password.length : 0})`
|
|
2962
|
-
);
|
|
2963
|
-
}
|
|
2964
|
-
email = opts.email;
|
|
2965
|
-
password = opts.password;
|
|
2966
|
-
}
|
|
2967
|
-
const hasPhone = opts.phone !== void 0;
|
|
2968
|
-
const hasOTP = opts.otp !== void 0;
|
|
2969
|
-
if (hasPhone !== hasOTP) {
|
|
2970
|
-
throw new Error(
|
|
2971
|
-
"testUser() needs phone AND otp together \u2014 nothing can receive an SMS at a fabricated number, so a phone fixture without its fixed code can never be signed in as"
|
|
2972
|
-
);
|
|
2973
|
-
}
|
|
2974
|
-
let phone = null;
|
|
2975
|
-
let otp = null;
|
|
2976
|
-
if (hasPhone) {
|
|
2977
|
-
if (typeof opts.phone !== "string" || !E164_RE.test(opts.phone)) {
|
|
2978
|
-
throw new Error(
|
|
2979
|
-
`testUser phone ${JSON.stringify(opts.phone)} must be E.164, e.g. "+905551112233"`
|
|
2980
|
-
);
|
|
2981
|
-
}
|
|
2982
|
-
if (typeof opts.otp !== "string" || !OTP_RE.test(opts.otp)) {
|
|
2983
|
-
throw new Error(
|
|
2984
|
-
`testUser otp ${JSON.stringify(opts.otp)} must be ${OTP_LENGTH} digits`
|
|
2985
|
-
);
|
|
2986
|
-
}
|
|
2987
|
-
phone = opts.phone;
|
|
2988
|
-
otp = opts.otp;
|
|
2989
|
-
}
|
|
2990
|
-
return { email, password, phone, otp, seed: normalizeSeed(opts.seed, email ?? "<template>") };
|
|
2991
|
-
}
|
|
2992
|
-
function defineTestUsers(input) {
|
|
2993
|
-
if (!isPlainObject2(input) || !isPlainObject2(input.users)) {
|
|
2994
|
-
throw new Error("defineTestUsers expects { users: { <name>: testUser({...}) } }");
|
|
2995
|
-
}
|
|
2996
|
-
const users = {};
|
|
2997
|
-
const emails = /* @__PURE__ */ new Map();
|
|
2998
|
-
for (const name of Object.keys(input.users)) {
|
|
2999
|
-
if (!TEST_USER_NAME_RE.test(name)) {
|
|
3000
|
-
throw new Error(
|
|
3001
|
-
`test user name ${JSON.stringify(name)} is invalid \u2014 must start with a letter and contain only letters, digits, and underscores`
|
|
3002
|
-
);
|
|
3003
|
-
}
|
|
3004
|
-
const def = input.users[name];
|
|
3005
|
-
if (def === void 0) continue;
|
|
3006
|
-
if (def.email !== null) {
|
|
3007
|
-
const lower = def.email.toLowerCase();
|
|
3008
|
-
const already = emails.get(lower);
|
|
3009
|
-
if (already !== void 0) {
|
|
3010
|
-
throw new Error(
|
|
3011
|
-
`test users ${JSON.stringify(already)} and ${JSON.stringify(name)} declare the same e-mail ${JSON.stringify(def.email)}`
|
|
3012
|
-
);
|
|
3013
|
-
}
|
|
3014
|
-
emails.set(lower, name);
|
|
3015
|
-
}
|
|
3016
|
-
users[name] = def;
|
|
3017
|
-
}
|
|
3018
|
-
return { __config: TEST_USERS_CONFIG_KIND, users };
|
|
3019
|
-
}
|
|
2465
|
+
interface Flags {${members(flags)}}
|
|
3020
2466
|
|
|
3021
|
-
|
|
3022
|
-
var FLAGS_CONFIG_KIND = "flags";
|
|
3023
|
-
var FLAG_KEY_RE = /^[a-zA-Z][a-zA-Z0-9_]*$/;
|
|
3024
|
-
function valueMatchesType(type, value) {
|
|
3025
|
-
switch (type) {
|
|
3026
|
-
case "boolean":
|
|
3027
|
-
return typeof value === "boolean";
|
|
3028
|
-
case "number":
|
|
3029
|
-
return typeof value === "number" && Number.isFinite(value);
|
|
3030
|
-
case "string":
|
|
3031
|
-
return typeof value === "string";
|
|
3032
|
-
case "json":
|
|
3033
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3034
|
-
}
|
|
3035
|
-
}
|
|
3036
|
-
function flag(opts) {
|
|
3037
|
-
if (opts === null || typeof opts !== "object") {
|
|
3038
|
-
throw new Error("flag() expects { type, default, variants?, description? }");
|
|
3039
|
-
}
|
|
3040
|
-
const { type } = opts;
|
|
3041
|
-
if (type !== "boolean" && type !== "number" && type !== "string" && type !== "json") {
|
|
3042
|
-
throw new Error(`flag type must be "boolean", "number", "string", or "json", got ${JSON.stringify(type)}`);
|
|
3043
|
-
}
|
|
3044
|
-
if (!valueMatchesType(type, opts.default)) {
|
|
3045
|
-
throw new Error(
|
|
3046
|
-
`flag default ${JSON.stringify(opts.default)} does not match type "${type}"`
|
|
3047
|
-
);
|
|
3048
|
-
}
|
|
3049
|
-
let variants = null;
|
|
3050
|
-
if (opts.variants !== void 0) {
|
|
3051
|
-
if (opts.type !== "string") {
|
|
3052
|
-
throw new Error(`flag variants are only valid for type "string" (got type "${type}")`);
|
|
3053
|
-
}
|
|
3054
|
-
if (!Array.isArray(opts.variants)) {
|
|
3055
|
-
throw new Error("flag variants must be an array of strings");
|
|
3056
|
-
}
|
|
3057
|
-
for (const v of opts.variants) {
|
|
3058
|
-
if (typeof v !== "string" || v.length === 0) {
|
|
3059
|
-
throw new Error(`flag variant ${JSON.stringify(v)} must be a non-empty string`);
|
|
3060
|
-
}
|
|
3061
|
-
}
|
|
3062
|
-
variants = [...new Set(opts.variants)];
|
|
3063
|
-
if (variants.length === 0) {
|
|
3064
|
-
throw new Error("flag variants must be a non-empty list when supplied");
|
|
3065
|
-
}
|
|
3066
|
-
if (!variants.includes(opts.default)) {
|
|
3067
|
-
throw new Error(
|
|
3068
|
-
`flag default ${JSON.stringify(opts.default)} is not one of its variants [${variants.map((v) => JSON.stringify(v)).join(", ")}]`
|
|
3069
|
-
);
|
|
3070
|
-
}
|
|
3071
|
-
}
|
|
3072
|
-
let conditions = null;
|
|
3073
|
-
if (opts.conditions !== void 0) {
|
|
3074
|
-
if (!Array.isArray(opts.conditions)) {
|
|
3075
|
-
throw new Error("flag conditions must be an array of { when, value }");
|
|
3076
|
-
}
|
|
3077
|
-
const compiled = [];
|
|
3078
|
-
for (const [i, branch] of opts.conditions.entries()) {
|
|
3079
|
-
if (branch === null || typeof branch !== "object") {
|
|
3080
|
-
throw new Error(`flag condition ${i} must be an object { when, value }`);
|
|
3081
|
-
}
|
|
3082
|
-
if (typeof branch.when !== "string" || branch.when.trim().length === 0) {
|
|
3083
|
-
throw new Error(`flag condition ${i} needs a non-empty "when" expression`);
|
|
3084
|
-
}
|
|
3085
|
-
if (!valueMatchesType(type, branch.value)) {
|
|
3086
|
-
throw new Error(
|
|
3087
|
-
`flag condition ${i} value ${JSON.stringify(branch.value)} does not match type "${type}"`
|
|
3088
|
-
);
|
|
3089
|
-
}
|
|
3090
|
-
if (variants !== null && !variants.includes(branch.value)) {
|
|
3091
|
-
throw new Error(
|
|
3092
|
-
`flag condition ${i} value ${JSON.stringify(branch.value)} is not one of its variants [${variants.map((v) => JSON.stringify(v)).join(", ")}]`
|
|
3093
|
-
);
|
|
3094
|
-
}
|
|
3095
|
-
compiled.push({ when: branch.when.trim(), value: branch.value });
|
|
3096
|
-
}
|
|
3097
|
-
conditions = compiled.length > 0 ? compiled : null;
|
|
3098
|
-
}
|
|
3099
|
-
let description = null;
|
|
3100
|
-
if (opts.description !== void 0) {
|
|
3101
|
-
if (typeof opts.description !== "string") {
|
|
3102
|
-
throw new Error("flag description must be a string");
|
|
3103
|
-
}
|
|
3104
|
-
const trimmed = opts.description.trim();
|
|
3105
|
-
description = trimmed.length > 0 ? trimmed : null;
|
|
3106
|
-
}
|
|
3107
|
-
return {
|
|
3108
|
-
type,
|
|
3109
|
-
default: opts.default,
|
|
3110
|
-
variants,
|
|
3111
|
-
conditions,
|
|
3112
|
-
description
|
|
3113
|
-
};
|
|
3114
|
-
}
|
|
3115
|
-
function defineFlags(input) {
|
|
3116
|
-
if (input === null || typeof input !== "object" || typeof input.flags !== "object") {
|
|
3117
|
-
throw new Error("defineFlags expects { flags: { <key>: flag({...}) } }");
|
|
3118
|
-
}
|
|
3119
|
-
const flags = {};
|
|
3120
|
-
for (const key of Object.keys(input.flags)) {
|
|
3121
|
-
if (!FLAG_KEY_RE.test(key)) {
|
|
3122
|
-
throw new Error(
|
|
3123
|
-
`flag key ${JSON.stringify(key)} is invalid \u2014 must start with a letter and contain only letters, digits, and underscores`
|
|
3124
|
-
);
|
|
3125
|
-
}
|
|
3126
|
-
const def = input.flags[key];
|
|
3127
|
-
if (def === void 0) continue;
|
|
3128
|
-
flags[key] = def;
|
|
3129
|
-
}
|
|
3130
|
-
return { __config: FLAGS_CONFIG_KIND, flags };
|
|
2467
|
+
interface Buckets {${bucketMembers(names.buckets)}}
|
|
3131
2468
|
}
|
|
3132
2469
|
|
|
3133
|
-
|
|
3134
|
-
|
|
3135
|
-
var SECRET_NAME_RE = /^[A-Z][A-Z0-9_]*$/;
|
|
3136
|
-
function secret(name, opts = {}) {
|
|
3137
|
-
if (typeof name !== "string" || !SECRET_NAME_RE.test(name)) {
|
|
3138
|
-
throw new Error(
|
|
3139
|
-
`secret name ${JSON.stringify(name)} is invalid \u2014 must be an environment-variable name: an uppercase letter, then uppercase letters, digits, and underscores (e.g. "NEO4J_PASSWORD")`
|
|
3140
|
-
);
|
|
3141
|
-
}
|
|
3142
|
-
if (opts === null || typeof opts !== "object") {
|
|
3143
|
-
throw new Error(`secret("${name}") expects { required?, description? }`);
|
|
3144
|
-
}
|
|
3145
|
-
let required = true;
|
|
3146
|
-
if (opts.required !== void 0) {
|
|
3147
|
-
if (typeof opts.required !== "boolean") {
|
|
3148
|
-
throw new Error(`secret("${name}") required must be a boolean`);
|
|
3149
|
-
}
|
|
3150
|
-
required = opts.required;
|
|
3151
|
-
}
|
|
3152
|
-
let description = null;
|
|
3153
|
-
if (opts.description !== void 0) {
|
|
3154
|
-
if (typeof opts.description !== "string") {
|
|
3155
|
-
throw new Error(`secret("${name}") description must be a string`);
|
|
3156
|
-
}
|
|
3157
|
-
const trimmed = opts.description.trim();
|
|
3158
|
-
description = trimmed.length > 0 ? trimmed : null;
|
|
3159
|
-
}
|
|
3160
|
-
return { name, required, description };
|
|
3161
|
-
}
|
|
3162
|
-
function defineSecrets(input) {
|
|
3163
|
-
if (input === null || typeof input !== "object" || !Array.isArray(input.secrets)) {
|
|
3164
|
-
throw new Error('defineSecrets expects { secrets: [secret("NAME"), ...] }');
|
|
3165
|
-
}
|
|
3166
|
-
const byName = /* @__PURE__ */ new Map();
|
|
3167
|
-
for (const def of input.secrets) {
|
|
3168
|
-
if (def === null || typeof def !== "object" || typeof def.name !== "string" || typeof def.required !== "boolean") {
|
|
3169
|
-
throw new Error(
|
|
3170
|
-
`defineSecrets: every entry must come from secret() \u2014 got ${JSON.stringify(def)}`
|
|
3171
|
-
);
|
|
3172
|
-
}
|
|
3173
|
-
if (byName.has(def.name)) {
|
|
3174
|
-
throw new Error(
|
|
3175
|
-
`secret "${def.name}" is declared twice in config/secrets.ts \u2014 remove one; a name has exactly one requirement`
|
|
3176
|
-
);
|
|
3177
|
-
}
|
|
3178
|
-
byName.set(def.name, def);
|
|
3179
|
-
}
|
|
3180
|
-
const secrets = [...byName.values()].sort((a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0);
|
|
3181
|
-
return { __config: SECRETS_CONFIG_KIND, secrets };
|
|
2470
|
+
export {};
|
|
2471
|
+
`;
|
|
3182
2472
|
}
|
|
3183
2473
|
|
|
3184
2474
|
// src/decorators/registry.ts
|
|
@@ -3186,7 +2476,7 @@ var ROUTES = /* @__PURE__ */ Symbol.for("palbase.backend.routes");
|
|
|
3186
2476
|
var PARAM_BUFFER = /* @__PURE__ */ Symbol.for("palbase.backend.paramBuffer");
|
|
3187
2477
|
var RETURN_BUFFER = /* @__PURE__ */ Symbol.for("palbase.backend.returnBuffer");
|
|
3188
2478
|
var THROWS_BUFFER = /* @__PURE__ */ Symbol.for("palbase.backend.throwsBuffer");
|
|
3189
|
-
function
|
|
2479
|
+
function carrierOf(target) {
|
|
3190
2480
|
const ctor = typeof target === "function" ? target : target.constructor ?? target;
|
|
3191
2481
|
return ctor;
|
|
3192
2482
|
}
|
|
@@ -3203,7 +2493,7 @@ function ownParamBuffer(carrier) {
|
|
|
3203
2493
|
return carrier[PARAM_BUFFER];
|
|
3204
2494
|
}
|
|
3205
2495
|
function recordRoute(target, fnName, method, subpath, options) {
|
|
3206
|
-
const carrier =
|
|
2496
|
+
const carrier = carrierOf(target);
|
|
3207
2497
|
const routes = ownRoutes(carrier);
|
|
3208
2498
|
const buffer = ownParamBuffer(carrier);
|
|
3209
2499
|
const params = (buffer[fnName] ?? []).slice().sort((a, b) => a.index - b.index);
|
|
@@ -3219,7 +2509,7 @@ function recordRoute(target, fnName, method, subpath, options) {
|
|
|
3219
2509
|
routes.push(route);
|
|
3220
2510
|
}
|
|
3221
2511
|
function recordParam(target, fnName, meta) {
|
|
3222
|
-
const carrier =
|
|
2512
|
+
const carrier = carrierOf(target);
|
|
3223
2513
|
const buffer = ownParamBuffer(carrier);
|
|
3224
2514
|
(buffer[fnName] ??= []).push(meta);
|
|
3225
2515
|
const routes = carrier[ROUTES];
|
|
@@ -3232,7 +2522,7 @@ function recordParam(target, fnName, meta) {
|
|
|
3232
2522
|
}
|
|
3233
2523
|
}
|
|
3234
2524
|
function recordThrows(target, fnName, throws) {
|
|
3235
|
-
const carrier =
|
|
2525
|
+
const carrier = carrierOf(target);
|
|
3236
2526
|
const routes = carrier[ROUTES];
|
|
3237
2527
|
const route = routes?.find((r) => r.fnName === fnName);
|
|
3238
2528
|
if (route) {
|
|
@@ -3246,7 +2536,7 @@ function recordThrows(target, fnName, throws) {
|
|
|
3246
2536
|
if (throwsBuffer) throwsBuffer[fnName] = throws;
|
|
3247
2537
|
}
|
|
3248
2538
|
function getRoutes(ctor) {
|
|
3249
|
-
const carrier =
|
|
2539
|
+
const carrier = carrierOf(ctor);
|
|
3250
2540
|
const routes = carrier[ROUTES] ?? [];
|
|
3251
2541
|
const returnBuffer = carrier[RETURN_BUFFER];
|
|
3252
2542
|
if (returnBuffer) {
|
|
@@ -3355,12 +2645,12 @@ var Query = makeMethodDecorator("QUERY");
|
|
|
3355
2645
|
// src/decorators/webhook.ts
|
|
3356
2646
|
var WEBHOOK_META = /* @__PURE__ */ Symbol.for("palbase.backend.webhookMeta");
|
|
3357
2647
|
var WEBHOOK_EVENTS = /* @__PURE__ */ Symbol.for("palbase.backend.webhookEvents");
|
|
3358
|
-
function
|
|
2648
|
+
function carrierOf2(ctor) {
|
|
3359
2649
|
return ctor;
|
|
3360
2650
|
}
|
|
3361
2651
|
function Webhook(options) {
|
|
3362
2652
|
return function(ctor) {
|
|
3363
|
-
const carrier =
|
|
2653
|
+
const carrier = carrierOf2(ctor);
|
|
3364
2654
|
Object.defineProperty(carrier, WEBHOOK_META, {
|
|
3365
2655
|
value: options,
|
|
3366
2656
|
enumerable: false,
|
|
@@ -3378,7 +2668,7 @@ function Webhook(options) {
|
|
|
3378
2668
|
}
|
|
3379
2669
|
function On(event) {
|
|
3380
2670
|
return function(target, fnName) {
|
|
3381
|
-
const carrier =
|
|
2671
|
+
const carrier = carrierOf2(target.constructor);
|
|
3382
2672
|
const existing = carrier[WEBHOOK_EVENTS];
|
|
3383
2673
|
const entries = existing ? [...existing] : [];
|
|
3384
2674
|
entries.push({ event, fnName: String(fnName) });
|
|
@@ -3391,7 +2681,7 @@ function On(event) {
|
|
|
3391
2681
|
};
|
|
3392
2682
|
}
|
|
3393
2683
|
function getWebhookConfig(ctor) {
|
|
3394
|
-
const carrier =
|
|
2684
|
+
const carrier = carrierOf2(ctor);
|
|
3395
2685
|
const meta = carrier[WEBHOOK_META];
|
|
3396
2686
|
const entries = carrier[WEBHOOK_EVENTS] ?? [];
|
|
3397
2687
|
if (!meta) {
|
|
@@ -3526,21 +2816,36 @@ function validateUploadConfigShape(c) {
|
|
|
3526
2816
|
if (c === null || typeof c !== "object") {
|
|
3527
2817
|
throw new Error("@Upload config must be an object { bucket, pathTemplate, ... }");
|
|
3528
2818
|
}
|
|
3529
|
-
|
|
2819
|
+
const bucket = c.bucket;
|
|
2820
|
+
if (typeof bucket !== "string" || bucket.length === 0) {
|
|
3530
2821
|
throw new Error("@Upload config.bucket must be a non-empty bucket name");
|
|
3531
2822
|
}
|
|
3532
2823
|
if (typeof c.pathTemplate !== "string" || c.pathTemplate.length === 0) {
|
|
3533
2824
|
throw new Error("@Upload config.pathTemplate must be a non-empty key template");
|
|
3534
2825
|
}
|
|
3535
2826
|
}
|
|
3536
|
-
|
|
3537
|
-
|
|
3538
|
-
|
|
3539
|
-
|
|
3540
|
-
|
|
3541
|
-
|
|
3542
|
-
|
|
3543
|
-
|
|
2827
|
+
|
|
2828
|
+
// src/decorators/sse.ts
|
|
2829
|
+
function Sse(subpath = "", config) {
|
|
2830
|
+
const { auth, rateLimit, ...sseConfig } = config;
|
|
2831
|
+
const options = {
|
|
2832
|
+
sseConfig,
|
|
2833
|
+
...auth !== void 0 ? { auth } : {},
|
|
2834
|
+
...rateLimit !== void 0 ? { rateLimit } : {}
|
|
2835
|
+
};
|
|
2836
|
+
return function(target, propertyKey) {
|
|
2837
|
+
recordRoute(target, String(propertyKey), "POST", subpath, options);
|
|
2838
|
+
};
|
|
2839
|
+
}
|
|
2840
|
+
function SseOut() {
|
|
2841
|
+
return function(target, propertyKey, parameterIndex) {
|
|
2842
|
+
recordParam(target, String(propertyKey), { index: parameterIndex, kind: "sseOut" });
|
|
2843
|
+
};
|
|
2844
|
+
}
|
|
2845
|
+
function Signal() {
|
|
2846
|
+
return function(target, propertyKey, parameterIndex) {
|
|
2847
|
+
recordParam(target, String(propertyKey), { index: parameterIndex, kind: "signal" });
|
|
2848
|
+
};
|
|
3544
2849
|
}
|
|
3545
2850
|
|
|
3546
2851
|
// src/decorators/params.ts
|
|
@@ -3590,6 +2895,93 @@ function defineMiddleware(fn) {
|
|
|
3590
2895
|
return fn;
|
|
3591
2896
|
}
|
|
3592
2897
|
|
|
2898
|
+
// src/errors.ts
|
|
2899
|
+
var HTTP_ERROR_BRAND = /* @__PURE__ */ Symbol.for("palbase.backend.httpError");
|
|
2900
|
+
var HttpError = class extends Error {
|
|
2901
|
+
status;
|
|
2902
|
+
error;
|
|
2903
|
+
errorDescription;
|
|
2904
|
+
data;
|
|
2905
|
+
/** See {@link HTTP_ERROR_BRAND} — how the engine recognises this across SDK copies. */
|
|
2906
|
+
[HTTP_ERROR_BRAND] = true;
|
|
2907
|
+
constructor(status, error, errorDescription, data) {
|
|
2908
|
+
super(errorDescription);
|
|
2909
|
+
this.name = "HttpError";
|
|
2910
|
+
this.status = status;
|
|
2911
|
+
this.error = error;
|
|
2912
|
+
this.errorDescription = errorDescription;
|
|
2913
|
+
if (data !== void 0) {
|
|
2914
|
+
this.data = data;
|
|
2915
|
+
}
|
|
2916
|
+
}
|
|
2917
|
+
/**
|
|
2918
|
+
* Serialize to the standard Palbase error response format.
|
|
2919
|
+
* The `requestId` is injected by the runtime layer from the request context.
|
|
2920
|
+
* When called without arguments (e.g. JSON.stringify), request_id is omitted.
|
|
2921
|
+
* When `data` is set, it is appended as a strict-superset field.
|
|
2922
|
+
*/
|
|
2923
|
+
toJSON(requestId) {
|
|
2924
|
+
const result = {
|
|
2925
|
+
error: this.error,
|
|
2926
|
+
error_description: this.errorDescription,
|
|
2927
|
+
status: this.status
|
|
2928
|
+
};
|
|
2929
|
+
if (requestId) {
|
|
2930
|
+
result.request_id = requestId;
|
|
2931
|
+
}
|
|
2932
|
+
if (this.data !== void 0) {
|
|
2933
|
+
result.data = this.data;
|
|
2934
|
+
}
|
|
2935
|
+
return result;
|
|
2936
|
+
}
|
|
2937
|
+
};
|
|
2938
|
+
var PalError = class extends HttpError {
|
|
2939
|
+
constructor(status, code, description, data) {
|
|
2940
|
+
super(status, code, description, data);
|
|
2941
|
+
this.name = "PalError";
|
|
2942
|
+
}
|
|
2943
|
+
};
|
|
2944
|
+
var NamedHttpError = class extends HttpError {
|
|
2945
|
+
constructor(status, defaultCode, name, message, code, data) {
|
|
2946
|
+
super(status, code ?? defaultCode, message ?? defaultMessage(name), data);
|
|
2947
|
+
this.name = name;
|
|
2948
|
+
}
|
|
2949
|
+
};
|
|
2950
|
+
function defaultMessage(name) {
|
|
2951
|
+
const spaced = name.replace(/([a-z0-9])([A-Z])/g, "$1 $2");
|
|
2952
|
+
return spaced.charAt(0).toUpperCase() + spaced.slice(1).toLowerCase();
|
|
2953
|
+
}
|
|
2954
|
+
var BadRequest = class extends NamedHttpError {
|
|
2955
|
+
constructor(data, message) {
|
|
2956
|
+
super(400, "bad_request", "BadRequest", message, void 0, data);
|
|
2957
|
+
}
|
|
2958
|
+
};
|
|
2959
|
+
var Unauthorized = class extends NamedHttpError {
|
|
2960
|
+
constructor(message, code, data) {
|
|
2961
|
+
super(401, "unauthorized", "Unauthorized", message, code, data);
|
|
2962
|
+
}
|
|
2963
|
+
};
|
|
2964
|
+
var Forbidden = class extends NamedHttpError {
|
|
2965
|
+
constructor(message, code, data) {
|
|
2966
|
+
super(403, "forbidden", "Forbidden", message, code, data);
|
|
2967
|
+
}
|
|
2968
|
+
};
|
|
2969
|
+
var NotFound = class extends NamedHttpError {
|
|
2970
|
+
constructor(message, code, data) {
|
|
2971
|
+
super(404, "not_found", "NotFound", message, code, data);
|
|
2972
|
+
}
|
|
2973
|
+
};
|
|
2974
|
+
var Conflict = class extends NamedHttpError {
|
|
2975
|
+
constructor(message, code, data) {
|
|
2976
|
+
super(409, "conflict", "Conflict", message, code, data);
|
|
2977
|
+
}
|
|
2978
|
+
};
|
|
2979
|
+
var TooManyRequests = class extends NamedHttpError {
|
|
2980
|
+
constructor(data, message) {
|
|
2981
|
+
super(429, "too_many_requests", "TooManyRequests", message, void 0, data);
|
|
2982
|
+
}
|
|
2983
|
+
};
|
|
2984
|
+
|
|
3593
2985
|
// src/error-registry.ts
|
|
3594
2986
|
var import_zod_to_openapi = require("@asteasolutions/zod-to-openapi");
|
|
3595
2987
|
var import_zod = require("zod");
|
|
@@ -3599,27 +2991,7 @@ var BUILTIN_DATA_SCHEMAS = {
|
|
|
3599
2991
|
bad_request: import_zod.z.object({
|
|
3600
2992
|
fields: import_zod.z.array(import_zod.z.object({ field: import_zod.z.string(), message: import_zod.z.string() }))
|
|
3601
2993
|
}),
|
|
3602
|
-
too_many_requests: import_zod.z.object({ retryAfter: import_zod.z.number().int() })
|
|
3603
|
-
// Purchases (@RequireEntitlement / @Spend). Their payloads are what make a
|
|
3604
|
-
// paywall renderable on the client: WHICH entitlement was missing, and for a
|
|
3605
|
-
// spend the real ceiling and reset time rather than a guessed retry delay.
|
|
3606
|
-
entitlement_required: import_zod.z.object({ entitlement: import_zod.z.string().nullable() }),
|
|
3607
|
-
quota_exceeded: import_zod.z.object({
|
|
3608
|
-
key: import_zod.z.string(),
|
|
3609
|
-
scope: import_zod.z.string(),
|
|
3610
|
-
window: import_zod.z.string(),
|
|
3611
|
-
used: import_zod.z.number().int(),
|
|
3612
|
-
reserved: import_zod.z.number().int(),
|
|
3613
|
-
max: import_zod.z.number().int(),
|
|
3614
|
-
remaining: import_zod.z.number().int(),
|
|
3615
|
-
resetAt: import_zod.z.string()
|
|
3616
|
-
}),
|
|
3617
|
-
credit_insufficient: import_zod.z.object({
|
|
3618
|
-
key: import_zod.z.string(),
|
|
3619
|
-
balance: import_zod.z.number().int(),
|
|
3620
|
-
reserved: import_zod.z.number().int(),
|
|
3621
|
-
remaining: import_zod.z.number().int()
|
|
3622
|
-
})
|
|
2994
|
+
too_many_requests: import_zod.z.object({ retryAfter: import_zod.z.number().int() })
|
|
3623
2995
|
};
|
|
3624
2996
|
function getErrorRegistry() {
|
|
3625
2997
|
const g = globalThis;
|
|
@@ -3631,13 +3003,7 @@ function getErrorRegistry() {
|
|
|
3631
3003
|
["forbidden", 403, "Forbidden"],
|
|
3632
3004
|
["not_found", 404, "NotFound"],
|
|
3633
3005
|
["conflict", 409, "Conflict"],
|
|
3634
|
-
["too_many_requests", 429, "TooManyRequests"]
|
|
3635
|
-
// Thrown by the purchases decorators, so they are pre-seeded like the
|
|
3636
|
-
// other built-ins: a project never declares them, but every gated or
|
|
3637
|
-
// metered route must surface them as TYPED errors on the client.
|
|
3638
|
-
["entitlement_required", 403, "EntitlementRequired"],
|
|
3639
|
-
["quota_exceeded", 429, "QuotaExceeded"],
|
|
3640
|
-
["credit_insufficient", 429, "CreditInsufficient"]
|
|
3006
|
+
["too_many_requests", 429, "TooManyRequests"]
|
|
3641
3007
|
]) {
|
|
3642
3008
|
const dataSchema = BUILTIN_DATA_SCHEMAS[code];
|
|
3643
3009
|
m.set(code, {
|
|
@@ -3658,8 +3024,8 @@ function defineError(code, status, dataSchema) {
|
|
|
3658
3024
|
`defineError: status for "${code}" must be a 4xx/5xx integer, got ${status} \u2014 error responses must not clobber success responses in the project spec.`
|
|
3659
3025
|
);
|
|
3660
3026
|
}
|
|
3661
|
-
const
|
|
3662
|
-
const existing =
|
|
3027
|
+
const registry2 = getErrorRegistry();
|
|
3028
|
+
const existing = registry2.get(code);
|
|
3663
3029
|
const className = defaultClassName(code);
|
|
3664
3030
|
const dataDigest = dataSchema ? digestOf(code, dataSchema) : void 0;
|
|
3665
3031
|
if (existing) {
|
|
@@ -3688,7 +3054,7 @@ function defineError(code, status, dataSchema) {
|
|
|
3688
3054
|
};
|
|
3689
3055
|
const cls = dataSchema ? makeWithData(dataSchema) : makeWithoutData();
|
|
3690
3056
|
Object.defineProperty(cls, "name", { value: className });
|
|
3691
|
-
|
|
3057
|
+
registry2.set(code, {
|
|
3692
3058
|
code,
|
|
3693
3059
|
status,
|
|
3694
3060
|
className,
|
|
@@ -3847,52 +3213,6 @@ function getJobConfig(ctor) {
|
|
|
3847
3213
|
return { schedule: meta.schedule, timeout, retry, handler: run };
|
|
3848
3214
|
}
|
|
3849
3215
|
|
|
3850
|
-
// src/resource.ts
|
|
3851
|
-
var Resource = class {
|
|
3852
|
-
/** The env-var names this resource needs. Optional; omit for none. */
|
|
3853
|
-
static secrets;
|
|
3854
|
-
};
|
|
3855
|
-
function hasSecrets(value) {
|
|
3856
|
-
if (typeof value !== "object" && typeof value !== "function") return false;
|
|
3857
|
-
const secrets = value.secrets;
|
|
3858
|
-
return secrets === void 0 || Array.isArray(secrets);
|
|
3859
|
-
}
|
|
3860
|
-
var registry2 = [];
|
|
3861
|
-
function __registerResource(resource) {
|
|
3862
|
-
registry2.push({ resource, booted: false });
|
|
3863
|
-
}
|
|
3864
|
-
function declaredSecrets(resource) {
|
|
3865
|
-
const ctor = resource.constructor;
|
|
3866
|
-
return hasSecrets(ctor) ? ctor.secrets ?? [] : [];
|
|
3867
|
-
}
|
|
3868
|
-
async function __runResourceBoot(envMap) {
|
|
3869
|
-
for (const entry of registry2) {
|
|
3870
|
-
if (entry.booted) continue;
|
|
3871
|
-
const secrets = declaredSecrets(entry.resource);
|
|
3872
|
-
const env = {};
|
|
3873
|
-
for (const name of secrets) {
|
|
3874
|
-
const value = envMap[name];
|
|
3875
|
-
if (value === void 0) {
|
|
3876
|
-
throw new Error(
|
|
3877
|
-
`Resource ${entry.resource.constructor.name} requires secret "${name}" but it is not set. Set it with \`palbase secret set ${name} ...\` (or in Studio) and redeploy.`
|
|
3878
|
-
);
|
|
3879
|
-
}
|
|
3880
|
-
env[name] = value;
|
|
3881
|
-
}
|
|
3882
|
-
await entry.resource.init(env);
|
|
3883
|
-
entry.booted = true;
|
|
3884
|
-
}
|
|
3885
|
-
}
|
|
3886
|
-
async function __shutdownResources() {
|
|
3887
|
-
for (let i = registry2.length - 1; i >= 0; i -= 1) {
|
|
3888
|
-
const entry = registry2[i];
|
|
3889
|
-
if (entry.booted && entry.resource.shutdown) {
|
|
3890
|
-
await entry.resource.shutdown();
|
|
3891
|
-
}
|
|
3892
|
-
}
|
|
3893
|
-
registry2.length = 0;
|
|
3894
|
-
}
|
|
3895
|
-
|
|
3896
3216
|
// src/index.ts
|
|
3897
3217
|
var import_zod2 = require("zod");
|
|
3898
3218
|
// Annotate the CommonJS export names for ESM import in node:
|
|
@@ -3903,17 +3223,11 @@ var import_zod2 = require("zod");
|
|
|
3903
3223
|
Client,
|
|
3904
3224
|
Conflict,
|
|
3905
3225
|
Controller,
|
|
3906
|
-
DEFAULT_TEMPLATE_LOCALE,
|
|
3907
3226
|
Database,
|
|
3908
3227
|
Delete,
|
|
3909
3228
|
Deny,
|
|
3910
3229
|
Documents,
|
|
3911
|
-
EGRESS_CONFIG_KIND,
|
|
3912
|
-
EGRESS_TIMEOUT_DEFAULT_MS,
|
|
3913
|
-
EGRESS_TIMEOUT_MAX_MS,
|
|
3914
|
-
EGRESS_TIMEOUT_MIN_MS,
|
|
3915
3230
|
EXTENSION_DEPENDENCIES,
|
|
3916
|
-
FLAGS_CONFIG_KIND,
|
|
3917
3231
|
Flags,
|
|
3918
3232
|
Forbidden,
|
|
3919
3233
|
Get,
|
|
@@ -3922,35 +3236,28 @@ var import_zod2 = require("zod");
|
|
|
3922
3236
|
HttpError,
|
|
3923
3237
|
Job,
|
|
3924
3238
|
Log,
|
|
3925
|
-
NOTIFICATIONS_CONFIG_KIND,
|
|
3926
3239
|
NotFound,
|
|
3927
3240
|
Notifications,
|
|
3928
3241
|
On,
|
|
3929
3242
|
OptionalUser,
|
|
3930
3243
|
PALBASE_EXTENSIONS,
|
|
3931
|
-
PROVIDER_CATALOG,
|
|
3932
3244
|
PalError,
|
|
3933
3245
|
PalbaseModuleError,
|
|
3934
3246
|
Param,
|
|
3935
3247
|
Patch,
|
|
3936
3248
|
PolicyBuilder,
|
|
3937
3249
|
Post,
|
|
3938
|
-
Purchases,
|
|
3939
3250
|
Put,
|
|
3940
3251
|
Query,
|
|
3941
3252
|
QueryParams,
|
|
3942
|
-
RESERVED_SECRET_PREFIX,
|
|
3943
3253
|
Realtime,
|
|
3944
3254
|
Req,
|
|
3945
3255
|
RequestId,
|
|
3946
|
-
RequireEntitlement,
|
|
3947
|
-
Resource,
|
|
3948
|
-
SECRETS_CONFIG_KIND,
|
|
3949
|
-
STORAGE_CONFIG_KIND,
|
|
3950
3256
|
Secrets,
|
|
3951
|
-
|
|
3257
|
+
Signal,
|
|
3258
|
+
Sse,
|
|
3259
|
+
SseOut,
|
|
3952
3260
|
Storage,
|
|
3953
|
-
TEST_USERS_CONFIG_KIND,
|
|
3954
3261
|
TooManyRequests,
|
|
3955
3262
|
TraceId,
|
|
3956
3263
|
TxPlanError,
|
|
@@ -3961,33 +3268,19 @@ var import_zod2 = require("zod");
|
|
|
3961
3268
|
User,
|
|
3962
3269
|
Webhook,
|
|
3963
3270
|
__getRuntime,
|
|
3964
|
-
__registerResource,
|
|
3965
3271
|
__requestALS,
|
|
3966
3272
|
__resetRegisteredControllers,
|
|
3967
|
-
__runResourceBoot,
|
|
3968
3273
|
__runWithRuntime,
|
|
3969
3274
|
__setRuntime,
|
|
3970
|
-
__shutdownResources,
|
|
3971
3275
|
bigint,
|
|
3972
3276
|
boolean,
|
|
3973
|
-
bucket,
|
|
3974
3277
|
buildModuleClients,
|
|
3975
|
-
buildProvider,
|
|
3976
3278
|
dec,
|
|
3977
3279
|
defineChannels,
|
|
3978
|
-
defineEgress,
|
|
3979
3280
|
defineError,
|
|
3980
|
-
defineFlags,
|
|
3981
3281
|
defineMiddleware,
|
|
3982
|
-
defineNotifications,
|
|
3983
3282
|
defineSchema,
|
|
3984
|
-
defineSecrets,
|
|
3985
|
-
defineStorage,
|
|
3986
|
-
defineTestUsers,
|
|
3987
|
-
entitlementFor,
|
|
3988
3283
|
enumType,
|
|
3989
|
-
extractVariables,
|
|
3990
|
-
flag,
|
|
3991
3284
|
getErrorRegistry,
|
|
3992
3285
|
getHookConfig,
|
|
3993
3286
|
getJobConfig,
|
|
@@ -4000,26 +3293,20 @@ var import_zod2 = require("zod");
|
|
|
4000
3293
|
jsonb,
|
|
4001
3294
|
makeEnvDts,
|
|
4002
3295
|
makeHttpClient,
|
|
4003
|
-
|
|
3296
|
+
makeStackDts,
|
|
4004
3297
|
makeTypedDB,
|
|
4005
3298
|
now,
|
|
4006
3299
|
numeric,
|
|
4007
3300
|
openai,
|
|
4008
3301
|
ownerOnly,
|
|
4009
|
-
parseFileSizeLimit,
|
|
4010
3302
|
policy,
|
|
4011
3303
|
publicChannel,
|
|
4012
3304
|
raw,
|
|
4013
3305
|
recordThrows,
|
|
4014
|
-
reservedSecretKey,
|
|
4015
|
-
secret,
|
|
4016
|
-
spendFor,
|
|
4017
|
-
testUser,
|
|
4018
3306
|
text,
|
|
4019
3307
|
timestamp,
|
|
4020
3308
|
toSchemaJSON,
|
|
4021
3309
|
uuid,
|
|
4022
|
-
validateUploadAgainstStorage,
|
|
4023
3310
|
vector,
|
|
4024
3311
|
z
|
|
4025
3312
|
});
|