Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .forceignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
**/jsconfig.json

**/.eslintrc.json

**/__tests__/**
2 changes: 1 addition & 1 deletion .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ Closes #

- [ ] My code follows the style guidelines of this project
- [ ] I have performed a self-review of my own code
- [ ] I have commented my code where necessary
- [ ] Names explain my code, and any comment I added is one nothing else could carry ([code style](../dev/docs/code-style.md))
- [ ] I have made corresponding changes to the documentation
- [ ] My changes generate no new warnings or errors
- [ ] I have added tests that prove my fix is effective or that my feature works
Expand Down
4 changes: 2 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,8 @@ website/.vitepress/dist/
scripts/agent/

# Consumer-app scratch state, not source
package-tests/consumer-app/.sf/
package-tests/consumer-app/.sfdx/
dev/package-tests/consumer-app/.sf/
dev/package-tests/consumer-app/.sfdx/

# Announcement drafts - marketing copy, not a repo artifact
release-notes/linkedin/
5 changes: 4 additions & 1 deletion .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,13 @@ sfdx-project.json
config/
ticket/
website/
package-tests/anonymous/
dev/package-tests/anonymous/
release-notes/
package.json
README.md
CONTRIBUTING.md
SECURITY.md
CODE_OF_CONDUCT.md

# Manual test jobs and screenshot scripts, not part of the package
dev/job-timeline-demo/
2 changes: 1 addition & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"apexPMD.rulesetPath": "pmd/ruleset.xml",
"apexPMD.rulesetPath": "dev/pmd/ruleset.xml",
"apexPMD.runOnFileOpen": true,
"apexPMD.runOnFileSave": true
}
10 changes: 5 additions & 5 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ git checkout -b feature/my-awesome-feature

### 4. Make Your Changes

Make your changes in the appropriate files. Please follow the current coding style and conventions used in the codebase.
Make your changes in the appropriate files. Please follow the current coding style and conventions used in the codebase, and the [code style](dev/docs/code-style.md): class, method and variable names explain the code, and a comment is allowed only when it cannot be done otherwise.

- For Salesforce metadata: follow Salesforce best practices
- For LWC components: follow the patterns in existing components
Expand Down Expand Up @@ -55,7 +55,7 @@ finding has to be resolved before merge. `npm run pmd` prints the same report wi
failing. PMD needs Java 8+ on your PATH; `scripts/pmd.sh` downloads the pinned PMD release
into `.pmd-dist/` on first run.

The ruleset lives in `pmd/ruleset.xml` and is kept identical to the shared Beyond The Cloud
The ruleset lives in `dev/pmd/ruleset.xml` and is kept identical to the shared Beyond The Cloud
ruleset. Do not weaken it to silence a finding. Either fix the code, or add a narrow
`@SuppressWarnings('PMD.RuleName')` at the class or method with a `PMD False Positives:` note
saying why. Suppressions travel with the source, which matters because consumers vendor these
Expand Down Expand Up @@ -100,14 +100,14 @@ Please include:
Before submitting your PR, make sure you've:

- [ ] Written clear and concise commit messages
- [ ] Followed the [code style](docs/code-style.md), in particular: code is self-explaining and comments are a last resort
- [ ] Followed the [code style](dev/docs/code-style.md), in particular: code is self-explaining and comments are a last resort
- [ ] Added or updated relevant documentation (if applicable)
- [ ] Added or updated unit tests (if applicable)
- [ ] Verified that all existing tests pass (`npm test`)
- [ ] Run linting and formatting (`npm run lint`, `npm run prettier`)
- [ ] Updated the documentation site if needed, and for any public API change also `website/ai-usage.md`, the one-page contract agents read (take signatures from the code, not from memory)
- [ ] Added a consumer-perspective test in `package-tests/consumer-app` for any new `global` surface
- [ ] Checked [API evolution rules](docs/api-evolution.md) if you touched a `global` type or added an extension point
- [ ] Added a consumer-perspective test in `dev/package-tests/consumer-app` for any new `global` surface
- [ ] Checked [API evolution rules](dev/docs/api-evolution.md) if you touched a `global` type or added an extension point
- [ ] Added every new field to its page layout in `force-app/main/default/layouts` and, for `AsyncResult__c`, to `AsyncResultAccess` (a field missing from either is invisible to admins)
- [ ] Added any new `extras/` class to `extras/README.md` and to the tables in `website/introduction/packaged-install.md`
- [ ] Added any new website page to the sidebar in `website/.vitepress/config.mts` (`llms.txt` is generated from it)
Expand Down
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,17 @@ sf project deploy start --source-dir force-app --target-org your-org

No namespace, nothing extra to set up, upgrades by redeploying the next tag. Guide: [Deploying the Source](https://async.beyondthecloud.dev/introduction/source-deploy).

## What's in this repo

| Folder | For | What it is |
| --- | --- | --- |
| `force-app` | everyone | the library |
| `extras` | packaged installs | optional classes you copy into your org |
| `website` | everyone | the docs site |
| `release-notes` | everyone | what changed in each version |
| `scripts`, `config` | maintainers | release, PMD and header scripts, scratch org definition |
| `dev` | maintainers | tests, demos and rules for working on the library. You don't need it to use Async Lib |

## Contributors

<a href="https://github.com/beyond-the-cloud-dev/async-lib/graphs/contributors">
Expand Down
14 changes: 14 additions & 0 deletions dev/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# dev

For people working on Async Lib. You don't need any of this to use it.

| Folder | What it is |
| --- | --- |
| `verify` | `bash dev/verify/verify.sh <phase>`, from `static` up to `all`. Run it before a PR |
| `package-tests` | tests that use the library the way a subscriber does, through the namespace |
| `job-timeline-demo` | real jobs to test the Async Jobs card by hand and to retake the docs screenshots |
| `docs` | code style and the rules for changing a shipped `global` surface |
| `pmd` | the PMD ruleset |
| `jest-mocks` | mocks for LWC Jest tests |

`verify` creates scratch orgs when they are missing. It uses the `target-dev-hub` from `sf config`, or `ASYNC_LIB_DEVHUB` if you set it.
File renamed without changes.
15 changes: 10 additions & 5 deletions docs/code-style.md → dev/docs/code-style.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@

## Comments

**Code must be self-explaining. A comment is a last resort, not a default.**
**No comments, unless it cannot be done otherwise. Class, method and variable
names explain the code.**

Classes, methods, fields and variables carry the meaning. If a comment feels
necessary, that is almost always a naming or structure problem, so fix the code
Expand Down Expand Up @@ -39,7 +40,7 @@ String.valueOf((DateTime) job);
Rules of thumb that stay in prose belong in `website/explanations/`, not in the
source. If the reason is about **how consumers use the library**, document it
there and link it from the error message. If it is about **how the framework may
evolve**, it belongs in `docs/api-evolution.md`.
evolve**, it belongs in `dev/docs/api-evolution.md`.

### The two allowed exceptions

Expand All @@ -63,13 +64,13 @@ otherwise remove it.
/**
* Superseded by Async.Retryable.resetBeforeRetry(Integer). Nothing calls this any more.
* It cannot be deleted: dropping a global member makes the package install fail in every
* subscriber org that referenced it. See docs/api-evolution.md.
* subscriber org that referenced it. See dev/docs/api-evolution.md.
**/
```

## Why ApexDoc is not enforced

`pmd/ruleset.xml` deliberately excludes `category/apex/documentation.xml`.
`dev/pmd/ruleset.xml` deliberately excludes `category/apex/documentation.xml`.
Requiring `@description` and `@param` on every member produces exactly the
restatement this policy exists to remove. Editor plugins ship that rule on by
default, so expect warnings; ignore them.
Expand All @@ -78,7 +79,7 @@ default, so expect warnings; ignore them.

Ordinary clean-code expectations apply, and they matter more here than in an org
codebase because this is a library whose public surface is
[frozen once shipped](/docs/api-evolution.md):
[frozen once shipped](api-evolution.md):

- **KISS.** The smallest thing that solves the actual problem. No configuration
nobody asked for.
Expand All @@ -91,3 +92,7 @@ codebase because this is a library whose public surface is
spend it. Prefer a marker interface the consumer can add to any class.
- **Guard clauses over nesting.** Early return, and let the shape of the method
show the flow.
- **No inner class as an `@AuraEnabled` parameter.** Under a namespace the call
dies with `An internal server error has occurred` before the method runs, and
a non-namespaced org never shows it. Use a top-level class. PMD enforces this
as `AuraEnabledInnerClassParameter`.
8 changes: 8 additions & 0 deletions dev/jest-mocks/lightning/modal.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { LightningElement, api } from 'lwc';

export default class LightningModal extends LightningElement {
@api label;
@api size;
static open = jest.fn(() => Promise.resolve());
close() {}
}
29 changes: 29 additions & 0 deletions dev/job-timeline-demo/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# Job Timeline demo

Real jobs for the Async Jobs card, to test it by hand and to retake the docs screenshots. Scratch orgs only: `seed` changes `QueueableJobSetting.All`, and `cleanup` deletes every `AsyncResult__c` row.

Not part of the package. Needs `sf`, `jq`, and for screenshots a global `playwright` (`npm i -g playwright`).

```bash
bash dev/job-timeline-demo/run.sh <action> [org-alias] [chromium-path]
```

Org alias defaults to `async-lib-scratch`.

| Action | What it does | Look at |
| --- | --- | --- |
| `seed` | deploys the demo jobs and settings, runs failed, retried, chunk, priority and 80 small chains. Settles in about 4 minutes | `Northwind Order #10248`, and `Fabrikam Order #20001` for more than 50 chains |
| `live` | a long chain with a runtime insert, a retry and a failure | `Northwind Order #10248` |
| `live-priority` | a job that adds a priority job and a plain job after 30 s, then a chunk run. Watch them arrive with `NEW`, in run order | `Northwind Order #10248` |
| `live-delay` | a chain with three delays and one failure, about 5 minutes. Shows `DELAYED`, `RUNNING`, `RUNNING \| DELAYED`, then `RUNNING \| ✕ 1 \| DELAYED`, and ends `FAILED` | `Northwind Order #10248` |
| `live-quick` | two plain jobs, about 90 seconds, to watch a chain go in and out of `In flight` | `Northwind Order #10248` |
| `showcase` | the runs in the docs screenshots, a minute apart. About 7 minutes | `Acme Order #00124` |
| `screenshots` | retakes all six images in `website/public/images/job-timeline`. Run `showcase` first | |
| `cleanup` | deletes every result row and the demo accounts | |
| `reset` | puts `QueueableJobSetting.All` back to the repo defaults. Do this before `verify.sh full` | |

