@reventlessdev/rescript-pulumi-aws 3.0.0-alpha.2 → 3.0.0-alpha.4

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.
@@ -388,25 +388,66 @@ export function request(ctx) {
388
388
  `;
389
389
  }
390
390
 
391
- let indexConnectionResponseCode = `
392
- export function response(ctx) {
393
- if (ctx.error) util.error(ctx.error.message, ctx.error.type);
394
- const items = ctx.result?.items ?? [];
395
- const next = ctx.result?.nextToken ?? null;
396
- const edges = items.map((item, i) => ({
391
+ function pageWindowBudget(filtered) {
392
+ return `(` + filtered + ` ? (_first > 1000 ? _first : 1000) : _first + _from)`;
393
+ }
394
+
395
+ function cursorDecode(args) {
396
+ return `
397
+ let _window = null;
398
+ let _from = 0;
399
+ let _cursorPath = null;
400
+ if (` + args + `.after != null && ` + args + `.after !== '') {
401
+ const _c = JSON.parse(util.base64Decode(` + args + `.after));
402
+ _window = (_c.t !== undefined ? _c.t : _c.token) ?? null;
403
+ _from = _c.n !== undefined ? _c.n + 1 : 0;
404
+ _cursorPath = _c.p ?? 's';
405
+ }`;
406
+ }
407
+
408
+ let cursorPathGuard = `
409
+ if (_cursorPath !== null && _cursorPath !== (_exempt ? 's' : 'q')) {
410
+ util.error('This cursor belongs to a different read of this list; restart from the first page.', 'CursorPathMismatch');
411
+ }`;
412
+
413
+ function connectionPageResponse(pathExpr) {
414
+ let tag = pathExpr !== undefined ? `, p: ` + pathExpr : "";
415
+ return `
416
+ const _first = ctx.args.first ?? 50;
417
+ const _rest = items.slice(_from);
418
+ const _page = _rest.slice(0, _first);
419
+ const _more = _rest.length > _first;
420
+ const _next = ctx.result?.nextToken ?? null;
421
+ const _lastIndex = _page.length - 1;
422
+ // A row's cursor names its own position. The last row of a page that closes its
423
+ // window is the exception — no position follows it there, so it names the next
424
+ // window, or resuming from it answers blank.
425
+ const edges = _page.map((item, i) => ({
397
426
  node: item,
398
- cursor: util.base64Encode(JSON.stringify({ token: next, index: i })),
427
+ cursor: util.base64Encode(JSON.stringify(
428
+ (!_more && _next && i === _lastIndex)
429
+ ? { t: _next, n: -1` + tag + ` }
430
+ : { t: _window, n: _from + i` + tag + ` }
431
+ )),
399
432
  }));
400
- const boundary = next ? util.base64Encode(JSON.stringify({ token: next, index: -1 })) : null;
433
+ // A window the filter emptied leaves no row to cut a cursor from; the token is
434
+ // the window's, so a client can step past it rather than restart.
435
+ const _boundary = _next ? util.base64Encode(JSON.stringify({ t: _next, n: -1` + tag + ` })) : null;
401
436
  return {
402
437
  edges,
403
438
  pageInfo: {
404
- hasNextPage: !!next,
439
+ hasNextPage: _more || !!_next,
405
440
  hasPreviousPage: !!ctx.args.after,
406
- startCursor: edges.length > 0 ? edges[0].cursor : boundary,
407
- endCursor: edges.length > 0 ? edges[edges.length - 1].cursor : boundary,
441
+ startCursor: edges.length > 0 ? edges[0].cursor : _boundary,
442
+ endCursor: edges.length > 0 ? edges[edges.length - 1].cursor : _boundary,
408
443
  },
409
- };
444
+ };`;
445
+ }
446
+
447
+ let indexConnectionResponseCode = `
448
+ export function response(ctx) {
449
+ if (ctx.error) util.error(ctx.error.message, ctx.error.type);
450
+ const items = ctx.result?.items ?? [];` + cursorDecode("ctx.args") + connectionPageResponse(undefined) + `
410
451
  }`;
411
452
 
412
453
  let indexBackwardPagingGuard = `
@@ -414,12 +455,8 @@ let indexBackwardPagingGuard = `
414
455
  util.error('Backward pagination (last/before) is not supported on by-index connections; use first/after.', 'UnsupportedPagination');
415
456
  }`;
416
457
 
417
- let indexCursorPreamble = `
418
- let after = null;
419
- if (args.after != null && args.after !== '') {
420
- const parsed = JSON.parse(util.base64Decode(args.after));
421
- after = parsed.token ?? null;
422
- }`;
458
+ let indexCursorPreamble = cursorDecode("args") + `
459
+ const _first = args.first ?? 50;`;
423
460
 
424
461
  let indexReservedArgs = `key === 'first' || key === 'after' || key === 'last' || key === 'before' || key === 'includeRetired' || key === 'limit' || key === 'nextToken' || key === 'forward'`;
425
462
 
@@ -458,8 +495,8 @@ export function request(ctx) {
458
495
  operation: 'Query',
459
496
  query,
460
497
  index: '` + index + `',
461
- limit: (args.first ?? 50),
462
- nextToken: after,
498
+ limit: ` + (`(` + "expression" + ` ? (_first > 1000 ? _first : 1000) : _first + _from)`) + `,
499
+ nextToken: _window,
463
500
  scanIndexForward: (args.forward ?? true)
464
501
  };
465
502
  if (expression) {
@@ -515,8 +552,8 @@ export function request(ctx) {
515
552
  operation: 'Query',
516
553
  query,
517
554
  index: '` + index + `',
518
- limit: (args.first ?? 50),
519
- nextToken: after,
555
+ limit: ` + (`(` + "expression" + ` ? (_first > 1000 ? _first : 1000) : _first + _from)`) + `,
556
+ nextToken: _window,
520
557
  scanIndexForward: (args.forward ?? true)
521
558
  };
522
559
  if (expression) {
@@ -539,18 +576,17 @@ export function request(ctx) {
539
576
  ` + resultResponseCode + `
540
577
  `;
541
578
 
542
- function listAllItemsConnection(labelField, filterFieldsOpt, rangeFieldsOpt, sortFieldsOpt, requireAttribute, ownerField, elevatedGroupsOpt, retiredField, retiredValues) {
579
+ function listAllItemsConnection(labelField, filterFieldsOpt, rangeFieldsOpt, sortFieldsOpt, requireAttribute, ownerField, elevatedGroupsOpt, retiredField, retiredValues, ownerIndex, ownerIndexSortField) {
543
580
  let filterFields = filterFieldsOpt !== undefined ? filterFieldsOpt : [];
544
581
  let rangeFields = rangeFieldsOpt !== undefined ? rangeFieldsOpt : [];
545
582
  let sortFields = sortFieldsOpt !== undefined ? sortFieldsOpt : [];
546
583
  let elevatedGroups = elevatedGroupsOpt !== undefined ? elevatedGroupsOpt : [];
584
+ let ownerIndex$1 = Stdlib_Option.isSome(ownerField) ? ownerIndex : undefined;
547
585
  let requireAttributeClause = requireAttribute !== undefined ? `
548
586
  names['#` + requireAttribute + `'] = '` + requireAttribute + `';
549
587
  parts.push('attribute_exists(#` + requireAttribute + `)');` : "";
550
- let ownerClause;
551
- if (ownerField !== undefined) {
552
- let elevatedLiteral = elevatedGroups.map(g => `'` + g + `'`).join(", ");
553
- ownerClause = `
588
+ let elevatedLiteral = elevatedGroups.map(g => `'` + g + `'`).join(", ");
589
+ let ownerIdentityPreamble = `
554
590
  // ── owner scoping (generated) ──
555
591
  // Not read from ctx.args: a predicate deciding what the caller may see must
556
592
  // arrive on a channel the caller cannot name, and this field is usually absent
@@ -561,15 +597,15 @@ function listAllItemsConnection(labelField, filterFieldsOpt, rangeFieldsOpt, sor
561
597
  const _elevated = [` + elevatedLiteral + `];
562
598
  // No identity at all, or an identity with no \`sub\`, is the IAM service caller
563
599
  // the API also accepts — inside the trust boundary, and exempt.
564
- const _exempt = _sub == null || _groups.some(g => _elevated.indexOf(g) >= 0);
600
+ const _exempt = _sub == null || _groups.some(g => _elevated.indexOf(g) >= 0);`;
601
+ let ownerClause = ownerField !== undefined ? (
602
+ ownerIndex$1 !== undefined ? ownerIdentityPreamble : ownerIdentityPreamble + `
565
603
  if (!_exempt) {
566
604
  names['#owner'] = '` + ownerField + `';
567
605
  values[':owner'] = util.dynamodb.toDynamoDB(_sub);
568
606
  parts.push('#owner = :owner');
569
- }`;
570
- } else {
571
- ownerClause = "";
572
- }
607
+ }`
608
+ ) : "";
573
609
  let retiredClause;
574
610
  if (retiredField !== undefined) {
575
611
  let elevatedLiteral$1 = elevatedGroups.map(g => `'` + g + `'`).join(", ");
@@ -621,13 +657,14 @@ function listAllItemsConnection(labelField, filterFieldsOpt, rangeFieldsOpt, sor
621
657
  parts.push('#` + f + ` <= :` + f + `To');
622
658
  }`).join("");
623
659
  let sortFieldsLiteral = sortFields.map(f => `'` + f + `'`).join(", ");
660
+ let sortGuard = ownerIndex$1 !== undefined && ownerIndexSortField !== undefined ? "!_indexOrdered && " : "";
624
661
  let sortBlock = sortFields.length === 0 ? "" : `
625
662
  // Per-page sort (Scan returns items in indeterminate order; ScanIndexForward
626
663
  // does not apply to Scan). Global ordering across pages requires v1.5 index
627
664
  // promotion; @scanSort is per-page even then.
628
665
  const orderBy = ctx.args.orderBy;
629
666
  const sortFields = [` + sortFieldsLiteral + `];
630
- if (orderBy && orderBy.field && sortFields.indexOf(orderBy.field) >= 0) {
667
+ if (` + sortGuard + `orderBy && orderBy.field && sortFields.indexOf(orderBy.field) >= 0) {
631
668
  const field = orderBy.field;
632
669
  const nulls = items.filter(it => it[field] === null || it[field] === undefined);
633
670
  const nonNulls = items.filter(it => it[field] !== null && it[field] !== undefined);
@@ -642,6 +679,44 @@ function listAllItemsConnection(labelField, filterFieldsOpt, rangeFieldsOpt, sor
642
679
  if (orderBy.direction === 'DESC') encoded.reverse();
643
680
  items = encoded.map(e => JSON.parse(e.split('\\x01')[1])).concat(nulls);
644
681
  }`;
682
+ let indexOrderedExpr = ownerIndex$1 !== undefined && ownerIndexSortField !== undefined ? `!_exempt && !!(ctx.args.orderBy && ctx.args.orderBy.field === '` + ownerIndexSortField + `')` : "false";
683
+ let requestOperation = ownerField !== undefined ? (
684
+ ownerIndex$1 !== undefined ? `
685
+ const _indexOrdered = ` + indexOrderedExpr + `;
686
+ const req = _exempt
687
+ ? {
688
+ operation: 'Scan',
689
+ limit: ` + (`(` + "parts.length > 0" + ` ? (_first > 1000 ? _first : 1000) : _first + _from)`) + `,
690
+ nextToken: _window,
691
+ }
692
+ : {
693
+ operation: 'Query',
694
+ index: '` + ownerIndex$1 + `',
695
+ query: {
696
+ expression: '#owner = :owner',
697
+ expressionNames: { '#owner': '` + ownerField + `' },
698
+ expressionValues: { ':owner': util.dynamodb.toDynamoDB(_sub) },
699
+ },
700
+ limit: ` + (`(` + "parts.length > 0" + ` ? (_first > 1000 ? _first : 1000) : _first + _from)`) + `,
701
+ nextToken: _window,
702
+ scanIndexForward: !(_indexOrdered && ctx.args.orderBy.direction === 'DESC'),
703
+ };` : `
704
+ const req = {
705
+ operation: 'Scan',
706
+ limit: ` + (`(` + "parts.length > 0" + ` ? (_first > 1000 ? _first : 1000) : _first + _from)`) + `,
707
+ nextToken: _window,
708
+ };`
709
+ ) : `
710
+ const req = {
711
+ operation: 'Scan',
712
+ limit: ` + (`(` + "parts.length > 0" + ` ? (_first > 1000 ? _first : 1000) : _first + _from)`) + `,
713
+ nextToken: _window,
714
+ };`;
715
+ let requestPathGuard = Stdlib_Option.isSome(ownerIndex$1) ? cursorPathGuard : "";
716
+ let responsePathPreamble = ownerIndex$1 !== undefined ? ownerIdentityPreamble + `
717
+ const _path = _exempt ? 's' : 'q';
718
+ const _indexOrdered = ` + indexOrderedExpr + `;` : "";
719
+ let pageResponse = ownerIndex$1 !== undefined ? connectionPageResponse("_path") : connectionPageResponse(undefined);
645
720
  return importUtil + `
646
721
  export function request(ctx) {
647
722
  // Scan cannot page backward (ScanIndexForward is Query-only). Fail loud rather than
@@ -673,18 +748,8 @@ export function request(ctx) {
673
748
  });
674
749
  parts.push('#id IN (' + placeholders.join(', ') + ')');
675
750
  }` + filterClauses + rangeClauses + requireAttributeClause + ownerClause + retiredClause + `
676
- // The cursor is base64(JSON({ token, index })); decode the after arg back to the raw
677
- // DynamoDB continuation token the response side emitted (Fix 1 round-trip).
678
- let after = null;
679
- if (ctx.args.after != null && ctx.args.after !== '') {
680
- const parsed = JSON.parse(util.base64Decode(ctx.args.after));
681
- after = parsed.token ?? null;
682
- }
683
- const req = {
684
- operation: 'Scan',
685
- limit: (ctx.args.first ?? 50),
686
- nextToken: after,
687
- };
751
+ ` + cursorDecode("ctx.args") + requestPathGuard + `
752
+ const _first = ctx.args.first ?? 50;` + requestOperation + `
688
753
  if (parts.length > 0) {
689
754
  req.filter = {
690
755
  expression: parts.join(' AND '),
@@ -696,30 +761,7 @@ export function request(ctx) {
696
761
  }
697
762
  export function response(ctx) {
698
763
  if (ctx.error) util.error(ctx.error.message, ctx.error.type);
699
- let items = ctx.result?.items ?? [];` + sortBlock + `
700
- // One Scan continuation token per page; encode it (with the item's page index for a
701
- // unique, opaque Relay cursor). The request side decodes .token back to the raw
702
- // DynamoDB nextToken (Fix 1).
703
- const next = ctx.result?.nextToken ?? null;
704
- const edges = items.map((item, i) => ({
705
- node: item,
706
- cursor: util.base64Encode(JSON.stringify({ token: next, index: i })),
707
- }));
708
- // A filtered/1MB-capped page can be empty or short while next is still set (limit
709
- // caps rows scanned, not returned). The token is page-level, so synthesise a
710
- // boundary cursor from it alone so a client can resume past a fully-filtered-out
711
- // page instead of restarting from page 1 (Fix 3). The request only reads .token,
712
- // so index -1 is inert on resume.
713
- const boundary = next ? util.base64Encode(JSON.stringify({ token: next, index: -1 })) : null;
714
- return {
715
- edges,
716
- pageInfo: {
717
- hasNextPage: !!next,
718
- hasPreviousPage: !!ctx.args.after,
719
- startCursor: edges.length > 0 ? edges[0].cursor : boundary,
720
- endCursor: edges.length > 0 ? edges[edges.length - 1].cursor : boundary,
721
- },
722
- };
764
+ let items = ctx.result?.items ?? [];` + responsePathPreamble + sortBlock + cursorDecode("ctx.args") + pageResponse + `
723
765
  }
724
766
  `;
725
767
  }
@@ -948,12 +990,9 @@ export function request(ctx) {
948
990
  }
949
991
  export function response(ctx) {
950
992
  if (ctx.error) util.error(ctx.error.message, ctx.error.type);
951
- // BatchGetItem returns null in the result array for keys that don't exist
952
- // in the table, preserving index correspondence with the input. The SDL
953
- // returns this field as \`[T!]!\` (non-null element list), so any single
954
- // missing id makes the entire field fail with "Cannot return null for
955
- // non-nullable type" and the caller sees data=null. Filter the nulls so
956
- // the field returns just the items that were found.
993
+ // BatchGetItem returns null for keys that don't exist, preserving index
994
+ // correspondence. The SDL declares \`[T!]!\`, so one missing id would null the
995
+ // whole field drop them and return what was found.
957
996
  // The owner and retirement guards the list pushes into a FilterExpression,
958
997
  // applied after the read because BatchGetItem has none to push into. A row the
959
998
  // caller does not own is dropped rather than refused, for the reason the
@@ -1249,6 +1288,10 @@ export {
1249
1288
  queryByIndex,
1250
1289
  queryByIndexDeletable,
1251
1290
  queryByIndexSort,
1291
+ pageWindowBudget,
1292
+ cursorDecode,
1293
+ cursorPathGuard,
1294
+ connectionPageResponse,
1252
1295
  indexConnectionResponseCode,
1253
1296
  indexBackwardPagingGuard,
1254
1297
  indexCursorPreamble,
@@ -1285,4 +1328,4 @@ export {
1285
1328
  resolveIdsResult,
1286
1329
  $$null,
1287
1330
  }
1288
- /* No side effect */
1331
+ /* indexConnectionResponseCode Not a pure module */
@@ -66,3 +66,26 @@ type args = {
66
66
  @module("@pulumi/aws") @scope("dynamodb") @new
67
67
  external make: (~name: string, ~args: args, ~opts: Pulumi.CustomResourceOptions.t=?) => table =
68
68
  "Table"
69
+
70
+ /** Look up a table this stack does not own.
71
+
72
+ In a submodule because the lookup result's fields are a subset of [t]'s: at
73
+ file level the later record would win inference for `{id, name, arn}` and
74
+ silently retype every such destructuring of a real table.
75
+
76
+ The lookup fails the deploy when the table does not exist, which is the point —
77
+ a name that resolves to nothing would otherwise reach a Lambda's environment
78
+ and surface at sign-in. */
79
+ module Get = {
80
+ type args = {name: string}
81
+
82
+ type result = {
83
+ arn: string,
84
+ id: string,
85
+ name: string,
86
+ }
87
+
88
+ @module("@pulumi/aws") @scope("dynamodb") @val
89
+ external output: (~args: args, ~opts: Pulumi.InvokeOptions.t=?) => Pulumi.Output.t<result> =
90
+ "getTableOutput"
91
+ }
@@ -1,2 +1,9 @@
1
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. */
2
+
3
+
4
+ let Get = {};
5
+
6
+ export {
7
+ Get,
8
+ }
9
+ /* No side effect */