@depup/postgres 3.4.8-depup.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.
@@ -0,0 +1,173 @@
1
+ const originCache = new Map()
2
+ , originStackCache = new Map()
3
+ , originError = Symbol('OriginError')
4
+
5
+ export const CLOSE = {}
6
+ export class Query extends Promise {
7
+ constructor(strings, args, handler, canceller, options = {}) {
8
+ let resolve
9
+ , reject
10
+
11
+ super((a, b) => {
12
+ resolve = a
13
+ reject = b
14
+ })
15
+
16
+ this.tagged = Array.isArray(strings.raw)
17
+ this.strings = strings
18
+ this.args = args
19
+ this.handler = handler
20
+ this.canceller = canceller
21
+ this.options = options
22
+
23
+ this.state = null
24
+ this.statement = null
25
+
26
+ this.resolve = x => (this.active = false, resolve(x))
27
+ this.reject = x => (this.active = false, reject(x))
28
+
29
+ this.active = false
30
+ this.cancelled = null
31
+ this.executed = false
32
+ this.signature = ''
33
+
34
+ this[originError] = this.handler.debug
35
+ ? new Error()
36
+ : this.tagged && cachedError(this.strings)
37
+ }
38
+
39
+ get origin() {
40
+ return (this.handler.debug
41
+ ? this[originError].stack
42
+ : this.tagged && originStackCache.has(this.strings)
43
+ ? originStackCache.get(this.strings)
44
+ : originStackCache.set(this.strings, this[originError].stack).get(this.strings)
45
+ ) || ''
46
+ }
47
+
48
+ static get [Symbol.species]() {
49
+ return Promise
50
+ }
51
+
52
+ cancel() {
53
+ return this.canceller && (this.canceller(this), this.canceller = null)
54
+ }
55
+
56
+ simple() {
57
+ this.options.simple = true
58
+ this.options.prepare = false
59
+ return this
60
+ }
61
+
62
+ async readable() {
63
+ this.simple()
64
+ this.streaming = true
65
+ return this
66
+ }
67
+
68
+ async writable() {
69
+ this.simple()
70
+ this.streaming = true
71
+ return this
72
+ }
73
+
74
+ cursor(rows = 1, fn) {
75
+ this.options.simple = false
76
+ if (typeof rows === 'function') {
77
+ fn = rows
78
+ rows = 1
79
+ }
80
+
81
+ this.cursorRows = rows
82
+
83
+ if (typeof fn === 'function')
84
+ return (this.cursorFn = fn, this)
85
+
86
+ let prev
87
+ return {
88
+ [Symbol.asyncIterator]: () => ({
89
+ next: () => {
90
+ if (this.executed && !this.active)
91
+ return { done: true }
92
+
93
+ prev && prev()
94
+ const promise = new Promise((resolve, reject) => {
95
+ this.cursorFn = value => {
96
+ resolve({ value, done: false })
97
+ return new Promise(r => prev = r)
98
+ }
99
+ this.resolve = () => (this.active = false, resolve({ done: true }))
100
+ this.reject = x => (this.active = false, reject(x))
101
+ })
102
+ this.execute()
103
+ return promise
104
+ },
105
+ return() {
106
+ prev && prev(CLOSE)
107
+ return { done: true }
108
+ }
109
+ })
110
+ }
111
+ }
112
+
113
+ describe() {
114
+ this.options.simple = false
115
+ this.onlyDescribe = this.options.prepare = true
116
+ return this
117
+ }
118
+
119
+ stream() {
120
+ throw new Error('.stream has been renamed to .forEach')
121
+ }
122
+
123
+ forEach(fn) {
124
+ this.forEachFn = fn
125
+ this.handle()
126
+ return this
127
+ }
128
+
129
+ raw() {
130
+ this.isRaw = true
131
+ return this
132
+ }
133
+
134
+ values() {
135
+ this.isRaw = 'values'
136
+ return this
137
+ }
138
+
139
+ async handle() {
140
+ !this.executed && (this.executed = true) && await 1 && this.handler(this)
141
+ }
142
+
143
+ execute() {
144
+ this.handle()
145
+ return this
146
+ }
147
+
148
+ then() {
149
+ this.handle()
150
+ return super.then.apply(this, arguments)
151
+ }
152
+
153
+ catch() {
154
+ this.handle()
155
+ return super.catch.apply(this, arguments)
156
+ }
157
+
158
+ finally() {
159
+ this.handle()
160
+ return super.finally.apply(this, arguments)
161
+ }
162
+ }
163
+
164
+ function cachedError(xs) {
165
+ if (originCache.has(xs))
166
+ return originCache.get(xs)
167
+
168
+ const x = Error.stackTraceLimit
169
+ Error.stackTraceLimit = 4
170
+ originCache.set(xs, new Error())
171
+ Error.stackTraceLimit = x
172
+ return originCache.get(xs)
173
+ }
@@ -0,0 +1,31 @@
1
+ export default Queue
2
+
3
+ function Queue(initial = []) {
4
+ let xs = initial.slice()
5
+ let index = 0
6
+
7
+ return {
8
+ get length() {
9
+ return xs.length - index
10
+ },
11
+ remove: (x) => {
12
+ const index = xs.indexOf(x)
13
+ return index === -1
14
+ ? null
15
+ : (xs.splice(index, 1), x)
16
+ },
17
+ push: (x) => (xs.push(x), x),
18
+ shift: () => {
19
+ const out = xs[index++]
20
+
21
+ if (index === xs.length) {
22
+ index = 0
23
+ xs = []
24
+ } else {
25
+ xs[index - 1] = undefined
26
+ }
27
+
28
+ return out
29
+ }
30
+ }
31
+ }
@@ -0,0 +1,16 @@
1
+ export default class Result extends Array {
2
+ constructor() {
3
+ super()
4
+ Object.defineProperties(this, {
5
+ count: { value: null, writable: true },
6
+ state: { value: null, writable: true },
7
+ command: { value: null, writable: true },
8
+ columns: { value: null, writable: true },
9
+ statement: { value: null, writable: true }
10
+ })
11
+ }
12
+
13
+ static get [Symbol.species]() {
14
+ return Array
15
+ }
16
+ }
@@ -0,0 +1,278 @@
1
+ import { Buffer } from 'node:buffer'
2
+ const noop = () => { /* noop */ }
3
+
4
+ export default function Subscribe(postgres, options) {
5
+ const subscribers = new Map()
6
+ , slot = 'postgresjs_' + Math.random().toString(36).slice(2)
7
+ , state = {}
8
+
9
+ let connection
10
+ , stream
11
+ , ended = false
12
+
13
+ const sql = subscribe.sql = postgres({
14
+ ...options,
15
+ transform: { column: {}, value: {}, row: {} },
16
+ max: 1,
17
+ fetch_types: false,
18
+ idle_timeout: null,
19
+ max_lifetime: null,
20
+ connection: {
21
+ ...options.connection,
22
+ replication: 'database'
23
+ },
24
+ onclose: async function() {
25
+ if (ended)
26
+ return
27
+ stream = null
28
+ state.pid = state.secret = undefined
29
+ connected(await init(sql, slot, options.publications))
30
+ subscribers.forEach(event => event.forEach(({ onsubscribe }) => onsubscribe()))
31
+ },
32
+ no_subscribe: true
33
+ })
34
+
35
+ const end = sql.end
36
+ , close = sql.close
37
+
38
+ sql.end = async() => {
39
+ ended = true
40
+ stream && (await new Promise(r => (stream.once('close', r), stream.end())))
41
+ return end()
42
+ }
43
+
44
+ sql.close = async() => {
45
+ stream && (await new Promise(r => (stream.once('close', r), stream.end())))
46
+ return close()
47
+ }
48
+
49
+ return subscribe
50
+
51
+ async function subscribe(event, fn, onsubscribe = noop, onerror = noop) {
52
+ event = parseEvent(event)
53
+
54
+ if (!connection)
55
+ connection = init(sql, slot, options.publications)
56
+
57
+ const subscriber = { fn, onsubscribe }
58
+ const fns = subscribers.has(event)
59
+ ? subscribers.get(event).add(subscriber)
60
+ : subscribers.set(event, new Set([subscriber])).get(event)
61
+
62
+ const unsubscribe = () => {
63
+ fns.delete(subscriber)
64
+ fns.size === 0 && subscribers.delete(event)
65
+ }
66
+
67
+ return connection.then(x => {
68
+ connected(x)
69
+ onsubscribe()
70
+ stream && stream.on('error', onerror)
71
+ return { unsubscribe, state, sql }
72
+ })
73
+ }
74
+
75
+ function connected(x) {
76
+ stream = x.stream
77
+ state.pid = x.state.pid
78
+ state.secret = x.state.secret
79
+ }
80
+
81
+ async function init(sql, slot, publications) {
82
+ if (!publications)
83
+ throw new Error('Missing publication names')
84
+
85
+ const xs = await sql.unsafe(
86
+ `CREATE_REPLICATION_SLOT ${ slot } TEMPORARY LOGICAL pgoutput NOEXPORT_SNAPSHOT`
87
+ )
88
+
89
+ const [x] = xs
90
+
91
+ const stream = await sql.unsafe(
92
+ `START_REPLICATION SLOT ${ slot } LOGICAL ${
93
+ x.consistent_point
94
+ } (proto_version '1', publication_names '${ publications }')`
95
+ ).writable()
96
+
97
+ const state = {
98
+ lsn: Buffer.concat(x.consistent_point.split('/').map(x => Buffer.from(('00000000' + x).slice(-8), 'hex')))
99
+ }
100
+
101
+ stream.on('data', data)
102
+ stream.on('error', error)
103
+ stream.on('close', sql.close)
104
+
105
+ return { stream, state: xs.state }
106
+
107
+ function error(e) {
108
+ console.error('Unexpected error during logical streaming - reconnecting', e) // eslint-disable-line
109
+ }
110
+
111
+ function data(x) {
112
+ if (x[0] === 0x77) {
113
+ parse(x.subarray(25), state, sql.options.parsers, handle, options.transform)
114
+ } else if (x[0] === 0x6b && x[17]) {
115
+ state.lsn = x.subarray(1, 9)
116
+ pong()
117
+ }
118
+ }
119
+
120
+ function handle(a, b) {
121
+ const path = b.relation.schema + '.' + b.relation.table
122
+ call('*', a, b)
123
+ call('*:' + path, a, b)
124
+ b.relation.keys.length && call('*:' + path + '=' + b.relation.keys.map(x => a[x.name]), a, b)
125
+ call(b.command, a, b)
126
+ call(b.command + ':' + path, a, b)
127
+ b.relation.keys.length && call(b.command + ':' + path + '=' + b.relation.keys.map(x => a[x.name]), a, b)
128
+ }
129
+
130
+ function pong() {
131
+ const x = Buffer.alloc(34)
132
+ x[0] = 'r'.charCodeAt(0)
133
+ x.fill(state.lsn, 1)
134
+ x.writeBigInt64BE(BigInt(Date.now() - Date.UTC(2000, 0, 1)) * BigInt(1000), 25)
135
+ stream.write(x)
136
+ }
137
+ }
138
+
139
+ function call(x, a, b) {
140
+ subscribers.has(x) && subscribers.get(x).forEach(({ fn }) => fn(a, b, x))
141
+ }
142
+ }
143
+
144
+ function Time(x) {
145
+ return new Date(Date.UTC(2000, 0, 1) + Number(x / BigInt(1000)))
146
+ }
147
+
148
+ function parse(x, state, parsers, handle, transform) {
149
+ const char = (acc, [k, v]) => (acc[k.charCodeAt(0)] = v, acc)
150
+
151
+ Object.entries({
152
+ R: x => { // Relation
153
+ let i = 1
154
+ const r = state[x.readUInt32BE(i)] = {
155
+ schema: x.toString('utf8', i += 4, i = x.indexOf(0, i)) || 'pg_catalog',
156
+ table: x.toString('utf8', i + 1, i = x.indexOf(0, i + 1)),
157
+ columns: Array(x.readUInt16BE(i += 2)),
158
+ keys: []
159
+ }
160
+ i += 2
161
+
162
+ let columnIndex = 0
163
+ , column
164
+
165
+ while (i < x.length) {
166
+ column = r.columns[columnIndex++] = {
167
+ key: x[i++],
168
+ name: transform.column.from
169
+ ? transform.column.from(x.toString('utf8', i, i = x.indexOf(0, i)))
170
+ : x.toString('utf8', i, i = x.indexOf(0, i)),
171
+ type: x.readUInt32BE(i += 1),
172
+ parser: parsers[x.readUInt32BE(i)],
173
+ atttypmod: x.readUInt32BE(i += 4)
174
+ }
175
+
176
+ column.key && r.keys.push(column)
177
+ i += 4
178
+ }
179
+ },
180
+ Y: () => { /* noop */ }, // Type
181
+ O: () => { /* noop */ }, // Origin
182
+ B: x => { // Begin
183
+ state.date = Time(x.readBigInt64BE(9))
184
+ state.lsn = x.subarray(1, 9)
185
+ },
186
+ I: x => { // Insert
187
+ let i = 1
188
+ const relation = state[x.readUInt32BE(i)]
189
+ const { row } = tuples(x, relation.columns, i += 7, transform)
190
+
191
+ handle(row, {
192
+ command: 'insert',
193
+ relation
194
+ })
195
+ },
196
+ D: x => { // Delete
197
+ let i = 1
198
+ const relation = state[x.readUInt32BE(i)]
199
+ i += 4
200
+ const key = x[i] === 75
201
+ handle(key || x[i] === 79
202
+ ? tuples(x, relation.columns, i += 3, transform).row
203
+ : null
204
+ , {
205
+ command: 'delete',
206
+ relation,
207
+ key
208
+ })
209
+ },
210
+ U: x => { // Update
211
+ let i = 1
212
+ const relation = state[x.readUInt32BE(i)]
213
+ i += 4
214
+ const key = x[i] === 75
215
+ const xs = key || x[i] === 79
216
+ ? tuples(x, relation.columns, i += 3, transform)
217
+ : null
218
+
219
+ xs && (i = xs.i)
220
+
221
+ const { row } = tuples(x, relation.columns, i + 3, transform)
222
+
223
+ handle(row, {
224
+ command: 'update',
225
+ relation,
226
+ key,
227
+ old: xs && xs.row
228
+ })
229
+ },
230
+ T: () => { /* noop */ }, // Truncate,
231
+ C: () => { /* noop */ } // Commit
232
+ }).reduce(char, {})[x[0]](x)
233
+ }
234
+
235
+ function tuples(x, columns, xi, transform) {
236
+ let type
237
+ , column
238
+ , value
239
+
240
+ const row = transform.raw ? new Array(columns.length) : {}
241
+ for (let i = 0; i < columns.length; i++) {
242
+ type = x[xi++]
243
+ column = columns[i]
244
+ value = type === 110 // n
245
+ ? null
246
+ : type === 117 // u
247
+ ? undefined
248
+ : column.parser === undefined
249
+ ? x.toString('utf8', xi + 4, xi += 4 + x.readUInt32BE(xi))
250
+ : column.parser.array === true
251
+ ? column.parser(x.toString('utf8', xi + 5, xi += 4 + x.readUInt32BE(xi)))
252
+ : column.parser(x.toString('utf8', xi + 4, xi += 4 + x.readUInt32BE(xi)))
253
+
254
+ transform.raw
255
+ ? (row[i] = transform.raw === true
256
+ ? value
257
+ : transform.value.from ? transform.value.from(value, column) : value)
258
+ : (row[column.name] = transform.value.from
259
+ ? transform.value.from(value, column)
260
+ : value
261
+ )
262
+ }
263
+
264
+ return { i: xi, row: transform.row.from ? transform.row.from(row) : row }
265
+ }
266
+
267
+ function parseEvent(x) {
268
+ const xs = x.match(/^(\*|insert|update|delete)?:?([^.]+?\.?[^=]+)?=?(.+)?/i) || []
269
+
270
+ if (!xs)
271
+ throw new Error('Malformed subscribe pattern: ' + x)
272
+
273
+ const [, command, path, key] = xs
274
+
275
+ return (command || '*')
276
+ + (path ? ':' + (path.indexOf('.') === -1 ? 'public.' + path : path) : '')
277
+ + (key ? '=' + key : '')
278
+ }