presidium 0.15.18 → 0.15.23

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/DynamoStream.js CHANGED
@@ -58,9 +58,8 @@ const DynamoStream = function (options) {
58
58
  this.getRecordsLimit = options.getRecordsLimit ?? 1000
59
59
  this.getRecordsInterval = options.getRecordsInterval ?? 1000
60
60
  this.shardIteratorType = options.shardIteratorType ?? 'LATEST'
61
- this.shardUpdatePeriod = options.shardUpdatePeriod ?? 30000
61
+ this.shardUpdatePeriod = options.shardUpdatePeriod ?? 15000
62
62
  this.listStreamsLimit = options.listStreamsLimit ?? 100
63
- this.debug = options.debug ?? false
64
63
  this.client = new DynamoDBStreams({
65
64
  apiVersion: '2012-08-10',
66
65
  accessKeyId: 'id',
@@ -90,14 +89,18 @@ DynamoStream.prototype.getStreams = async function* getStreams() {
90
89
  Limit: this.listStreamsLimit,
91
90
  TableName: this.table
92
91
  }).promise()
93
- yield* streams.Streams
92
+ if (streams.Streams.length > 0) {
93
+ yield* streams.Streams
94
+ }
94
95
  while (!this.closed && streams.LastEvaluatedStreamArn != null) {
95
96
  streams = await this.client.listStreams({
96
97
  Limit: this.listStreamsLimit,
97
98
  TableName: this.table,
98
99
  ExclusiveStartStreamArn: streams.LastEvaluatedStreamArn,
99
100
  }).promise()
100
- yield* streams.Streams
101
+ if (streams.Streams.length > 0) {
102
+ yield* streams.Streams
103
+ }
101
104
  }
102
105
  }
103
106
 
@@ -109,16 +112,18 @@ DynamoStream.prototype.getShards = async function* getShards(
109
112
  StreamArn: Stream.StreamArn,
110
113
  Limit: 100,
111
114
  }).promise().then(get('StreamDescription'))
112
- yield* shards.Shards.map(
113
- (Shard, ShardNumber) => ({ ...Shard, Stream, ShardNumber }))
115
+ if (shards.Shards.length > 0) {
116
+ yield* shards.Shards.map(assign({ Stream: always(Stream) }))
117
+ }
114
118
  while (!this.closed && shards.LastEvaluatedShardId != null) {
115
119
  shards = await this.client.describeStream({
116
120
  StreamArn: Stream.StreamArn,
117
121
  Limit: 100,
118
122
  ExclusiveStartShardId: shards.LastEvaluatedShardId,
119
123
  }).promise().then(get('StreamDescription'))
120
- yield* shards.Shards.map(
121
- (Shard, ShardNumber) => ({ ...Shard, Stream, ShardNumber }))
124
+ if (shards.Shards.length > 0) {
125
+ yield* shards.Shards.map(assign({ Stream: always(Stream) }))
126
+ }
122
127
  }
123
128
  }
124
129
 
@@ -144,13 +149,23 @@ DynamoStream.prototype.getRecords = async function* getRecords(
144
149
  Limit: this.getRecordsLimit
145
150
  }).promise()
146
151
 
147
- yield* records.Records
152
+ if (records.Records.length > 0) {
153
+ yield* records.Records.map(assign({
154
+ table: always(this.table),
155
+ shardId: always(Shard.ShardId),
156
+ }))
157
+ }
148
158
  while (!this.closed && records.NextShardIterator != null) {
149
159
  records = await this.client.getRecords({
150
160
  ShardIterator: records.NextShardIterator,
151
161
  Limit: this.getRecordsLimit
152
162
  }).promise()
153
- yield* records.Records
163
+ if (records.Records.length > 0) {
164
+ yield* records.Records.map(assign({
165
+ table: always(this.table),
166
+ shardId: always(Shard.ShardId),
167
+ }))
168
+ }
154
169
  await new Promise(resolve => setTimeout(resolve, this.getRecordsInterval))
155
170
  }
156
171
  }
@@ -166,21 +181,21 @@ DynamoStream.prototype[Symbol.asyncIterator] = async function* () {
166
181
  })),
167
182
  transform(map(identity), []),
168
183
  ])()
