@adobe-commerce/aio-toolkit 1.1.1 → 1.2.1

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/dist/index.js CHANGED
@@ -31,6 +31,10 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
31
31
  // src/index.ts
32
32
  var index_exports = {};
33
33
  __export(index_exports, {
34
+ AbdbCollection: () => collection_default,
35
+ AbdbColumn: () => column_default,
36
+ AbdbColumnType: () => AbdbColumnType,
37
+ AbdbRepository: () => abdb_repository_default,
34
38
  AdminUiSdk: () => AdminUiSdk,
35
39
  AdobeAuth: () => adobe_auth_default,
36
40
  AdobeCommerceClient: () => adobe_commerce_client_default,
@@ -2391,6 +2395,674 @@ __name(_FileRepository, "FileRepository");
2391
2395
  var FileRepository = _FileRepository;
2392
2396
  var file_repository_default = FileRepository;
2393
2397
 
2398
+ // src/framework/abdb/collection/index.ts
2399
+ var import_aio_lib_db = require("@adobe/aio-lib-db");
2400
+
2401
+ // src/framework/abdb/column/types.ts
2402
+ var AbdbColumnType = /* @__PURE__ */ ((AbdbColumnType2) => {
2403
+ AbdbColumnType2["STRING"] = "STRING";
2404
+ AbdbColumnType2["NUMBER"] = "NUMBER";
2405
+ AbdbColumnType2["BOOLEAN"] = "BOOLEAN";
2406
+ AbdbColumnType2["DATETIME"] = "DATETIME";
2407
+ return AbdbColumnType2;
2408
+ })(AbdbColumnType || {});
2409
+
2410
+ // src/framework/abdb/column/index.ts
2411
+ var ISO_8601_DATETIME = /^\d{4}-\d{2}-\d{2}([Tt ]\d{2}:\d{2}(:\d{2}(\.\d{1,9})?)?(Z|[+-]\d{2}(:?\d{2})?)?)?$/;
2412
+ var _AbdbColumn = class _AbdbColumn {
2413
+ /**
2414
+ * @param options - Field definition
2415
+ * @throws {Error} When `name` is missing or blank, or `type` is missing / not a member of {@link AbdbColumnType}
2416
+ */
2417
+ constructor(options) {
2418
+ if (!options?.name?.trim()) {
2419
+ throw new Error('AbdbColumn: "name" is required and cannot be empty');
2420
+ }
2421
+ if (!options.type || !Object.values(AbdbColumnType).includes(options.type)) {
2422
+ throw new Error('AbdbColumn: "type" is required');
2423
+ }
2424
+ this._name = options.name.trim();
2425
+ this._type = options.type;
2426
+ this._description = void 0;
2427
+ this._isRequired = options.isRequired ?? false;
2428
+ if (options.description !== void 0) {
2429
+ const trimmed = options.description.trim();
2430
+ if (trimmed.length > 0) {
2431
+ this._description = trimmed;
2432
+ }
2433
+ }
2434
+ Object.freeze(this);
2435
+ }
2436
+ /**
2437
+ * Returns the trimmed field name.
2438
+ */
2439
+ getName() {
2440
+ return this._name;
2441
+ }
2442
+ /**
2443
+ * Returns the expected value type for this column.
2444
+ */
2445
+ getType() {
2446
+ return this._type;
2447
+ }
2448
+ /**
2449
+ * Returns the optional description, if one was provided and non-whitespace.
2450
+ *
2451
+ * @returns Description string, or `undefined` when not set
2452
+ */
2453
+ getDescription() {
2454
+ return this._description;
2455
+ }
2456
+ /**
2457
+ * Returns whether a value is mandatory for {@link AbdbColumn#validate}.
2458
+ *
2459
+ * @returns `true` if constructed with `isRequired: true`; otherwise `false`
2460
+ */
2461
+ getIsRequired() {
2462
+ return this._isRequired;
2463
+ }
2464
+ /**
2465
+ * Serializes this column to a plain object for JSON (config files, APIs, persistence).
2466
+ */
2467
+ toJSON() {
2468
+ const json = {
2469
+ name: this._name,
2470
+ type: this._type
2471
+ };
2472
+ if (this._description !== void 0) {
2473
+ json.description = this._description;
2474
+ }
2475
+ if (this._isRequired) {
2476
+ json.isRequired = true;
2477
+ }
2478
+ return json;
2479
+ }
2480
+ /**
2481
+ * Creates a new column with the same or overridden options. Does not mutate this instance.
2482
+ *
2483
+ * Passing `description: ' '` (blank / whitespace) clears the description on the new column.
2484
+ *
2485
+ * @param overrides - Partial options; omitted keys keep current values
2486
+ * @returns A new {@link AbdbColumn} instance
2487
+ */
2488
+ clone(overrides = {}) {
2489
+ const name = overrides.name ?? this._name;
2490
+ const type = overrides.type ?? this._type;
2491
+ const isRequired = overrides.isRequired ?? this._isRequired;
2492
+ if (overrides.description !== void 0) {
2493
+ return new _AbdbColumn({ name, type, description: overrides.description, isRequired });
2494
+ }
2495
+ if (this._description !== void 0) {
2496
+ return new _AbdbColumn({ name, type, description: this._description, isRequired });
2497
+ }
2498
+ return new _AbdbColumn({ name, type, isRequired });
2499
+ }
2500
+ /**
2501
+ * Validates a payload value against this column's requiredness and {@link AbdbColumnType}.
2502
+ *
2503
+ * **Missing values** (`undefined`, `null`, or empty / whitespace-only strings) are handled first:
2504
+ * if `isRequired` is `false`, validation succeeds; if `true`, throws.
2505
+ *
2506
+ * **Type rules** (when a non-missing value is present):
2507
+ * - `STRING`: primitive string
2508
+ * - `NUMBER`: finite number, or a non-empty string whose trim parses to a finite number via `Number`
2509
+ * - `BOOLEAN`: primitive boolean, or non-empty trimmed string `"true"` / `"false"` (case-insensitive)
2510
+ * - `DATETIME`: valid `Date`, finite numeric timestamp (ms), or non-empty trimmed ISO 8601 date/datetime string
2511
+ *
2512
+ * @param value - Document field or API payload value
2513
+ * @throws {Error} When required and missing, or when the value does not match the column type
2514
+ */
2515
+ validate(value) {
2516
+ if (this._isMissingValue(value)) {
2517
+ if (this._isRequired) {
2518
+ throw new Error(`AbdbColumn: "${this._name}" is required`);
2519
+ }
2520
+ return;
2521
+ }
2522
+ switch (this._type) {
2523
+ case "STRING" /* STRING */:
2524
+ this._validateStringValue(value);
2525
+ return;
2526
+ case "NUMBER" /* NUMBER */:
2527
+ this._validateNumberValue(value);
2528
+ return;
2529
+ case "BOOLEAN" /* BOOLEAN */:
2530
+ this._validateBooleanValue(value);
2531
+ return;
2532
+ case "DATETIME" /* DATETIME */:
2533
+ this._validateDateTimeValue(value);
2534
+ return;
2535
+ default: {
2536
+ const _unreachable = this._type;
2537
+ throw new Error(`AbdbColumn: unhandled type "${_unreachable}"`);
2538
+ }
2539
+ }
2540
+ }
2541
+ /**
2542
+ * Whether `value` counts as absent for requiredness checks (before type validation).
2543
+ *
2544
+ * @returns `true` for `undefined`, `null`, or a string that is empty after trim
2545
+ */
2546
+ _isMissingValue(value) {
2547
+ if (value === void 0 || value === null) {
2548
+ return true;
2549
+ }
2550
+ return typeof value === "string" && value.trim() === "";
2551
+ }
2552
+ /**
2553
+ * @throws {Error} When `value` is not a primitive string
2554
+ */
2555
+ _validateStringValue(value) {
2556
+ if (typeof value !== "string") {
2557
+ throw new Error(`AbdbColumn: "${this._name}" expects string, got ${typeof value}`);
2558
+ }
2559
+ }
2560
+ /**
2561
+ * @throws {Error} When not a finite number or numeric string
2562
+ */
2563
+ _validateNumberValue(value) {
2564
+ if (typeof value === "number") {
2565
+ if (Number.isFinite(value)) {
2566
+ return;
2567
+ }
2568
+ throw new Error(`AbdbColumn: "${this._name}" expects a finite number, got ${String(value)}`);
2569
+ }
2570
+ if (typeof value === "string") {
2571
+ const trimmed = value.trim();
2572
+ const n = Number(trimmed);
2573
+ if (Number.isFinite(n)) {
2574
+ return;
2575
+ }
2576
+ throw new Error(
2577
+ `AbdbColumn: "${this._name}" expects a finite number or numeric string, got ${JSON.stringify(value)}`
2578
+ );
2579
+ }
2580
+ throw new Error(
2581
+ `AbdbColumn: "${this._name}" expects a finite number or numeric string, got ${typeof value}`
2582
+ );
2583
+ }
2584
+ /**
2585
+ * @throws {Error} When not a boolean or `"true"` / `"false"` string
2586
+ */
2587
+ _validateBooleanValue(value) {
2588
+ if (typeof value === "boolean") {
2589
+ return;
2590
+ }
2591
+ if (typeof value === "string") {
2592
+ const t = value.trim().toLowerCase();
2593
+ if (t === "true" || t === "false") {
2594
+ return;
2595
+ }
2596
+ throw new Error(
2597
+ `AbdbColumn: "${this._name}" expects boolean or "true"/"false" string, got ${JSON.stringify(value)}`
2598
+ );
2599
+ }
2600
+ throw new Error(
2601
+ `AbdbColumn: "${this._name}" expects boolean or "true"/"false" string, got ${typeof value}`
2602
+ );
2603
+ }
2604
+ /**
2605
+ * @throws {Error} When not a valid `Date`, finite timestamp, or ISO 8601 string
2606
+ */
2607
+ _validateDateTimeValue(value) {
2608
+ if (value instanceof Date) {
2609
+ if (!Number.isNaN(value.getTime())) {
2610
+ return;
2611
+ }
2612
+ throw new Error(`AbdbColumn: "${this._name}" expects a valid Date`);
2613
+ }
2614
+ if (typeof value === "number") {
2615
+ if (Number.isFinite(value)) {
2616
+ return;
2617
+ }
2618
+ throw new Error(
2619
+ `AbdbColumn: "${this._name}" expects a finite numeric timestamp, got ${value}`
2620
+ );
2621
+ }
2622
+ if (typeof value === "string") {
2623
+ const s = value.trim();
2624
+ if (!ISO_8601_DATETIME.test(s)) {
2625
+ throw new Error(
2626
+ `AbdbColumn: "${this._name}" expects ISO 8601 datetime string, got ${JSON.stringify(value)}`
2627
+ );
2628
+ }
2629
+ const t = Date.parse(s);
2630
+ if (Number.isNaN(t)) {
2631
+ throw new Error(
2632
+ `AbdbColumn: "${this._name}" expects ISO 8601 datetime string, got ${JSON.stringify(value)}`
2633
+ );
2634
+ }
2635
+ return;
2636
+ }
2637
+ throw new Error(
2638
+ `AbdbColumn: "${this._name}" expects Date, finite numeric timestamp, or ISO 8601 datetime string, got ${typeof value}`
2639
+ );
2640
+ }
2641
+ };
2642
+ __name(_AbdbColumn, "AbdbColumn");
2643
+ var AbdbColumn = _AbdbColumn;
2644
+ var column_default = AbdbColumn;
2645
+
2646
+ // src/framework/abdb/collection/index.ts
2647
+ var _AbdbCollection = class _AbdbCollection {
2648
+ /**
2649
+ * Creates a collection and optionally configures it in a callback (e.g. chained {@link addColumn} calls).
2650
+ *
2651
+ * @param name - Raw collection name; trimmed and validated
2652
+ * @param callback - Optional function invoked with `this` for fluent setup
2653
+ * @throws {Error} When `name` is empty, whitespace-only, or contains invalid characters
2654
+ */
2655
+ constructor(name, callback) {
2656
+ this._name = this._validateCollectionName(name);
2657
+ this._columns = /* @__PURE__ */ new Map();
2658
+ if (callback) {
2659
+ callback(this);
2660
+ }
2661
+ }
2662
+ /**
2663
+ * Returns this collection's name.
2664
+ */
2665
+ getName() {
2666
+ return this._name;
2667
+ }
2668
+ addColumn(name, type, descriptionOrOptions, isRequired) {
2669
+ const trimmed = this._validateColumnName(name);
2670
+ if (this._columns.has(trimmed)) {
2671
+ throw new Error(`AbdbCollection: duplicate column name "${trimmed}"`);
2672
+ }
2673
+ const columnOptions = { name: trimmed, type };
2674
+ if (typeof descriptionOrOptions === "string") {
2675
+ columnOptions.description = descriptionOrOptions;
2676
+ if (isRequired !== void 0) {
2677
+ columnOptions.isRequired = isRequired;
2678
+ }
2679
+ } else if (descriptionOrOptions !== void 0) {
2680
+ if (descriptionOrOptions.description !== void 0) {
2681
+ columnOptions.description = descriptionOrOptions.description;
2682
+ }
2683
+ if (descriptionOrOptions.isRequired !== void 0) {
2684
+ columnOptions.isRequired = descriptionOrOptions.isRequired;
2685
+ }
2686
+ }
2687
+ this._columns.set(trimmed, new column_default(columnOptions));
2688
+ return this;
2689
+ }
2690
+ /**
2691
+ * Returns a defensive copy of columns in insertion order.
2692
+ */
2693
+ getColumns() {
2694
+ return Array.from(this._columns.values());
2695
+ }
2696
+ /**
2697
+ * Returns the column registered under `name`, or `undefined` if no such column exists.
2698
+ *
2699
+ * @param name - Column name to look up (exact match after trimming)
2700
+ * @returns The {@link AbdbColumn} instance, or `undefined`
2701
+ */
2702
+ getColumn(name) {
2703
+ return this._columns.get(name.trim());
2704
+ }
2705
+ /**
2706
+ * Returns `true` when a column with the given name has been registered.
2707
+ *
2708
+ * @param name - Column name to check (exact match after trimming)
2709
+ */
2710
+ hasColumn(name) {
2711
+ return this._columns.has(name.trim());
2712
+ }
2713
+ /**
2714
+ * Validates a document-style object against this collection's columns. Throws on the **first** failing
2715
+ * column. Use {@link validateAll} when you need all errors reported at once.
2716
+ *
2717
+ * Missing keys are read as `undefined`, so a **required** column whose key is absent fails validation.
2718
+ * Extra keys not matching any column name are ignored.
2719
+ *
2720
+ * @param record - Key/value payload whose keys are column names
2721
+ * @throws {Error} When any column validation fails (requiredness or type)
2722
+ */
2723
+ validate(record) {
2724
+ for (const col of this._columns.values()) {
2725
+ col.validate(record[col.getName()]);
2726
+ }
2727
+ }
2728
+ /**
2729
+ * Validates a document-style object against **all** columns and collects every error instead of
2730
+ * stopping at the first failure. Useful at API boundaries where reporting all problems at once
2731
+ * gives a better developer or end-user experience.
2732
+ *
2733
+ * Returns an empty array when the record is fully valid.
2734
+ *
2735
+ * @param record - Key/value payload whose keys are column names
2736
+ * @returns Array of validation error messages, one per failing column (insertion order)
2737
+ *
2738
+ * @example
2739
+ * ```typescript
2740
+ * const errors = orders.validateAll(payload);
2741
+ * if (errors.length > 0) {
2742
+ * return { status: 400, body: { errors } };
2743
+ * }
2744
+ * ```
2745
+ */
2746
+ validateAll(record) {
2747
+ const errors = [];
2748
+ for (const col of this._columns.values()) {
2749
+ try {
2750
+ col.validate(record[col.getName()]);
2751
+ } catch (e) {
2752
+ errors.push(e instanceof Error ? e.message : String(e));
2753
+ }
2754
+ }
2755
+ return errors;
2756
+ }
2757
+ /**
2758
+ * Connects to the database (via `@adobe/aio-lib-db`), runs `callback` with the selected DB **collection**
2759
+ * handle and the **client**, then closes the client in a `finally` block.
2760
+ *
2761
+ * **Errors:** `DbError` instances are rethrown as `AbdbCollection: database error: …`;
2762
+ * any other value is wrapped as `AbdbCollection: unexpected error: …`.
2763
+ *
2764
+ * @param callback - Receives `(collection, client)` after connect
2765
+ * @param token - IMS access token for `initDb`
2766
+ * @param region - Data region (default: `'amer'`)
2767
+ * @returns Promise resolving to the callback's return value
2768
+ * @throws {Error} On init, connect, `DbError`, or callback failure
2769
+ */
2770
+ async run(callback, token, region = "amer") {
2771
+ let client;
2772
+ try {
2773
+ const db = await (0, import_aio_lib_db.init)({ token, region });
2774
+ client = await db.connect();
2775
+ const collection = await client.collection(this._name);
2776
+ return await callback(collection, client);
2777
+ } catch (error) {
2778
+ if (error instanceof import_aio_lib_db.DbError) {
2779
+ throw new Error(`AbdbCollection: database error: ${error.message}`);
2780
+ }
2781
+ const detail = error instanceof Error ? error.message : String(error);
2782
+ throw new Error(`AbdbCollection: unexpected error: ${detail}`);
2783
+ } finally {
2784
+ await client?.close();
2785
+ }
2786
+ }
2787
+ /**
2788
+ * Validates and normalizes the collection identifier used at construction.
2789
+ *
2790
+ * @throws {Error} If the name is empty or not `[a-zA-Z0-9_]`
2791
+ */
2792
+ _validateCollectionName(name) {
2793
+ return this._validateIdentifier(
2794
+ name,
2795
+ 'AbdbCollection: "name" is required and cannot be empty',
2796
+ "AbdbCollection: name must contain only alphanumeric characters and underscores"
2797
+ );
2798
+ }
2799
+ /**
2800
+ * Validates and normalizes a column name before {@link addColumn} stores it.
2801
+ *
2802
+ * @throws {Error} If the name is empty or not `[a-zA-Z0-9_]`
2803
+ */
2804
+ _validateColumnName(name) {
2805
+ return this._validateIdentifier(
2806
+ name,
2807
+ 'AbdbCollection: column "name" is required and cannot be empty',
2808
+ "AbdbCollection: column name must contain only alphanumeric characters and underscores"
2809
+ );
2810
+ }
2811
+ /**
2812
+ * Shared validation for collection and column identifiers.
2813
+ *
2814
+ * @param raw - Unvalidated string
2815
+ * @param emptyMessage - Thrown when `raw` is missing or whitespace-only
2816
+ * @param invalidMessage - Thrown when the trimmed value is not alphanumeric with underscores
2817
+ * @returns The trimmed identifier
2818
+ */
2819
+ _validateIdentifier(raw, emptyMessage, invalidMessage) {
2820
+ if (!raw || raw.trim() === "") {
2821
+ throw new Error(emptyMessage);
2822
+ }
2823
+ const trimmed = raw.trim();
2824
+ if (!/^[a-zA-Z0-9_]+$/.test(trimmed)) {
2825
+ throw new Error(invalidMessage);
2826
+ }
2827
+ return trimmed;
2828
+ }
2829
+ };
2830
+ __name(_AbdbCollection, "AbdbCollection");
2831
+ var AbdbCollection = _AbdbCollection;
2832
+ var collection_default = AbdbCollection;
2833
+
2834
+ // src/framework/repository/abdb-repository/index.ts
2835
+ var _AbdbRepository = class _AbdbRepository {
2836
+ /**
2837
+ * @param collection - Schema and DB-connection wrapper for the target collection
2838
+ * @param token - IMS access token forwarded to every `collection.run()` call
2839
+ * @param region - Data region forwarded to every `collection.run()` call (default: `'amer'`)
2840
+ * @throws {Error} When `collection` is not an {@link AbdbCollection} instance, or `token` is missing
2841
+ */
2842
+ constructor(collection, token, region = "amer") {
2843
+ if (!(collection instanceof collection_default)) {
2844
+ throw new Error('AbdbRepository: "collection" must be an AbdbCollection instance');
2845
+ }
2846
+ if (!token || typeof token !== "string" || !token.trim()) {
2847
+ throw new Error('AbdbRepository: "token" is required');
2848
+ }
2849
+ if (!collection.hasColumn("_created_at")) {
2850
+ collection.addColumn("_created_at", "DATETIME" /* DATETIME */, "Record creation timestamp");
2851
+ }
2852
+ if (!collection.hasColumn("_updated_at")) {
2853
+ collection.addColumn("_updated_at", "DATETIME" /* DATETIME */, "Record last-updated timestamp");
2854
+ }
2855
+ this._collection = collection;
2856
+ this._token = token;
2857
+ this._region = region;
2858
+ }
2859
+ /**
2860
+ * Returns the name of the underlying collection.
2861
+ */
2862
+ getName() {
2863
+ return this._collection.getName();
2864
+ }
2865
+ /**
2866
+ * Returns the underlying {@link AbdbCollection} instance.
2867
+ */
2868
+ getCollection() {
2869
+ return this._collection;
2870
+ }
2871
+ /**
2872
+ * Returns all documents matching `filter` as an array.
2873
+ * Passing no filter (or an empty object) returns every document in the collection.
2874
+ *
2875
+ * @param filter - Optional query filter (default: `{}`)
2876
+ * @returns Array of matched documents (may be empty)
2877
+ */
2878
+ async find(filter = {}) {
2879
+ return this._collection.run(
2880
+ async (collection) => {
2881
+ return collection.find(filter).toArray();
2882
+ },
2883
+ this._token,
2884
+ this._region
2885
+ );
2886
+ }
2887
+ /**
2888
+ * Returns the first document matching `filter`, or `null` if none found.
2889
+ *
2890
+ * @param filter - Query filter (e.g. `{ _id: 'abc' }` or `{ email: 'a@b.com' }`)
2891
+ * @returns The matched document, or `null`
2892
+ */
2893
+ async findOne(filter) {
2894
+ return this._collection.run(
2895
+ async (collection) => {
2896
+ return collection.findOne(filter);
2897
+ },
2898
+ this._token,
2899
+ this._region
2900
+ );
2901
+ }
2902
+ /**
2903
+ * Returns `true` when a document with the given `_id` exists in the collection.
2904
+ * Uses `countDocuments` rather than fetching the full document for efficiency.
2905
+ *
2906
+ * @param id - The `_id` to check
2907
+ * @returns `true` if the document exists, `false` otherwise
2908
+ */
2909
+ async exists(id) {
2910
+ if (!id) return false;
2911
+ const n = await this._collection.run(
2912
+ (collection) => {
2913
+ return collection.countDocuments({ _id: id });
2914
+ },
2915
+ this._token,
2916
+ this._region
2917
+ );
2918
+ return n > 0;
2919
+ }
2920
+ /**
2921
+ * Returns the number of documents in the collection matching `filter`.
2922
+ * Passing no filter (or an empty object) counts every document.
2923
+ *
2924
+ * @param filter - Optional query filter (default: `{}`)
2925
+ * @returns Total number of matching documents
2926
+ */
2927
+ async count(filter = {}) {
2928
+ return this._collection.run(
2929
+ (collection) => {
2930
+ return collection.countDocuments(filter);
2931
+ },
2932
+ this._token,
2933
+ this._region
2934
+ );
2935
+ }
2936
+ /**
2937
+ * Inserts or updates a document.
2938
+ *
2939
+ * - **Insert** (no `id`): stamps both `_created_at` and `_updated_at`, runs full schema
2940
+ * validation, then calls `insertOne`. Returns the new document's `_id`.
2941
+ * - **Update** (with `id`): stamps `_updated_at`, runs **partial** validation (only columns
2942
+ * present in the payload are checked — required fields that are absent are not enforced
2943
+ * because they already exist in the stored document), then calls `updateOne` with
2944
+ * `upsert: true`. Returns the same `id` that was passed in.
2945
+ *
2946
+ * @param payload - Document fields to write
2947
+ * @param id - When provided, updates the document with this `_id`; otherwise inserts
2948
+ * @returns The `_id` of the inserted or updated document
2949
+ */
2950
+ async save(payload = {}, id = "") {
2951
+ const now = (/* @__PURE__ */ new Date()).toISOString();
2952
+ if (id) {
2953
+ const updatePayload = { ...payload, _updated_at: now };
2954
+ this._validatePartial(updatePayload);
2955
+ await this._collection.run(
2956
+ async (collection) => {
2957
+ await collection.updateOne({ _id: id }, updatePayload, { upsert: true });
2958
+ },
2959
+ this._token,
2960
+ this._region
2961
+ );
2962
+ return id;
2963
+ }
2964
+ const insertPayload = { ...payload, _created_at: now, _updated_at: now };
2965
+ this._collection.validate(insertPayload);
2966
+ return this._collection.run(
2967
+ async (collection) => {
2968
+ return collection.insertOne(insertPayload);
2969
+ },
2970
+ this._token,
2971
+ this._region
2972
+ );
2973
+ }
2974
+ /**
2975
+ * Deletes the document with the given `_id`. No-ops silently when `id` is empty.
2976
+ *
2977
+ * @param id - The `_id` of the document to delete
2978
+ */
2979
+ async delete(id = "") {
2980
+ if (!id) return;
2981
+ await this._collection.run(
2982
+ async (collection) => {
2983
+ await collection.deleteOne({ _id: id });
2984
+ },
2985
+ this._token,
2986
+ this._region
2987
+ );
2988
+ }
2989
+ /**
2990
+ * Inserts multiple documents in a single `insertMany` call.
2991
+ * Each payload is stamped with `_created_at` / `_updated_at` and validated
2992
+ * against the full collection schema before the bulk write is sent to the DB.
2993
+ *
2994
+ * @param payloads - Array of document payloads to insert
2995
+ * @returns Array of inserted `_id` values in insertion order
2996
+ */
2997
+ async insertAll(payloads) {
2998
+ const now = (/* @__PURE__ */ new Date()).toISOString();
2999
+ const insertPayloads = payloads.map((payload) => {
3000
+ const doc = { ...payload, _created_at: now, _updated_at: now };
3001
+ this._collection.validate(doc);
3002
+ return doc;
3003
+ });
3004
+ return this._collection.run(
3005
+ async (collection) => {
3006
+ return collection.insertMany(insertPayloads);
3007
+ },
3008
+ this._token,
3009
+ this._region
3010
+ );
3011
+ }
3012
+ /**
3013
+ * Applies a partial update to all documents matching `filter` using a single `updateMany` call.
3014
+ * Stamps `_updated_at` on every matched document and runs partial validation on the payload
3015
+ * (only the columns present in the payload are checked).
3016
+ *
3017
+ * @param payload - Fields to update on every matched document
3018
+ * @param filter - Query condition selecting which documents to update (default: `{}` — all documents)
3019
+ */
3020
+ async updateAll(payload, filter = {}) {
3021
+ const updatePayload = { ...payload, _updated_at: (/* @__PURE__ */ new Date()).toISOString() };
3022
+ this._validatePartial(updatePayload);
3023
+ await this._collection.run(
3024
+ async (collection) => {
3025
+ await collection.updateMany(filter, updatePayload);
3026
+ },
3027
+ this._token,
3028
+ this._region
3029
+ );
3030
+ }
3031
+ /**
3032
+ * Deletes all documents matching `filter` using a single `deleteMany` call.
3033
+ * Passing no filter (or an empty object) deletes every document in the collection.
3034
+ *
3035
+ * @param filter - Optional query filter (default: `{}`)
3036
+ */
3037
+ async deleteAll(filter = {}) {
3038
+ await this._collection.run(
3039
+ async (collection) => {
3040
+ await collection.deleteMany(filter);
3041
+ },
3042
+ this._token,
3043
+ this._region
3044
+ );
3045
+ }
3046
+ /**
3047
+ * Validates only the columns whose keys are present in `record`.
3048
+ * Required columns that are absent from the payload are intentionally skipped —
3049
+ * they already exist in the stored document and are not being changed.
3050
+ *
3051
+ * Used by the update path of {@link save} and {@link updateAll}.
3052
+ */
3053
+ _validatePartial(record) {
3054
+ for (const col of this._collection.getColumns()) {
3055
+ const key = col.getName();
3056
+ if (key in record) {
3057
+ col.validate(record[key]);
3058
+ }
3059
+ }
3060
+ }
3061
+ };
3062
+ __name(_AbdbRepository, "AbdbRepository");
3063
+ var AbdbRepository = _AbdbRepository;
3064
+ var abdb_repository_default = AbdbRepository;
3065
+
2394
3066
  // src/framework/publish-event/index.ts
2395
3067
  var import_aio_sdk3 = require("@adobe/aio-sdk");
2396
3068
  var import_cloudevents = require("cloudevents");
@@ -8852,6 +9524,10 @@ __name(_AdminUiSdk, "AdminUiSdk");
8852
9524
  var AdminUiSdk = _AdminUiSdk;
8853
9525
  // Annotate the CommonJS export names for ESM import in node:
8854
9526
  0 && (module.exports = {
9527
+ AbdbCollection,
9528
+ AbdbColumn,
9529
+ AbdbColumnType,
9530
+ AbdbRepository,
8855
9531
  AdminUiSdk,
8856
9532
  AdobeAuth,
8857
9533
  AdobeCommerceClient,