An idle card does not poll, so refresh it after starting a `live` action. It polls by itself from then on.

Priority examples from `seed` are on `Northwind Order #10248`: `Priority set before start` shows the priority job first, in run order. Open the same card after `live-priority` has finished and the added jobs sit in place with no `NEW`.

Changed the card? Add an example here if a new state has none, then run `showcase` and `screenshots`.
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/**
* Copyright (c) 2026 Beyond The Cloud Sp. z o.o. (BeyondTheCloud.Dev)
* Licensed under the MIT License (https://github.com/beyond-the-cloud-dev/async-lib/blob/main/LICENSE)
*
* v3.1.0
**/
public class AccountRollupJob extends OrderStepJob {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/**
* Copyright (c) 2026 Beyond The Cloud Sp. z o.o. (BeyondTheCloud.Dev)
* Licensed under the MIT License (https://github.com/beyond-the-cloud-dev/async-lib/blob/main/LICENSE)
*
* v3.1.0
**/
public class CommissionSplitJob extends QueueableJob implements Async.Retryable {
public void resetBeforeRetry(Integer attempt) {
}
public override void work() {
throw new DmlException(
'UNABLE_TO_LOCK_ROW: unable to obtain exclusive access to this record. ' +
'The commission split was rolled back.'
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/**
* Copyright (c) 2026 Beyond The Cloud Sp. z o.o. (BeyondTheCloud.Dev)
* Licensed under the MIT License (https://github.com/beyond-the-cloud-dev/async-lib/blob/main/LICENSE)
*
* v3.1.0
**/
public class CreateInvoiceJob extends OrderStepJob {
public override void work() {
super.work();
Async.queueable(new WriteAuditTrail()).name('Write audit trail').attachFinalizer();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
/**
* Copyright (c) 2026 Beyond The Cloud Sp. z o.o. (BeyondTheCloud.Dev)
* Licensed under the MIT License (https://github.com/beyond-the-cloud-dev/async-lib/blob/main/LICENSE)
*
* v3.1.0
**/
public class OrderPagesJob extends ChunkJob implements Async.ChunkResettable {
public Integer spinMs = 2500;
public override void work(List<SObject> page) {
Long until = System.currentTimeMillis() + spinMs;
while (System.currentTimeMillis() < until) {
}
}
public void resetBeforeNextChunk(Integer pageNumber) {
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/**
* Copyright (c) 2026 Beyond The Cloud Sp. z o.o. (BeyondTheCloud.Dev)
* Licensed under the MIT License (https://github.com/beyond-the-cloud-dev/async-lib/blob/main/LICENSE)
*
* v3.1.0
**/
public virtual class OrderStepJob extends QueueableJob {
public Integer spinMs = 1500;
public override virtual void work() {
Long until = System.currentTimeMillis() + spinMs;
while (System.currentTimeMillis() < until) {
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/**
* Copyright (c) 2026 Beyond The Cloud Sp. z o.o. (BeyondTheCloud.Dev)
* Licensed under the MIT License (https://github.com/beyond-the-cloud-dev/async-lib/blob/main/LICENSE)
*
* v3.1.0
**/
public class PriorityDemoJob extends QueueableJob {
public Integer spinMs = 1000;
public Id addsJobsLinkedTo;
public override void work() {
Long until = System.currentTimeMillis() + spinMs;
while (System.currentTimeMillis() < until) {
}
if (addsJobsLinkedTo == null) {
return;
}
Async.queueable(new PriorityDemoJob())
.name('Urgent, added at runtime')
.priority(1)
.linkRecord(addsJobsLinkedTo)
.enqueue();
Async.queueable(new PriorityDemoJob())
.name('Plain, added at runtime')
.linkRecord(addsJobsLinkedTo)
.chain();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
/**
* Copyright (c) 2026 Beyond The Cloud Sp. z o.o. (BeyondTheCloud.Dev)
* Licensed under the MIT License (https://github.com/beyond-the-cloud-dev/async-lib/blob/main/LICENSE)
*
* v3.1.0
**/
public class RepriceLinesJob extends ChunkJob implements Async.ChunkResettable {
public override void work(List<SObject> page) {
if (getRun().currentPageNumber() == 5) {
throw new DmlException('FIELD_CUSTOM_VALIDATION_EXCEPTION: Discount cannot exceed 40%');
}
}
public void resetBeforeNextChunk(Integer pageNumber) {
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/**
* Copyright (c) 2026 Beyond The Cloud Sp. z o.o. (BeyondTheCloud.Dev)
* Licensed under the MIT License (https://github.com/beyond-the-cloud-dev/async-lib/blob/main/LICENSE)
*
* v3.1.0
**/
public class ReserveStockJob extends OrderStepJob {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
/**
* Copyright (c) 2026 Beyond The Cloud Sp. z o.o. (BeyondTheCloud.Dev)
* Licensed under the MIT License (https://github.com/beyond-the-cloud-dev/async-lib/blob/main/LICENSE)
*
* v3.1.0
**/
public class StopOnClosedPeriodJob extends OrderStepJob {
public override void work() {
Async.stopChain();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/**
* Copyright (c) 2026 Beyond The Cloud Sp. z o.o. (BeyondTheCloud.Dev)
* Licensed under the MIT License (https://github.com/beyond-the-cloud-dev/async-lib/blob/main/LICENSE)
*
* v3.1.0
**/
public class SyncToErpJob extends OrderStepJob {
}
Loading
Loading