169
- let muxAsyncIterator = Mux.race(shards.map(Shard => this.getRecords(Shard)))
170
- let iterationPromise = muxAsyncIterator.next()
171
- let shardUpdatePromise = new Promise(resolve => setTimeout(
172
- thunkify(resolve, SymbolUpdateShards), this.shardUpdatePeriod))
173
-
174
- if (this.debug) {
175
- console.log('Starting shards:', shards.map(get('ShardId')))
176
- }
184
+ let muxAsyncIterator = Mux.race([
185
+ ...shards.map(Shard => this.getRecords(Shard)),
186
+ (async function* UpdateShardsGenerator() {
187
+ while (true) {
188
+ await new Promise(resolve => {
189
+ setTimeout(resolve, this.shardUpdatePeriod)
190
+ })
191
+ yield SymbolUpdateShards
192
+ }
193
+ }).call(this),
194
+ ])
177
195
 
178
196
  while (!this.closed) {
179
- const iteration = await Promise.race([
180
- shardUpdatePromise,
181
- iterationPromise,
182
- ])
183
- if (iteration == SymbolUpdateShards) {
197
+ const { value, done } = await muxAsyncIterator.next()
198
+ if (value == SymbolUpdateShards) {
184
199
  const latestShards = await pipe([
185
200
  always(this.getStreams()),
186
201
  flatMap(Stream => this.getShards(Stream)),
@@ -197,23 +212,17 @@ DynamoStream.prototype[Symbol.asyncIterator] = async function* () {
197
212
  })),
198
213
  ])()
199
214
 
200
- if (this.debug) {
201
- console.log('Latest shards:', latestShards.map(get('ShardId')))
202
- }
203
-
204
215
  shards = latestShards
205
- muxAsyncIterator = newShards.length == 0 ? muxAsyncIterator : Mux.race([
206
- ...newShards.map(Shard => this.getRecords(Shard)),
207
- muxAsyncIterator,
208
- ])
209
- shardUpdatePromise = new Promise(resolve => setTimeout(
210
- thunkify(resolve, SymbolUpdateShards), this.shardUpdatePeriod))
211
- } else if (iteration.done) {
216
+ if (newShards.length > 0) {
217
+ muxAsyncIterator = Mux.race([
218
+ ...newShards.map(Shard => this.getRecords(Shard)),
219
+ muxAsyncIterator,
220
+ ])
221
+ }
222
+ } else if (done) {
212
223
  await new Promise(resolve => setTimeout(resolve, 1000))
213
- iterationPromise = muxAsyncIterator.next()
214
224
  } else {
215
- yield iteration.value
216
- iterationPromise = muxAsyncIterator.next()
225
+ yield value
217
226
  }
218
227
  }
219
228
  }
