@reventlessdev/rescript-pulumi-aws 3.0.0-alpha.26 → 3.0.0-alpha.28

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/CHANGELOG.md CHANGED
@@ -3,6 +3,25 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ # 3.0.0-alpha.28 (2026-10-15)
7
+
8
+ * feat!: schedules take a Schedule.time, run on EventBridge Scheduler on AWS ([2d74557](https://github.com/ReventlessDev/reventless-core/commit/2d74557a6304917ded8f2b9f4107af9298c91ac0))
9
+
10
+ ### BREAKING CHANGES
11
+
12
+ * Schedule.rate is replaced by Schedule.time, and a
13
+ schedule's `rate` field is now `time`. A once-only time already past
14
+ never fires, locally as on AWS.
15
+
16
+
17
+
18
+ # 3.0.0-alpha.27 (2026-10-10)
19
+
20
+ ### Bug Fixes
21
+
22
+ * **pulumi-aws:** a list's last page no longer promises another ([00b141f](https://github.com/ReventlessDev/reventless-core/commit/00b141fbfdbf44544fbea8ab979356831381f48a))
23
+
24
+
6
25
  # 3.0.0-alpha.26 (2026-10-10)
7
26
 
8
27
  ### Bug Fixes
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reventlessdev/rescript-pulumi-aws",
3
- "version": "3.0.0-alpha.26",
3
+ "version": "3.0.0-alpha.28",
4
4
  "description": "ReScript bindings for @pulumi/aws",
5
5
  "license": "Apache-2.0",
6
6
  "jest": {
@@ -22,9 +22,9 @@
22
22
  "@pulumi/aws": "~7.19.0",
23
23
  "@pulumi/aws-native": "^1.62.0",
24
24
  "sury": "11.0.0",
25
+ "@reventlessdev/rescript-aws-sdk": "3.0.0-alpha.31",
25
26
  "@reventlessdev/rescript-node": "2.0.0-alpha.22",
26
- "@reventlessdev/rescript-pulumi-pulumi": "2.3.0-alpha.23",
27
- "@reventlessdev/rescript-aws-sdk": "3.0.0-alpha.30"
27
+ "@reventlessdev/rescript-pulumi-pulumi": "2.3.0-alpha.23"
28
28
  },
29
29
  "devDependencies": {
30
30
  "esbuild": "^0.25.12",
@@ -564,19 +564,24 @@ the survivors serves short — usually empty — pages under a selective filter.
564
564
  no loops in APPSYNC_JS the door reads wider instead and addresses the surplus by
565
565
  position. 1 MB caps a page anyway, hence 1000; `filtered` is the JS expression
566
566
  saying whether a filter was pushed down.
567
+
568
+ An unfiltered read takes one row past the page. DynamoDB hands back a token
569
+ whenever it stops AT the limit, end of data or not, so a read of exactly the page
570
+ reports a next page that comes back blank; the extra row is what says one exists.
567
571
  */
568
572
  let pageWindowBudget = (~filtered: string) =>
569
- `(${filtered} ? (_first > 1000 ? _first : 1000) : _first + _from)`
573
+ `(${filtered} ? (_first > 1000 ? _first : 1000) : _first + _from + 1)`
570
574
 
571
575
  /**
572
576
  The same budget for the full-list door, which may also read backward.
573
577
 
574
578
  A backward page is the slice `[_from, _upTo)` of the window, so the read has to
575
579
  reach `_upTo` rows into it — `_first + _from` would stop short and hand back a
576
- page with its tail missing.
580
+ page with its tail missing. Forward reads one row past the page, for the reason
581
+ `pageWindowBudget` gives; a backward page needs none, as one provably follows it.
577
582
  */
578
583
  let listPageWindowBudget = {
579
- let need = "(_backward ? _upTo : _first + _from)"
584
+ let need = "(_backward ? _upTo : _first + _from + 1)"
580
585
  `(parts.length > 0 ? (${need} > 1000 ? ${need} : 1000) : ${need})`
581
586
  }
582
587
 
@@ -389,10 +389,10 @@ export function request(ctx) {
389
389
  }
390
390
 
391
391
  function pageWindowBudget(filtered) {
392
- return `(` + filtered + ` ? (_first > 1000 ? _first : 1000) : _first + _from)`;
392
+ return `(` + filtered + ` ? (_first > 1000 ? _first : 1000) : _first + _from + 1)`;
393
393
  }
394
394
 
395
- let need = "(_backward ? _upTo : _first + _from)";
395
+ let need = "(_backward ? _upTo : _first + _from + 1)";
396
396
 
397
397
  let listPageWindowBudget = `(parts.length > 0 ? (` + need + ` > 1000 ? ` + need + ` : 1000) : ` + need + `)`;
398
398
 
@@ -528,7 +528,7 @@ export function request(ctx) {
528
528
  operation: 'Query',
529
529
  query,
530
530
  index: '` + index + `',
531
- limit: ` + (`(` + "expression" + ` ? (_first > 1000 ? _first : 1000) : _first + _from)`) + `,
531
+ limit: ` + (`(` + "expression" + ` ? (_first > 1000 ? _first : 1000) : _first + _from + 1)`) + `,
532
532
  nextToken: _window,
533
533
  scanIndexForward: (args.forward ?? true)
534
534
  };
@@ -585,7 +585,7 @@ export function request(ctx) {
585
585
  operation: 'Query',
586
586
  query,
587
587
  index: '` + index + `',
588
- limit: ` + (`(` + "expression" + ` ? (_first > 1000 ? _first : 1000) : _first + _from)`) + `,
588
+ limit: ` + (`(` + "expression" + ` ? (_first > 1000 ? _first : 1000) : _first + _from + 1)`) + `,
589
589
  nextToken: _window,
590
590
  scanIndexForward: (args.forward ?? true)
591
591
  };
@@ -0,0 +1,4 @@
1
+ /** @pulumi/aws/scheduler
2
+ see: https://www.pulumi.com/registry/packages/aws/api-docs/scheduler/
3
+ */
4
+ module ScheduleGroup = Scheduler_ScheduleGroup
@@ -0,0 +1,9 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+
3
+
4
+ let ScheduleGroup;
5
+
6
+ export {
7
+ ScheduleGroup,
8
+ }
9
+ /* No side effect */
@@ -0,0 +1,14 @@
1
+ /** @pulumi/aws/scheduler/schedulegroup
2
+ see: https://www.pulumi.com/registry/packages/aws/api-docs/scheduler/schedulegroup/
3
+ */
4
+ type t = {name: Pulumi.Output.t<string>, arn: Pulumi.Output.t<string>, id: Pulumi.Output.t<string>}
5
+
6
+ type args = {
7
+ /** 1 to 64 characters from letters, digits, `-`, `_` and `.`; generated when absent. */
8
+ name?: Pulumi.Input.t<string>,
9
+ tags?: Pulumi.Input.t<Aws.tags>,
10
+ }
11
+
12
+ @module("@pulumi/aws") @scope("scheduler") @new
13
+ external make: (~name: string, ~args: args, ~opts: Pulumi.CustomResourceOptions.t=?) => t =
14
+ "ScheduleGroup"
@@ -0,0 +1,2 @@
1
+ // Generated by ReScript, PLEASE EDIT WITH CARE
2
+ /* This output is empty. Its source's type definitions, externals and/or unused code got optimized away. */
@@ -158,7 +158,7 @@ describe('listAllItemsConnection', () => {
158
158
  const ctx = makeCtx({ args: {} })
159
159
  const result = request(ctx)
160
160
  expect(result.operation).toBe('Scan')
161
- expect(result.limit).toBe(50)
161
+ expect(result.limit).toBe(51)
162
162
  expect(result.nextToken).toBeNull()
163
163
  expect(result.filter).toBeUndefined()
164
164
  })
@@ -317,10 +317,11 @@ describe('listAllItemsConnection', () => {
317
317
  F.listAllItemsConnection('name', [], [], [], undefined, 'customerId', ['Admin']),
318
318
  )
319
319
 
320
- test('an unfiltered read examines exactly the page it serves', () => {
320
+ // One row past the page: it is what says a next page exists.
321
+ test('an unfiltered read examines one row past the page it serves', () => {
321
322
  const req = request(makeCtx({ args: { first: 25 }, identity: null }))
322
323
  expect(req.filter).toBeUndefined()
323
- expect(req.limit).toBe(25)
324
+ expect(req.limit).toBe(26)
324
325
  })
325
326
 
326
327
  test('a filtered read examines a wider window than the page', () => {
@@ -330,7 +331,7 @@ describe('listAllItemsConnection', () => {
330
331
  })
331
332
 
332
333
  test('a caller asking for more than the window gets what it asked for', () => {
333
- expect(request(makeCtx({ args: { first: 5000 } })).limit).toBe(5000)
334
+ expect(request(makeCtx({ args: { first: 5000 } })).limit).toBe(5001)
334
335
  })
335
336
 
336
337
  // The user-visible bug: one matching row must be one page, not three.
@@ -373,6 +374,34 @@ describe('listAllItemsConnection', () => {
373
374
  ).toBe('W2')
374
375
  })
375
376
 
377
+ // DynamoDB hands back a token whenever a read stops AT its limit, whether or
378
+ // not anything follows. A read of exactly the page therefore claimed a next
379
+ // page after the last full one, and it came back blank — found on a deployed
380
+ // list paged one row at a time. The mock pages a table the way DynamoDB does.
381
+ test('paging to the end serves no blank page, whatever the page size', () => {
382
+ const rows = ['a', 'b', 'c', 'd'].map(id => ({ id }))
383
+ const dynamo = req => {
384
+ const start = req.nextToken === null ? 0 : +req.nextToken
385
+ const items = rows.slice(start, start + req.limit)
386
+ const stoppedAtLimit = items.length === req.limit
387
+ return { items, nextToken: stoppedAtLimit ? String(start + req.limit) : null }
388
+ }
389
+ ;[1, 2, 4].map(first => {
390
+ const pages = []
391
+ let after
392
+ for (let i = 0; i < 10; i++) {
393
+ const args = after === undefined ? { first } : { first, after }
394
+ const r = response(makeCtx({ args, result: dynamo(request(makeCtx({ args, identity: null }))) }))
395
+ pages.push(r.edges.map(e => e.node.id))
396
+ if (!r.pageInfo.hasNextPage) break
397
+ after = r.pageInfo.endCursor
398
+ }
399
+ expect(pages.flat()).toEqual(['a', 'b', 'c', 'd'])
400
+ expect(pages.every(p => p.length > 0)).toBe(true)
401
+ expect(pages).toHaveLength(Math.ceil(4 / first))
402
+ })
403
+ })
404
+
376
405
  // Without this the final row of a window resumes into a window with nothing
377
406
  // left in it, which is the blank page again one step later.
378
407
  test('the last row of a page that closes its window names the next window', () => {
@@ -587,7 +616,7 @@ describe('queryByIndexSortFiltered', () => {
587
616
  expect(result.query.expression).toBe('#ownerId = :ownerId')
588
617
  expect(result.filter).toBeUndefined()
589
618
  expect(result.index).toBe('ownerId')
590
- expect(result.limit).toBe(50)
619
+ expect(result.limit).toBe(51)
591
620
  })
592
621
 
593
622
  test('query includes sortField when present', () => {
@@ -974,11 +1003,11 @@ describe('listAllItemsConnection — owner scoping', () => {
974
1003
 
975
1004
  // The read window exists because a FilterExpression cuts rows AFTER `Limit`.
976
1005
  // A key condition does not, so a scoped caller with no filter of their own
977
- // examines exactly the page they asked for — the arithmetic the window was
978
- // hiding, gone rather than papered over.
979
- test('a scoped caller with no filter examines exactly the page', () => {
1006
+ // examines the page they asked for and the one row that says whether another
1007
+ // follows — the arithmetic the window was hiding, gone rather than papered over.
1008
+ test('a scoped caller with no filter examines only the page, plus one', () => {
980
1009
  const { request } = indexed()
981
- expect(request(makeCtx({ args: { first: 25 }, identity: asUser('cust-a') })).limit).toBe(25)
1010
+ expect(request(makeCtx({ args: { first: 25 }, identity: asUser('cust-a') })).limit).toBe(26)
982
1011
  })
983
1012
 
984
1013
  test("a scoped caller's own filter still widens the window", () => {