encore.dev 0.0.0-devel.202311141645

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/LICENSE +362 -0
  2. package/README.md +4 -0
  3. package/api/mod.ts +47 -0
  4. package/config/mod.ts +2 -0
  5. package/config/secrets.ts +27 -0
  6. package/cron/mod.ts +17 -0
  7. package/dist/api/mod.d.ts +7 -0
  8. package/dist/api/mod.js +5 -0
  9. package/dist/api/mod.js.map +1 -0
  10. package/dist/config/mod.d.ts +2 -0
  11. package/dist/config/mod.js +2 -0
  12. package/dist/config/mod.js.map +1 -0
  13. package/dist/config/secrets.d.ts +11 -0
  14. package/dist/config/secrets.js +15 -0
  15. package/dist/config/secrets.js.map +1 -0
  16. package/dist/cron/mod.d.ts +14 -0
  17. package/dist/cron/mod.js +10 -0
  18. package/dist/cron/mod.js.map +1 -0
  19. package/dist/internal/types/mod.d.ts +7 -0
  20. package/dist/internal/types/mod.js +2 -0
  21. package/dist/internal/types/mod.js.map +1 -0
  22. package/dist/log/mod.d.ts +3 -0
  23. package/dist/log/mod.js +4 -0
  24. package/dist/log/mod.js.map +1 -0
  25. package/dist/mod.d.ts +0 -0
  26. package/dist/mod.js +2 -0
  27. package/dist/mod.js.map +1 -0
  28. package/dist/pubsub/acker.d.ts +26 -0
  29. package/dist/pubsub/acker.js +73 -0
  30. package/dist/pubsub/acker.js.map +1 -0
  31. package/dist/pubsub/attributes.d.ts +59 -0
  32. package/dist/pubsub/attributes.js +2 -0
  33. package/dist/pubsub/attributes.js.map +1 -0
  34. package/dist/pubsub/mod.d.ts +5 -0
  35. package/dist/pubsub/mod.js +3 -0
  36. package/dist/pubsub/mod.js.map +1 -0
  37. package/dist/pubsub/subscription.d.ts +100 -0
  38. package/dist/pubsub/subscription.js +164 -0
  39. package/dist/pubsub/subscription.js.map +1 -0
  40. package/dist/pubsub/topic.d.ts +105 -0
  41. package/dist/pubsub/topic.js +62 -0
  42. package/dist/pubsub/topic.js.map +1 -0
  43. package/dist/storage/sqldb/database.d.ts +51 -0
  44. package/dist/storage/sqldb/database.js +78 -0
  45. package/dist/storage/sqldb/database.js.map +1 -0
  46. package/dist/storage/sqldb/mod.d.ts +2 -0
  47. package/dist/storage/sqldb/mod.js +2 -0
  48. package/dist/storage/sqldb/mod.js.map +1 -0
  49. package/dist/tsconfig.tsbuildinfo +1 -0
  50. package/internal/types/mod.ts +10 -0
  51. package/log/mod.ts +4 -0
  52. package/mod.ts +0 -0
  53. package/package.json +77 -0
  54. package/pubsub/acker.ts +79 -0
  55. package/pubsub/attributes.ts +66 -0
  56. package/pubsub/mod.ts +7 -0
  57. package/pubsub/subscription.ts +302 -0
  58. package/pubsub/topic.ts +132 -0
  59. package/storage/sqldb/database.ts +105 -0
  60. package/storage/sqldb/mod.ts +2 -0