package/KinesisStream.js CHANGED
@@ -50,7 +50,7 @@ const KinesisStream = function (options) {
50
50
  this.name = options.name
51
51
  this.listShardsLimit = options.listShardsLimit ?? 1000
52
52
  this.getRecordsLimit = options.getRecordsLimit ?? 1000
53
- this.shardUpdateInterval = options.shardUpdateInterval ?? 10000
53
+ this.shardUpdatePeriod = options.shardUpdatePeriod ?? 15000
54
54
  this.getRecordsInterval = options.getRecordsInterval ?? 1000
55
55
  this.shardIteratorType = options.shardIteratorType ?? 'LATEST'
56
56
  this.shardIteratorTimestamp = options.shardIteratorTimestamp
@@ -183,43 +183,44 @@ const SymbolUpdateShards = Symbol('UpdateShards')
183
183
 
184
184
  KinesisStream.prototype[Symbol.asyncIterator] = async function* generateRecords() {
185
185
  let shards = await transform(map(identity), [])(this.listShards())
186
- let muxAsyncIterator = Mux.race(shards.map(Shard => this.getRecords(Shard)))
187
- let iterationPromise = muxAsyncIterator.next()
188
- let shardUpdatePromise = new Promise(resolve => setTimeout(
189
- thunkify(resolve, SymbolUpdateShards),
190
- this.shardUpdateInterval,
191
- ))
186
+ let muxAsyncIterator = Mux.race([
187
+ ...shards.map(Shard => this.getRecords(Shard)),
188
+ (async function* UpdateShardsGenerator() {
189
+ while (true) {
190
+ await new Promise(resolve => {
191
+ setTimeout(resolve, this.shardUpdatePeriod)
192
+ })
193
+ yield SymbolUpdateShards
194
+ }
195
+ }).call(this),
196
+ ])
192
197
 
193
198
  while (!this.closed) {
194
- const iteration = await Promise.race([
195
- shardUpdatePromise,
196
- iterationPromise,
197
- ])
198
- if (iteration == SymbolUpdateShards) {
199
+ const { value, done } = await muxAsyncIterator.next()
200
+ if (value == SymbolUpdateShards) {
199
201
  const latestShards = await transform(map(identity), [])(this.listShards())
200
- const newShards = differenceWith(
201
- (ShardA, ShardB) => ShardA.ShardId == ShardB.ShardId,
202
- latestShards,
203
- )(shards)
204
- const closedShards = differenceWith(
205
- (ShardA, ShardB) => ShardA.ShardId == ShardB.ShardId,
206
- shards,
207
- )(latestShards)
202
+ const newShards = pipe([
203
+ always(shards),
204
+ differenceWith(
205
+ (ShardA, ShardB) => ShardA.ShardId == ShardB.ShardId,
206
+ latestShards,
207
+ ),
208
+ map(assign({
209
+ ShardIteratorType: always('TRIM_HORIZON'),
210
+ })),
211
+ ])()
208
212
 
209
- closedShards.forEach(Shard => (Shard.closed = true))
210
213
  shards = latestShards
211
- muxAsyncIterator = newShards.length == 0 ? muxAsyncIterator : Mux.race([
212
- ...newShards.map(Shard => this.getRecords(Shard)),
213
- muxAsyncIterator,
214
- ])
215
- shardUpdatePromise = new Promise(resolve => setTimeout(
216
- thunkify(resolve, SymbolUpdateShards), this.shardUpdateInterval))
217
- } else if (iteration.done) {
214
+ if (newShards.length > 0) {
215
+ muxAsyncIterator = Mux.race([
216
+ ...newShards.map(Shard => this.getRecords(Shard)),
217
+ muxAsyncIterator,
218
+ ])
219
+ }
220
+ } else if (done) {
218
221
  await new Promise(resolve => setTimeout(resolve, 1000))
219
- iterationPromise = muxAsyncIterator.next()
220
222
  } else {
221
- yield iteration.value
222
- iterationPromise = muxAsyncIterator.next()
223
+ yield value
223
224
  }
224
225
  }
225
226
  }
@@ -27,6 +27,7 @@ const test = new Test('KinesisStream', KinesisStream)
27
27
  assert.deepEqual(first3, first3Again)
28
28
  this.streams.push(myStream)
29
29
  })
30
+
30
31
  .case({
31
32
  name: 'my-stream',
32
33
  endpoint: 'http://localhost:4567',
@@ -46,6 +47,26 @@ const test = new Test('KinesisStream', KinesisStream)
46
47
  myStream.close()
47
48
  this.streams.push(myStream)
48
49
  })
50
+
51
+ .case({
52
+ name: 'my-stream',
53
+ endpoint: 'http://localhost:4567',
54
+ shardUpdatePeriod: 500,
55
+ }, async function (myStream) {
56
+ await myStream.ready
57
+
58
+ // there shouldn't be any more records, so this should hang
59
+ const latestRecordPromise = asyncIterableTake(1)(myStream)
60
+ const raceResult = await Promise.race([
61
+ latestRecordPromise,
62
+ new Promise(resolve => setTimeout(thunkify(resolve, 'hey'), 3000))
63
+ ])
64
+ assert.equal(raceResult, 'hey')
65
+ // wait a second for shard update
66
+ await new Promise(resolve => setTimeout(thunkify(resolve, 'hey'), 1000))
67
+ myStream.close()
68
+ })
69
+
49
70
  .after(async function() {
50
71
  await map(stream => stream.delete())(this.streams)
51
72
  })
@@ -29,11 +29,11 @@ const CHECKSUM_LENGTH = 4
29
29
  const MINIMUM_MESSAGE_LENGTH = PRELUDE_LENGTH + CHECKSUM_LENGTH * 2