@@ -0,0 +1,73 @@
1
+ /**
2
+ * Acker is used to ack or nack a message.
3
+ */
4
+ export class Acker {
5
+ #acked;
6
+ #nacked;
7
+ #succesfulResult = false;
8
+ #ack;
9
+ #nack;
10
+ constructor(ack, nack) {
11
+ this.#ack = ack;
12
+ this.#nack = nack;
13
+ }
14
+ /**
15
+ * Acknowledges the message.
16
+ *
17
+ * If the message has been nacked, an error is thrown.
18
+ *
19
+ * Multiple calls to ack() will return the same promise, unless the
20
+ * promise has been rejected, in which case a new promise will be returned.
21
+ */
22
+ async ack() {
23
+ if (this.#nacked) {
24
+ throw new Error("message already nacked and cannot be acked");
25
+ }
26
+ if (this.#acked) {
27
+ return this.#acked;
28
+ }
29
+ this.#acked = this.#ack();
30
+ this.#acked
31
+ .then(() => {
32
+ this.#succesfulResult = true;
33
+ })
34
+ .catch((e) => {
35
+ this.#acked = undefined;
36
+ throw e;
37
+ });
38
+ }
39
+ /**
40
+ * Negatively acknowledges the message.
41
+ *
42
+ * If the message has been acked, an error is thrown.
43
+ *
44
+ * Multiple calls to nack() will return the same promise, unless the
45
+ * promise has been rejected, in which case a new promise will be returned.
46
+ */
47
+ async nack() {
48
+ if (this.#acked) {
49
+ throw new Error("message already acked and cannot be nacked");
50
+ }
51
+ if (this.#nacked) {
52
+ return this.#nacked;
53
+ }
54
+ this.#nacked = this.#nack();
55
+ this.#nacked
56
+ .then(() => {
57
+ this.#succesfulResult = true;
58
+ })
59
+ .catch((e) => {
60
+ this.#nacked = undefined;
61
+ throw e;
62
+ });
63
+ }
64
+ currentResult() {
65
+ if (this.#succesfulResult) {
66
+ return Promise.resolve(true);
67
+ }
68
+ return (this.#acked ?? this.#nacked ?? Promise.reject())
69
+ .then(() => true)
70
+ .catch(() => false);
71
+ }
72
+ }
73
+ //# sourceMappingURL=acker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"acker.js","sourceRoot":"","sources":["../../pubsub/acker.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,OAAO,KAAK;IAChB,MAAM,CAAiB;IACvB,OAAO,CAAiB;IACxB,gBAAgB,GAAG,KAAK,CAAC;IAChB,IAAI,CAAsB;IAC1B,KAAK,CAAsB;IAEpC,YAAY,GAAwB,EAAE,IAAyB;QAC7D,IAAI,CAAC,IAAI,GAAG,GAAG,CAAC;QAChB,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;IACpB,CAAC;IAED;;;;;;;OAOG;IACH,KAAK,CAAC,GAAG;QACP,IAAI,IAAI,CAAC,OAAO,EAAE;YAChB,MAAM,IAAI,KAAK,CAAC,4CAA4C,CAAC,CAAC;SAC/D;QACD,IAAI,IAAI,CAAC,MAAM,EAAE;YACf,OAAO,IAAI,CAAC,MAAM,CAAC;SACpB;QAED,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;QAC1B,IAAI,CAAC,MAAM;aACR,IAAI,CAAC,GAAG,EAAE;YACT,IAAI,CAAC,gBAAgB,GAAG,IAAI,CAAC;QAC/B,CAAC,CAAC;aACD,KAAK,CAAC,CAAC,CAAU,EAAE,EAAE;YACpB,IAAI,CAAC,MAAM,GAAG,SAAS,CAAC;YACxB,MAAM,CAAC,CAAC;QACV,CAAC,CAAC,CAAC;IACP,CAAC;IAED;;;;;;;OAOG;IACH,KAAK,CAAC,IAAI;QACR,IAAI,IAAI,CAAC,MAAM,EAAE;YACf,MAAM,IAAI,KAAK,CAAC,4CAA4C,CAAC,CAAC;SAC/D;QACD,IAAI,IAAI,CAAC,OAAO,EAAE;YAChB,OAAO,IAAI,CAAC,OAAO,CAAC;SACrB;QAED,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,KAAK,EAAE,CAAC;QAC5B,IAAI,CAAC,OAAO;aACT,IAAI,CAAC,GAAG,EAAE;YACT,IAAI,CAAC,gBAAgB,GAAG,IAAI,CAAC;QAC/B,CAAC,CAAC;aACD,KAAK,CAAC,CAAC,CAAU,EAAE,EAAE;YACpB,IAAI,CAAC,OAAO,GAAG,SAAS,CAAC;YACzB,MAAM,CAAC,CAAC;QACV,CAAC,CAAC,CAAC;IACP,CAAC;IAED,aAAa;QACX,IAAI,IAAI,CAAC,gBAAgB,EAAE;YACzB,OAAO,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;SAC9B;QAED,OAAO,CAAC,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,OAAO,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;aACrD,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC;aAChB,KAAK,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,CAAC;IACxB,CAAC;CACF"}
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Attribute represents a field on a message that should be sent
3
+ * as an attribute in a PubSub message, rather than in the message
4
+ * body.
5
+ *
6
+ * This is useful for ordering messages, or for filtering messages
7
+ * on a subscription - otherwise you should not use this.
8
+ *
9
+ * To create attributes on a message, use the `Attribute` type:
10
+ * type Message = {
11
+ * user_id: Attribute<number>;
12
+ * name: string;
13
+ * };
14
+ *
15
+ * const msg: Message = {
16
+ * user_id: 123,
17
+ * name: "John Doe",
18
+ * };
19
+ *
20
+ * The union of brandedAttribute is simply used to help the TypeScript compiler
21
+ * understand that the type is an attribute and allow the AttributesOf type
22
+ * to extract the keys of said type.
23
+ */
24
+ export type Attribute<T extends string | number | boolean> = T | brandedAttribute<T>;
25
+ /**
26
+ * AttributesOf is a helper type to extract all keys from an object
27
+ * who's type is an Attribute type.
28
+ *
29
+ * For example:
30
+ * type Message = {
31
+ * user_id: Attribute<number>;
32
+ * name: string;
33
+ * age: Attribute<number>;
34
+ * };
35
+ *
36
+ * type MessageAttributes = AttributesOf<Message>; // "user_id" | "age"
37
+ */
38
+ export type AttributesOf<T extends object> = keyof {
39
+ [Key in keyof T as Extract<T[Key], allBrandedTypes> extends never ? never : Key]: never;
40
+ };
41
+ /**
42
+ * supportedAttributeTypes is a union of all primitive types that are supported as attributes
43
+ */
44
+ type supportedAttributeTypes = string | number | boolean;
45
+ /**
46
+ * brandedAttribute is a helper type to brand a type as an attribute
47
+ * which is distinct from the base type. It is a compile time only
48
+ * type and has no runtime representation.
49
+ */
50
+ type brandedAttribute<T> = T & {
51
+ readonly __attributeBrand: unique symbol;
52
+ };
53
+ /**
54
+ * allBrandedTypes is a helper type to create a union of all branded supported attribute types
55
+ *
56
+ * The result of this is: brandedAttribute<string> | brandedAttribute<number> | brandedAttribute<boolean>
57
+ */
58
+ type allBrandedTypes<Union = supportedAttributeTypes> = Union extends supportedAttributeTypes ? brandedAttribute<Union> : never;
59
+ export {};
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=attributes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"attributes.js","sourceRoot":"","sources":["../../pubsub/attributes.ts"],"names":[],"mappings":""}
@@ -0,0 +1,5 @@
1
+ export { Topic } from "./topic.js";
2
+ export type { TopicConfig, DeliveryGuarantee } from "./topic.js";
3
+ export { Subscription } from "./subscription.js";
4
+ export type { SubscriptionConfig } from "./subscription.js";
5
+ export type { Attribute } from "./attributes.js";
@@ -0,0 +1,3 @@
1
+ export { Topic } from "./topic.js";
2
+ export { Subscription } from "./subscription.js";
3
+ //# sourceMappingURL=mod.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mod.js","sourceRoot":"","sources":["../../pubsub/mod.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAGhC,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC"}
@@ -0,0 +1,100 @@
1
+ import { DurationString } from "../internal/types/mod.js";
2
+ import { Topic } from "./topic.js";
3
+ export declare class Subscription<Msg extends object> {
4
+ #private;
5
+ private readonly client_id;
6
+ private readonly topic;
7
+ private readonly name;
8
+ private readonly handler;
9
+ private readonly config;
10
+ private readonly logger;
11
+ private failedState;
12
+ private activeDeliveries;
13
+ constructor(topic: Topic<Msg>, name: string, cfg: SubscriptionConfig<Msg>);
14
+ }
15
+ /**
16
+ * SubscriptionConfig is used when creating a subscription
17
+ *
18
+ * The values given here may be clamped to the supported values by
19
+ * the target cloud. (i.e. ack deadline may be brought within the supported range
20
+ * by the target cloud pubsub implementation).
21
+ */
22
+ export interface SubscriptionConfig<Msg> {
23
+ /**
24
+ * Handler is the function which will be called to process a message
25
+ * sent on the topic.
26
+ *
27
+ * When this function returns an error the message will be
28
+ * negatively acknowledged (nacked), which will cause a redelivery
29
+ * attempt to be made (unless the retry policy's MaxRetries has been reached).
30
+ */
31
+ handler: (msg: Msg) => Promise<unknown>;
32
+ /**
33
+ * MaxConcurrency is the maximum number of messages which will be processed
34
+ * simultaneously per instance of the service for this subscription.
35
+ *
36
+ * Note that this is per instance of the service, so if your service has
37
+ * scaled to 10 instances and this is set to 10, then 100 messages could be
38
+ * processed simultaneously.
39
+ *
40
+ * If the value is negative, then there will be no limit on the number
41
+ * of messages processed simultaneously.
42
+ *
43
+ * Note: This is not supported by all cloud providers; specifically on GCP
44
+ * when using Cloud Run instances on an unordered topic the subscription will
45
+ * be configured as a Push Subscription and will have an adaptive concurrency
46
+ * See [GCP Push Delivery Rate](https://cloud.google.com/pubsub/docs/push#push_delivery_rate).
47
+ *
48
+ * This setting also has no effect on Encore Cloud environments.
49
+ * If not set, it uses a reasonable default based on the cloud provider.
50
+ */
51
+ maxConcurrency?: number;
52
+ /**
53
+ * AckDeadline is the time a consumer has to process a message
54
+ * before it's returned to the subscription
55
+ *
56
+ * Default is 30 seconds, however the ack deadline must be at least
57
+ * 1 second.
58
+ */
59
+ ackDeadline?: DurationString;
60
+ /**
61
+ * MessageRetention is how long an undelivered message is kept
62
+ * on the topic before it's purged.
63
+ *
64
+ * Default is 7 days.
65
+ */
66
+ messageRetention?: DurationString;
67
+ /**
68
+ * RetryPolicy defines how a message should be retried when
69
+ * the subscriber returns an error
70
+ */
71
+ retryPolicy?: RetryPolicy;
72
+ }
73
+ /**
74
+ * RetryPolicy defines how a subscription should handle retries
75
+ * after errors either delivering the message or processing the message.
76
+ *
77
+ * The values given to this structure are parsed at compile time, such that
78
+ * the correct Cloud resources can be provisioned to support the queue.
79
+ *
80
+ * As such the values given here may be clamped to the supported values by
81
+ * the target cloud. (i.e. min/max values brought within the supported range
82
+ * by the target cloud).
83
+ */
84
+ export interface RetryPolicy {
85
+ /**
86
+ * The minimum time to wait between retries. Defaults to 10 seconds.
87
+ */
88
+ minBackoff?: DurationString;
89
+ /**
90
+ * The maximum time to wait between retries. Defaults to 10 minutes.
91
+ */
92
+ maxBackoff?: DurationString;
93
+ /**
94
+ * MaxRetries is used to control deadletter queuing logic, when:
95
+ * n == 0: A default value of 100 retries will be used
96
+ * n > 0: Encore will forward a message to a dead letter queue after n retries
97
+ * n == pubsub.InfiniteRetries: Messages will not be forwarded to the dead letter queue by the Encore framework
98
+ */
99
+ maxRetries?: number;
100
+ }
@@ -0,0 +1,164 @@
1
+ import { api } from "@encore.dev/internal-runtime";
2
+ import reqtrack from "@encore.dev/internal-runtime/reqtrack/index";
3
+ import { newSpanID, newTraceID, } from "@encore.dev/internal-runtime/reqtrack/tracecontext";
4
+ import { withLogCtx, } from "@encore.dev/internal-runtime/reqtrack/logging";
5
+ import { durationToProto, now, parseDuration, since, toDurationStr, } from "@encore.dev/internal-runtime/utils/timers";
6
+ import { runtime } from "@encore.dev/internal-runtime/jsruntime";
7
+ import log from "../log/mod.js";
8
+ import { PubSub } from "@encore.dev/sidecar-api";
9
+ import { Acker } from "./acker.js";
10
+ export class Subscription {
11
+ client_id;
12
+ topic;
13
+ name;
14
+ handler;
15
+ config;
16
+ logger;
17
+ failedState = false;
18
+ activeDeliveries = [];
19
+ constructor(topic, name, cfg) {
20
+ // Generate a unique client ID for this instance
21
+ const id = new Uint8Array(16);
22
+ runtime().getRandomValues(id);
23
+ this.client_id = Buffer.from(id).toString("hex");
24
+ this.topic = topic;
25
+ this.name = name;
26
+ this.handler = cfg.handler;
27
+ this.logger = log.with({ topic: topic.name, subscription: name });
28
+ const ackDeadline = parseDuration(cfg.ackDeadline ?? "30s");
29
+ const minBackoff = parseDuration(cfg.retryPolicy?.minBackoff ?? "10s");
30
+ const maxBackoff = parseDuration(cfg.retryPolicy?.maxBackoff ?? "10m");
31
+ this.config = new PubSub.SubscriptionConfig({
32
+ maxConcurrency: cfg.maxConcurrency ?? -1,
33
+ ackDeadline: durationToProto(ackDeadline),
34
+ retryPolicy: new PubSub.RetryPolicy({
35
+ minBackoff: durationToProto(minBackoff),
36
+ maxBackoff: durationToProto(maxBackoff),
37
+ maxRetries: cfg.retryPolicy?.maxRetries ?? 100,
38
+ }),
39
+ });
40
+ let backoff = 250; // Start at 250ms for exponential backoff
41
+ const start = () => {
42
+ this.logger.trace("starting subscription");
43
+ this.#startSubscription()
44
+ .then(() => this.logger.warn("subscription ended"))
45
+ .catch((e) => {
46
+ this.failedState = true;
47
+ backoff *= 2;
48
+ if (backoff > 60 * 1000) {
49
+ // cap the backoff at 1 minute
50
+ backoff = 60 * 1000;
51
+ }
52
+ this.logger.error(e, "subscription failed with error");
53
+ setTimeout(start, backoff);
54
+ });
55
+ };
56
+ this.logger.info("registered subscription");
57
+ // Start the subscription
58
+ process.nextTick(start);
59
+ }
60
+ async #startSubscription() {
61
+ const resp = api().pubsub.subscribe(new PubSub.SubscribeRequest({
62
+ clientId: this.client_id,
63
+ topic: this.topic.name,
64
+ subscription: this.name,
65
+ config: this.config,
66
+ }));
67
+ // Now we have an active subscription, we touch messages if we have active messages
68
+ const interval = setInterval(() => {
69
+ this.#touchActiveMessages();
70
+ }, 30 * 1000);
71
+ try {
72
+ for await (const msg of resp) {
73
+ if (this.failedState) {
74
+ this.logger.info("subscription recovered");
75
+ this.failedState = false;
76
+ }
77
+ process.nextTick(() => this.#handleMessage(msg));
78
+ }
79
+ }
80
+ finally {
81
+ clearInterval(interval);
82
+ }
83
+ }
84
+ #touchActiveMessages() {
85
+ if (this.activeDeliveries.length === 0) {
86
+ return;
87
+ }
88
+ const acks = this.activeDeliveries;
89
+ api()
90
+ .pubsub.touch({
91
+ clientId: this.client_id,
92
+ ackIds: acks,
93
+ })
94
+ .then(() => {
95
+ this.logger.trace("touched active messages", { ack_ids: acks });
96
+ })
97
+ .catch((e) => {
98
+ this.logger.error(e, "failed to touch messages", { ack_ids: acks });
99
+ });
100
+ }
101
+ async #handleMessage(event) {
102
+ if (event.event.case !== "messageDelivery") {
103
+ // If it's a keep alive or some other unknown message
104
+ return;
105
+ }
106
+ const msg = event.event.value;
107
+ const logCtx = {
108
+ topic: this.topic.name,
109
+ subscription: this.name,
110
+ msg_id: msg.msgId,
111
+ delivery_attempt: msg.deliveryAttempt,
112
+ };
113
+ const traceID = newTraceID();
114
+ const spanID = newSpanID();
115
+ const ctx = {
116
+ traceID: traceID,
117
+ spanID: spanID,
118
+ };
119
+ // Record this ack ID as being active
120
+ this.activeDeliveries.push(msg.ackId);
121
+ return reqtrack.run(ctx, async () => {
122
+ return withLogCtx(logCtx, async () => {
123
+ const acker = new Acker(() => this.#acknowledge(msg.ackId), () => this.#negativeAcknowledge(msg.ackId));
124
+ try {
125
+ this.logger.info("starting request");
126
+ const data = Buffer.from(msg.message?.data ?? Uint8Array.of()).toString();
127
+ const start = now();
128
+ await this.handler(JSON.parse(data), acker);
129
+ this.logger.info("request completed", {
130
+ duration: toDurationStr(since(start)),
131
+ });
132
+ const hasAcked = await acker.currentResult();
133
+ if (!hasAcked) {
134
+ await acker.ack();
135
+ }
136
+ }
137
+ catch (e) {
138
+ this.logger.error(e, "request failed");
139
+ const hasAcked = await acker.currentResult();
140
+ if (!hasAcked) {
141
+ await acker.nack();
142
+ }
143
+ }
144
+ });
145
+ });
146
+ }
147
+ async #acknowledge(ackID) {
148
+ await api().pubsub.acknowledge({
149
+ clientId: this.client_id,
150
+ ackId: ackID,
151
+ });
152
+ this.activeDeliveries = this.activeDeliveries.filter((id) => id !== ackID);
153
+ this.logger.trace("acknowledged message", { ack_id: ackID });
154
+ }
155
+ async #negativeAcknowledge(ackID) {
156
+ await api().pubsub.negativeAcknowledge({
157
+ clientId: this.client_id,
158
+ ackId: ackID,
159
+ });
160
+ this.activeDeliveries = this.activeDeliveries.filter((id) => id !== ackID);
161
+ this.logger.trace("negatively acknowledged message", { ack_id: ackID });
162
+ }
163
+ }
164
+ //# sourceMappingURL=subscription.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"subscription.js","sourceRoot":"","sources":["../../pubsub/subscription.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,8BAA8B,CAAC;AACnD,OAAO,QAAqB,MAAM,6CAA6C,CAAC;AAChF,OAAO,EACL,SAAS,EACT,UAAU,GACX,MAAM,oDAAoD,CAAC;AAC5D,OAAO,EAEL,UAAU,GACX,MAAM,+CAA+C,CAAC;AACvD,OAAO,EACL,eAAe,EACf,GAAG,EACH,aAAa,EACb,KAAK,EACL,aAAa,GACd,MAAM,2CAA2C,CAAC;AACnD,OAAO,EAAE,OAAO,EAAE,MAAM,wCAAwC,CAAC;AACjE,OAAO,GAAoB,MAAM,YAAY,CAAC;AAC9C,OAAO,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AAEjD,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAGhC,MAAM,OAAO,YAAY;IACN,SAAS,CAAS;IAClB,KAAK,CAAa;IAClB,IAAI,CAAS;IACb,OAAO,CAA+C;IACtD,MAAM,CAA4B;IAClC,MAAM,CAAS;IACxB,WAAW,GAAG,KAAK,CAAC;IACpB,gBAAgB,GAAa,EAAE,CAAC;IAExC,YAAY,KAAiB,EAAE,IAAY,EAAE,GAA4B;QACvE,gDAAgD;QAChD,MAAM,EAAE,GAAG,IAAI,UAAU,CAAC,EAAE,CAAC,CAAC;QAC9B,OAAO,EAAE,CAAC,eAAe,CAAC,EAAE,CAAC,CAAC;QAC9B,IAAI,CAAC,SAAS,GAAG,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;QAEjD,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;QACnB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,OAAO,GAAG,GAAG,CAAC,OAAO,CAAC;QAC3B,IAAI,CAAC,MAAM,GAAG,GAAG,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,CAAC,CAAC;QAElE,MAAM,WAAW,GAAG,aAAa,CAAC,GAAG,CAAC,WAAW,IAAI,KAAK,CAAC,CAAC;QAC5D,MAAM,UAAU,GAAG,aAAa,CAAC,GAAG,CAAC,WAAW,EAAE,UAAU,IAAI,KAAK,CAAC,CAAC;QACvE,MAAM,UAAU,GAAG,aAAa,CAAC,GAAG,CAAC,WAAW,EAAE,UAAU,IAAI,KAAK,CAAC,CAAC;QAEvE,IAAI,CAAC,MAAM,GAAG,IAAI,MAAM,CAAC,kBAAkB,CAAC;YAC1C,cAAc,EAAE,GAAG,CAAC,cAAc,IAAI,CAAC,CAAC;YACxC,WAAW,EAAE,eAAe,CAAC,WAAW,CAAC;YACzC,WAAW,EAAE,IAAI,MAAM,CAAC,WAAW,CAAC;gBAClC,UAAU,EAAE,eAAe,CAAC,UAAU,CAAC;gBACvC,UAAU,EAAE,eAAe,CAAC,UAAU,CAAC;gBACvC,UAAU,EAAE,GAAG,CAAC,WAAW,EAAE,UAAU,IAAI,GAAG;aAC/C,CAAC;SACH,CAAC,CAAC;QAEH,IAAI,OAAO,GAAG,GAAG,CAAC,CAAC,yCAAyC;QAC5D,MAAM,KAAK,GAAG,GAAG,EAAE;YACjB,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,uBAAuB,CAAC,CAAC;YAC3C,IAAI,CAAC,kBAAkB,EAAE;iBACtB,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,oBAAoB,CAAC,CAAC;iBAClD,KAAK,CAAC,CAAC,CAAU,EAAE,EAAE;gBACpB,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC;gBACxB,OAAO,IAAI,CAAC,CAAC;gBACb,IAAI,OAAO,GAAG,EAAE,GAAG,IAAI,EAAE;oBACvB,8BAA8B;oBAC9B,OAAO,GAAG,EAAE,GAAG,IAAI,CAAC;iBACrB;gBAED,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,gCAAgC,CAAC,CAAC;gBACvD,UAAU,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;YAC7B,CAAC,CAAC,CAAC;QACP,CAAC,CAAC;QAEF,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,yBAAyB,CAAC,CAAC;QAE5C,yBAAyB;QACzB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IAC1B,CAAC;IAED,KAAK,CAAC,kBAAkB;QACtB,MAAM,IAAI,GAAG,GAAG,EAAE,CAAC,MAAM,CAAC,SAAS,CACjC,IAAI,MAAM,CAAC,gBAAgB,CAAC;YAC1B,QAAQ,EAAE,IAAI,CAAC,SAAS;YACxB,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI;YACtB,YAAY,EAAE,IAAI,CAAC,IAAI;YACvB,MAAM,EAAE,IAAI,CAAC,MAAM;SACpB,CAAC,CACH,CAAC;QAEF,mFAAmF;QACnF,MAAM,QAAQ,GAAG,WAAW,CAAC,GAAG,EAAE;YAChC,IAAI,CAAC,oBAAoB,EAAE,CAAC;QAC9B,CAAC,EAAE,EAAE,GAAG,IAAI,CAAC,CAAC;QAEd,IAAI;YACF,IAAI,KAAK,EAAE,MAAM,GAAG,IAAI,IAAI,EAAE;gBAC5B,IAAI,IAAI,CAAC,WAAW,EAAE;oBACpB,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,wBAAwB,CAAC,CAAC;oBAC3C,IAAI,CAAC,WAAW,GAAG,KAAK,CAAC;iBAC1B;gBAED,OAAO,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,CAAC;aAClD;SACF;gBAAS;YACR,aAAa,CAAC,QAAQ,CAAC,CAAC;SACzB;IACH,CAAC;IAED,oBAAoB;QAClB,IAAI,IAAI,CAAC,gBAAgB,CAAC,MAAM,KAAK,CAAC,EAAE;YACtC,OAAO;SACR;QACD,MAAM,IAAI,GAAG,IAAI,CAAC,gBAAgB,CAAC;QAEnC,GAAG,EAAE;aACF,MAAM,CAAC,KAAK,CAAC;YACZ,QAAQ,EAAE,IAAI,CAAC,SAAS;YACxB,MAAM,EAAE,IAAI;SACb,CAAC;aACD,IAAI,CAAC,GAAG,EAAE;YACT,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,yBAAyB,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;QAClE,CAAC,CAAC;aACD,KAAK,CAAC,CAAC,CAAU,EAAE,EAAE;YACpB,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,0BAA0B,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;QACtE,CAAC,CAAC,CAAC;IACP,CAAC;IAED,KAAK,CAAC,cAAc,CAAC,KAA+B;QAClD,IAAI,KAAK,CAAC,KAAK,CAAC,IAAI,KAAK,iBAAiB,EAAE;YAC1C,qDAAqD;YACrD,OAAO;SACR;QACD,MAAM,GAAG,GAAG,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC;QAE9B,MAAM,MAAM,GAAW;YACrB,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI;YACtB,YAAY,EAAE,IAAI,CAAC,IAAI;YACvB,MAAM,EAAE,GAAG,CAAC,KAAK;YACjB,gBAAgB,EAAE,GAAG,CAAC,eAAe;SACtC,CAAC;QAEF,MAAM,OAAO,GAAG,UAAU,EAAE,CAAC;QAC7B,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;QAC3B,MAAM,GAAG,GAAY;YACnB,OAAO,EAAE,OAAO;YAChB,MAAM,EAAE,MAAM;SACf,CAAC;QAEF,qCAAqC;QACrC,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QAEtC,OAAO,QAAQ,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,IAAI,EAAE;YAClC,OAAO,UAAU,CAAC,MAAM,EAAE,KAAK,IAAI,EAAE;gBACnC,MAAM,KAAK,GAAG,IAAI,KAAK,CACrB,GAAG,EAAE,CAAC,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,KAAK,CAAC,EAClC,GAAG,EAAE,CAAC,IAAI,CAAC,oBAAoB,CAAC,GAAG,CAAC,KAAK,CAAC,CAC3C,CAAC;gBAEF,IAAI;oBACF,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC;oBACrC,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CACtB,GAAG,CAAC,OAAO,EAAE,IAAI,IAAI,UAAU,CAAC,EAAE,EAAE,CACrC,CAAC,QAAQ,EAAE,CAAC;oBAEb,MAAM,KAAK,GAAG,GAAG,EAAE,CAAC;oBACpB,MAAM,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,KAAK,CAAC,CAAC;oBAC5C,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,mBAAmB,EAAE;wBACpC,QAAQ,EAAE,aAAa,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;qBACtC,CAAC,CAAC;oBAEH,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,aAAa,EAAE,CAAC;oBAC7C,IAAI,CAAC,QAAQ,EAAE;wBACb,MAAM,KAAK,CAAC,GAAG,EAAE,CAAC;qBACnB;iBACF;gBAAC,OAAO,CAAU,EAAE;oBACnB,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,gBAAgB,CAAC,CAAC;oBAEvC,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,aAAa,EAAE,CAAC;oBAC7C,IAAI,CAAC,QAAQ,EAAE;wBACb,MAAM,KAAK,CAAC,IAAI,EAAE,CAAC;qBACpB;iBACF;YACH,CAAC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,YAAY,CAAC,KAAa;QAC9B,MAAM,GAAG,EAAE,CAAC,MAAM,CAAC,WAAW,CAAC;YAC7B,QAAQ,EAAE,IAAI,CAAC,SAAS;YACxB,KAAK,EAAE,KAAK;SACb,CAAC,CAAC;QACH,IAAI,CAAC,gBAAgB,GAAG,IAAI,CAAC,gBAAgB,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,KAAK,KAAK,CAAC,CAAC;QAC3E,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,sBAAsB,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC;IAC/D,CAAC;IAED,KAAK,CAAC,oBAAoB,CAAC,KAAa;QACtC,MAAM,GAAG,EAAE,CAAC,MAAM,CAAC,mBAAmB,CAAC;YACrC,QAAQ,EAAE,IAAI,CAAC,SAAS;YACxB,KAAK,EAAE,KAAK;SACb,CAAC,CAAC;QACH,IAAI,CAAC,gBAAgB,GAAG,IAAI,CAAC,gBAAgB,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,KAAK,KAAK,CAAC,CAAC;QAC3E,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,iCAAiC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC;IAC1E,CAAC;CACF"}
@@ -0,0 +1,105 @@
1
+ import { AttributesOf } from "./attributes.js";
2
+ /**
3
+ * A topic is a resource to which you can publish messages
4
+ * to be delivered to subscribers of that topic.
5
+ */
6
+ export declare class Topic<Msg extends object> {
7
+ readonly name: string;
8
+ readonly cfg: TopicConfig<Msg>;
9
+ constructor(name: string, cfg: TopicConfig<Msg>);
10
+ publish(msg: Msg): Promise<string>;
11
+ }
12
+ /**
13
+ * DeliveryGuarantee is used to configure the delivery contract for a topic.
14
+ */
15
+ export type DeliveryGuarantee = "at-least-once" | "exactly-once";
16
+ /**
17
+ * At Least Once delivery guarantees that a message for a subscription is delivered to
18
+ * a consumer at least once.
19
+ *
20
+ * On AWS and GCP there is no limit to the throughput for a topic.
21
+ */
22
+ export declare const atLeastOnce: DeliveryGuarantee;
23
+ /**
24
+ * ExactlyOnce guarantees that a message for a subscription is delivered to
25
+ * a consumer exactly once, to the best of the system's ability.
26
+ *
27
+ * However, there are edge cases when a message might be redelivered.
28
+ * For example, if a networking issue causes the acknowledgement of success
29
+ * processing the message to be lost before the cloud provider receives it.
30
+ *
31
+ * It is also important to note that the ExactlyOnce delivery guarantee only
32
+ * applies to the delivery of the message to the consumer, and not to the
33
+ * original publishing of the message, such that if a message is published twice,
34
+ * such as due to an retry within the application logic, it will be delivered twice.
35
+ * (i.e. ExactlyOnce delivery does not imply message deduplication on publish)
36
+ *
37
+ * As such it's recommended that the subscription handler function is idempotent
38
+ * and is able to handle duplicate messages.
39
+ *
40
+ * Subscriptions attached to ExactlyOnce topics have higher message delivery latency compared to AtLeastOnce.
41
+ *
42
+ * By using ExactlyOnce semantics on a topic, the throughput will be limited depending on the cloud provider:
43
+ * - AWS: 300 messages per second for the topic (see [AWS SQS Quotas]).
44
+ * - GCP: At least 3,000 messages per second across all topics in the region
45
+ * (can be higher on the region see [GCP PubSub Quotas]).
46
+ *
47
+ * [AWS SQS Quotas]: https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/quotas-messages.html
48
+ * [GCP PubSub Quotas]: https://cloud.google.com/pubsub/quotas#quotas
49
+ */
50
+ export declare const exactlyOnce: DeliveryGuarantee;
51
+ /**
52
+ * TopicConfig is used when creating a Topic
53
+ */
54
+ export interface TopicConfig<Msg extends object> {
55
+ /**
56
+ * DeliveryGuarantee is used to configure the delivery guarantee of a Topic
57
+ */
58
+ deliveryGuarantee: DeliveryGuarantee;
59
+ /**
60
+ * OrderingAttribute is the message attribute to use as a ordering key for
61
+ * messages and delivery will ensure that messages with the same value will
62
+ * be delivered in the order they where published.
63
+ *
64
+ * If OrderingAttribute is not set, messages can be delivered in any order.
65
+ *
66
+ * It is important to note, that in the case of an error being returned by a
67
+ * subscription handler, the message will be retried before any subsequent
68
+ * messages for that ordering key are delivered. This means depending on the
69
+ * retry configuration, a large backlog of messages for a given ordering key
70
+ * may build up. When using OrderingAttribute, it is recommended to use reason
71
+ * about your failure modes and set the retry configuration appropriately.
72
+ *
73
+ * Once the maximum number of retries has been reached, the message will be
74
+ * forwarded to the dead letter queue, and the next message for that ordering
75
+ * key will be delivered.
76
+ *
77
+ * To create attributes on a message, use the `Attribute` type:
78
+ *
79
+ * type UserEvent = {
80
+ * user_id Attribute<string>;
81
+ * action string;
82
+ * }
83
+ *
84
+ * const topic = new Topic<UserEvent>("user-events", {
85
+ * deliveryGuarantee: DeliveryGuarantee.AtLeastOnce,
86
+ * orderingAttribute: "user_id", // Messages with the same user-id will be delivered in the order they where
87
+ * published
88
+ * })
89
+ *
90
+ * topic.publish(ctx, {user_id: "1", action: "login"}) // This message will be delivered before the logout
91
+ * topic.publish(ctx, {user_id: "2", action: "login"}) // This could be delivered at any time because it has a different user id
92
+ * topic.publish(ctx, {user_id: "1", action: "logout"}) // This message will be delivered after the first message
93
+ *
94
+ * By using OrderingAttribute, the throughput will be limited depending on the cloud provider:
95
+ *
96
+ * - AWS: 300 messages per second for the topic (see [AWS SQS Quotas]).
97
+ * - GCP: 1MB/s for each ordering key (see [GCP PubSub Quotas]).
98
+ *
99
+ * Note: OrderingAttribute currently has no effect during local development.
100
+ *
101
+ * [AWS SQS Quotas]: https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/quotas-messages.html
102
+ * [GCP PubSub Quotas]: https://cloud.google.com/pubsub/quotas#resource_limits
103
+ */
104
+ orderingAttribute?: AttributesOf<Msg>;
105
+ }
@@ -0,0 +1,62 @@
1
+ import { api } from "@encore.dev/internal-runtime";
2
+ import reqtrack from "@encore.dev/internal-runtime/reqtrack/index";
3
+ import { PubSub } from "@encore.dev/sidecar-api";
4
+ /**
5
+ * A topic is a resource to which you can publish messages
6
+ * to be delivered to subscribers of that topic.
7
+ */
8
+ export class Topic {
9
+ name;
10
+ cfg;
11
+ constructor(name, cfg) {
12
+ this.name = name;
13
+ this.cfg = cfg;
14
+ }
15
+ async publish(msg) {
16
+ const res = await api().pubsub.publish(new PubSub.PublishRequest({
17
+ topic: this.name,
18
+ message: new PubSub.Message({
19
+ data: Buffer.from(JSON.stringify(msg)),
20
+ attributes: {},
21
+ traceParent: reqtrack.currentTraceParent(),
22
+ }),
23
+ }));
24
+ return res.id;
25
+ }
26
+ }
27
+ /**
28
+ * At Least Once delivery guarantees that a message for a subscription is delivered to
29
+ * a consumer at least once.
30
+ *
31
+ * On AWS and GCP there is no limit to the throughput for a topic.
32
+ */
33
+ export const atLeastOnce = "at-least-once";
34
+ /**
35
+ * ExactlyOnce guarantees that a message for a subscription is delivered to
36
+ * a consumer exactly once, to the best of the system's ability.
37
+ *
38
+ * However, there are edge cases when a message might be redelivered.
39
+ * For example, if a networking issue causes the acknowledgement of success
40
+ * processing the message to be lost before the cloud provider receives it.
41
+ *
42
+ * It is also important to note that the ExactlyOnce delivery guarantee only
43
+ * applies to the delivery of the message to the consumer, and not to the
44
+ * original publishing of the message, such that if a message is published twice,
45
+ * such as due to an retry within the application logic, it will be delivered twice.
46
+ * (i.e. ExactlyOnce delivery does not imply message deduplication on publish)
47
+ *
48
+ * As such it's recommended that the subscription handler function is idempotent
49
+ * and is able to handle duplicate messages.
50
+ *
51
+ * Subscriptions attached to ExactlyOnce topics have higher message delivery latency compared to AtLeastOnce.
52
+ *
53
+ * By using ExactlyOnce semantics on a topic, the throughput will be limited depending on the cloud provider:
54
+ * - AWS: 300 messages per second for the topic (see [AWS SQS Quotas]).
55
+ * - GCP: At least 3,000 messages per second across all topics in the region
56
+ * (can be higher on the region see [GCP PubSub Quotas]).
57
+ *
58
+ * [AWS SQS Quotas]: https://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSDeveloperGuide/quotas-messages.html
59
+ * [GCP PubSub Quotas]: https://cloud.google.com/pubsub/quotas#quotas
60
+ */
61
+ export const exactlyOnce = "exactly-once";
62
+ //# sourceMappingURL=topic.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"topic.js","sourceRoot":"","sources":["../../pubsub/topic.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,8BAA8B,CAAC;AACnD,OAAO,QAAQ,MAAM,6CAA6C,CAAC;AACnE,OAAO,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAC;AAGjD;;;GAGG;AACH,MAAM,OAAO,KAAK;IACA,IAAI,CAAS;IACb,GAAG,CAAmB;IAEtC,YAAY,IAAY,EAAE,GAAqB;QAC7C,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,GAAG,GAAG,GAAG,CAAC;IACjB,CAAC;IAEM,KAAK,CAAC,OAAO,CAAC,GAAQ;QAC3B,MAAM,GAAG,GAAG,MAAM,GAAG,EAAE,CAAC,MAAM,CAAC,OAAO,CACpC,IAAI,MAAM,CAAC,cAAc,CAAC;YACxB,KAAK,EAAE,IAAI,CAAC,IAAI;YAChB,OAAO,EAAE,IAAI,MAAM,CAAC,OAAO,CAAC;gBAC1B,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;gBACtC,UAAU,EAAE,EAAE;gBACd,WAAW,EAAE,QAAQ,CAAC,kBAAkB,EAAE;aAC3C,CAAC;SACH,CAAC,CACH,CAAC;QAEF,OAAO,GAAG,CAAC,EAAE,CAAC;IAChB,CAAC;CACF;AAOD;;;;;GAKG;AACH,MAAM,CAAC,MAAM,WAAW,GAAsB,eAAe,CAAC;AAE9D;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,MAAM,CAAC,MAAM,WAAW,GAAsB,cAAc,CAAC"}
@@ -0,0 +1,51 @@
1
+ import type { StringLiteral } from "@encore.dev/internal-runtime/utils/constraints";
2
+ export interface SQLDatabaseConfig {
3
+ migrations?: string;
4
+ }
5
+ /**
6
+ * Represents a single row from a query result
7
+ */
8
+ export type Row = Record<string, any>;
9
+ /** Represents a type that can be used in query template literals */
10
+ export type Primitive = string | number | boolean | null;
11
+ /**
12
+ * Constructing a new database object will result in Encore provisioning a database with
13
+ * that name and returning this object to represent it.
14
+ *
15
+ * If you want to reference an existing database, use `Database.Named(name)` as it is a
16
+ * compile error to create duplicate databases.
17
+ */
18
+ export declare class SQLDatabase {
19
+ private readonly runtimeCfg;
20
+ private readonly pool;
21
+ /**
22
+ * Creates a new database with the given name and configuration
23
+ */
24
+ constructor(name: string, cfg?: SQLDatabaseConfig);
25
+ /**
26
+ * Reference an existing database by name, if the database doesn't
27
+ * exist yet, use `new Database(name)` instead.
28
+ */
29
+ static named<name extends string>(name: StringLiteral<name>): SQLDatabase;
30
+ /**
31
+ * Returns the connection string for the database
32
+ */
33
+ get connectionString(): string;
34
+ /**
35
+ * q allows you to query the database using a template string, replacing your placeholders in the template
36
+ * with parametrised values. It returns an async generator that you can iterate over to get the results, in a
37
+ * streaming fashion.
38
+ *
39
+ * @example
40
+ *
41
+ * const email = "foo@example.com";
42
+ * const result = database.q`SELECT id FROM users WHERE email=${email}` // produces a prepared query of
43
+ * // "SELECT id FROM users WHERE email=$1"
44
+ * // with an arg array of [email]
45
+ *
46
+ * for await (const row of result) {
47
+ * console.log(row.id);
48
+ * }
49
+ */
50
+ q<T extends Row = Record<string, any>>(strings: TemplateStringsArray, ...expr: Primitive[]): AsyncGenerator<T>;
51
+ }