30
30
 
31
31
  /**
32
- * @name TranscribeStreaming
32
+ * @name TranscribeStream
33
33
  *
34
34
  * @synopsis
35
35
  * ```coffeescript [specscript]
36
- * const myTranscribeStream = new TranscribeStreaming(options {
36
+ * const myTranscribeStream = new TranscribeStream(options {
37
37
  * accessKeyId: string,
38
38
  * secretAccessKey: string,
39
39
  * region: string,
@@ -44,6 +44,24 @@ const MINIMUM_MESSAGE_LENGTH = PRELUDE_LENGTH + CHECKSUM_LENGTH * 2
44
44
  * sessionId?: string,
45
45
  * vocabularyName?: string,
46
46
  * })
47
+ *
48
+ * myTranscribeStream.on('transcription', transcriptionHandler (transcription {
49
+ * Alternatives: Array<{
50
+ * Items: Array<{
51
+ * Confidence?: number,
52
+ * Content: string,
53
+ * EndTime: number, // seconds
54
+ * StartTime: number, // seconds
55
+ * Type: 'pronunciation'|'punctuation',
56
+ * VocabularyFilterMatch: boolean,
57
+ * }>,
58
+ * Transcript: string,
59
+ * }>,
60
+ * EndTime: number, // seconds
61
+ * IsPartial: boolean,
62
+ * ResultId: string, // uuid
63
+ * StartTime: number, // seconds
64
+ * })=><>)
47
65
  * ```
48
66
  *
49
67
  * @description
@@ -59,7 +77,7 @@ const MINIMUM_MESSAGE_LENGTH = PRELUDE_LENGTH + CHECKSUM_LENGTH * 2
59
77
  *
60
78
  * `vocabularyName` - The name of the vocabulary to use when processing the transcription job, if any.
61
79
  */
62
- const TranscribeStreaming = function (options) {
80
+ const TranscribeStream = function (options) {
63
81
  const {
64
82
  accessKeyId,
65
83
  secretAccessKey,
@@ -109,10 +127,10 @@ const TranscribeStreaming = function (options) {
109
127
  return this
110
128
  }
111
129
 
112
- TranscribeStreaming.prototype = EventEmitter.prototype
130
+ TranscribeStream.prototype = EventEmitter.prototype
113
131
 
114
132
  /**
115
- * @name TranscribeStreaming.prototype.sendAudioChunk
133
+ * @name TranscribeStream.prototype.sendAudioChunk
116
134
  *
117
135
  * @synopsis
118
136
  * ```coffeescript [specscript]
@@ -125,7 +143,7 @@ TranscribeStreaming.prototype = EventEmitter.prototype
125
143
  * https://docs.aws.amazon.com/transcribe/latest/dg/event-stream.html
126
144
  * https://github.com/aws-samples/amazon-transcribe-comprehend-medical-twilio/blob/main/lib/transcribe-service.js
127
145
  */
128
- TranscribeStreaming.prototype.sendAudioChunk = function (chunk) {
146
+ TranscribeStream.prototype.sendAudioChunk = function (chunk) {
129
147
  const headersBytes = marshalHeaders({
130
148
  ':message-type': {
131
149
  type: 'string',
@@ -273,4 +291,4 @@ const unmarshalHeaders = function (headersView) {
273
291
  return headers
274
292
  }
275
293
 
276
- module.exports = TranscribeStreaming
294
+ module.exports = TranscribeStream
@@ -4,7 +4,7 @@ const WaveFile = require('wavefile').WaveFile
4
4
  const Test = require('thunk-test')
5
5
  const assert = require('assert')
6
6
  const rubico = require('rubico')
7
- const TranscribeStreaming = require('./TranscribeStreaming')
7
+ const TranscribeStream = require('./TranscribeStream')
8
8
  const AwsCredentials = require('./internal/AwsCredentials')
9
9
 
10
10
  const {
@@ -18,7 +18,7 @@ const {
18
18
  curry, __,
19
19
  } = rubico
20
20
 
21
- const test = new Test('TranscribeStreaming', async function () {
21
+ const test = new Test('TranscribeStream', async function () {
22
22
  const awsCreds = await AwsCredentials('default').catch(error => {
23
23
  if (error.code == 'ENOENT') {
24
24
  const accessKeyId = process.env.AWS_ACCESS_KEY_ID
@@ -32,13 +32,13 @@ const test = new Test('TranscribeStreaming', async function () {
32
32
  })
33
33
  awsCreds.region = 'us-east-1' // only valid region for transcribe
34
34
 
35
- const testTranscribeStreaming = new TranscribeStreaming({
35
+ const testTranscribeStream = new TranscribeStream({
36
36
  languageCode: 'en-US',
37
37
  mediaEncoding: 'pcm',
38
38
  sampleRate: 8000,
39
39
  ...awsCreds,
40
40
  })
41
- await testTranscribeStreaming.ready
41
+ await testTranscribeStream.ready
42
42
 
43
43
  const mediaStreamFixtureAwsKeynote =
44
44
  fs.createReadStream('./media-stream-fixture-aws-keynote.txt')
@@ -51,14 +51,14 @@ const test = new Test('TranscribeStreaming', async function () {
51
51
  const wav = new WaveFile()
52
52
  wav.fromScratch(1, 8000, '8', Buffer.from(event.media.payload, 'base64'))
53
53
  wav.fromMuLaw()
54
- testTranscribeStreaming.sendAudioChunk(Buffer.from(wav.data.samples))
54
+ testTranscribeStream.sendAudioChunk(Buffer.from(wav.data.samples))
55
55
  } else if (event.event == 'stop') {
56
- testTranscribeStreaming.websocket.close()
56
+ testTranscribeStream.websocket.close()
57
57
  }
58
58
  })
59
59
 
60
60
  const testTranscription = await new Promise(resolve => {
61
- testTranscribeStreaming.on('transcription', transcription => {
61
+ testTranscribeStream.on('transcription', transcription => {
62
62
  resolve(transcription.Alternatives[0].Transcript)
63
63
  })
64
64
  })
package/index.js CHANGED
@@ -31,24 +31,43 @@ const S3Bucket = require('./S3Bucket')
31
31
  // const Redshift = require('./Redshift')
32
32
  // const Gremlin = require('./Gremlin')
33
33
  const Redis = require('./Redis')
34
+ const TranscribeStream = require('./TranscribeStream')
34
35
 
35
36
  const Presidium = {
36
- Http, HttpServer,
37
- WebSocket, WebSocketServer,
38
- Dynamo, DynamoTable, DynamoIndex, DynamoStream,
39
- Docker, DockerImage, DockerContainer, DockerSwarm, DockerService,
40
- // ElasticTranscoder, ElasticTranscoderPipeline,
41
- // KinesisAnalytics, KinesisAnalyticsStream,
42
- // KinesisVideo, KinesisVideoStream,
43
- Kinesis, KinesisStream,
44
- Lambda, LambdaFunction,
45
- Mongo, MongoCollection,
37
+ Http,
38
+ HttpServer,
39
+ WebSocket,
40
+ WebSocketServer,
41
+ Dynamo,
42
+ DynamoTable,
43
+ DynamoIndex,
44
+ DynamoStream,
45
+ Docker,
46
+ DockerImage,
47
+ DockerContainer,
48
+ DockerSwarm,
49
+ DockerService,
50
+ // ElasticTranscoder,
51
+ // ElasticTranscoderPipeline,
52
+ // KinesisAnalytics,
53
+ // KinesisAnalyticsStream,
54
+ // KinesisVideo,
55
+ // KinesisVideoStream,
56
+ Kinesis,
57
+ KinesisStream,
58
+ Lambda,
59
+ LambdaFunction,
60
+ Mongo,
61
+ MongoCollection,
46
62
  ElasticsearchIndex,
47
- // SNS, SNSTopic,
48
- S3, S3Bucket,
63
+ // SNS,
64
+ // SNSTopic,
65
+ S3,
66
+ S3Bucket,
49
67
  // Redshift,
50
68
  // Gremlin,
51
69
  Redis,
70
+ TranscribeStream,
52
71
  }
53
72
 
54
73
  module.exports = Presidium
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "presidium",
3
- "version": "0.15.18",
3
+ "version": "0.15.23",
4
4
  "description": "A library for creating web services",
5
5
  "author": "Richard Tong",
6
6
  "license": "MIT",