From cf15866703298d4cb6c7fb3b9cf4e111f85b29f8 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sat, 18 Jul 2026 13:27:29 +0200 Subject: [PATCH 1/4] Add module archetype guidance and navigation --- src/docs/Modules/Module-Types.md | 93 +++++++++++++++++++++++++ src/docs/Modules/Repository-Defaults.md | 2 +- src/docs/Modules/index.md | 1 + src/zensical.toml | 1 + 4 files changed, 96 insertions(+), 1 deletion(-) create mode 100644 src/docs/Modules/Module-Types.md diff --git a/src/docs/Modules/Module-Types.md b/src/docs/Modules/Module-Types.md new file mode 100644 index 0000000..085504f --- /dev/null +++ b/src/docs/Modules/Module-Types.md @@ -0,0 +1,93 @@ +# Module types + +Most PSModule modules fall into one of a few archetypes. The general rules in +[PowerShell module standard](Standards.md) and [PowerShell Standards](../PowerShell/Standard/index.md) always apply; this +page adds the conventions that are specific to a module's type so that modules of the same kind feel +the same to use. + +Two archetypes have enough shared shape to standardize: + +- **Integration (API) modules** wrap an external service's REST or GraphQL API. +- **Data modules** convert or manage a data format or in-memory structure. + +A module can be both (for example, an integration module that also exposes conversion helpers). +Apply each relevant section. + +## Integration (API) modules + +Integration modules are the PowerShell face of an external service. `GitHub`, and the +service-client modules such as `Anthropic`, `OpenAI`, `Bluesky`, and `Domeneshop`, are integration +modules. + +### Command naming maps to the resource, not the HTTP method + +Name commands after the resource and the intent, using approved verbs. Never name a command after +the HTTP method or the endpoint path. Map REST methods to verbs: + +| REST method | PowerShell verb | Example | +| ----------- | --------------- | ------- | +| `GET` | `Get-` | `Get-GitHubRepository` | +| `POST` (create) | `New-` / `Add-` | `New-GitHubRepository` | +| `PUT` / `PATCH` (update) | `Set-` / `Update-` | `Set-GitHubRepository` | +| `DELETE` | `Remove-` | `Remove-GitHubRepository` | +| Non-CRUD action | Approved verb for the intent | `Invoke-`, `Start-`, `Stop-`, `Enable-`, ... | + +Prefix the noun with the service's term of art (`GitHubRepository`, not `Repository`). + +### Transport stays private + +Public functions accept resolved inputs and typed objects; private helpers own the concrete +`Invoke-RestMethod` / GraphQL / HTTP calls. This is the Dependency Inversion rule from +[Standards](Standards.md#solid-applied) applied to the network boundary. + +### Use Context for user and module settings + +Integration modules persist state with the [`Context`](https://github.com/PSModule/Context) module +rather than inventing bespoke storage. Two kinds of state are both standard: + +- **User settings and secrets**: accounts, tokens, sessions, and per-user configuration. Store these + in a per-user context. `Context` encrypts secrets at rest (via `Sodium`), so a user can resume work + without reconfiguring or logging in again when the service supports session refresh. +- **Module settings**: module-wide defaults, endpoints, and feature flags that are not tied to a + single user. Store these in a module-scoped context. + +Persisting both through `Context` gives every integration module the same, discoverable settings +model, and keeps secrets out of source, logs, and plain files. + +## Data modules + +Data modules convert between representations or manage an in-memory structure. `Hashtable` is the +reference shape; `Base64`, `Json`, `Lua`, `Hcl`, `Sodium`, and `Uri` follow the same pattern. + +### The neutral object is the pivot + +Every conversion goes through the neutral PowerShell object model +(`[PSCustomObject]` / `[hashtable]` / `[PSObject]`). `ConvertFrom-` parses a +format-specific representation into an object; `ConvertTo-` renders an object into the +format. Converting through the object as a common pivot means any format interoperates with any +other, instead of writing a direct converter for every pair. + +Always ship both directions so data can round-trip between the format and the object model. + +### Verb vocabulary + +| Verb pattern | Purpose | +| ------------ | ------- | +| `ConvertFrom-` | Format-specific text/representation -> `[PSCustomObject]` / `[hashtable]` | +| `ConvertTo-` | Object -> format-specific text/representation | +| `Import-` | Read from a file or store into objects | +| `Export-` | Write objects to a file or store | +| `Format-` | Produce a normalized or pretty rendering | +| `Merge-` | Combine two structures | +| `Compare-` | Diff two structures | +| `Test-` | Validate a value or structure | +| `Remove-Entry` | Remove elements by criteria | + +The `Hashtable` module demonstrates the full set: `ConvertFrom-Hashtable`, `ConvertTo-Hashtable`, +`Import-Hashtable`, `Export-Hashtable`, `Format-Hashtable`, `Merge-Hashtable`, and +`Remove-HashtableEntry`. + +## Where this connects + +- [PowerShell module standard](Standards.md): layout, private functions, and the mandatory context parameter. +- [Repository Defaults](Repository-Defaults.md): repository files, README shape, and agent onboarding. diff --git a/src/docs/Modules/Repository-Defaults.md b/src/docs/Modules/Repository-Defaults.md index 7ce6b66..0a94a19 100644 --- a/src/docs/Modules/Repository-Defaults.md +++ b/src/docs/Modules/Repository-Defaults.md @@ -2,7 +2,7 @@ This page defines the default repository contract for PowerShell module repositories in the PSModule organization. It describes what a newly created or maintained module repository should look like before module-specific code, tests, documentation, and managed repository files are considered. -The implementation standard still lives in [PowerShell module standard](Standards.md). This page covers repository defaults: files, metadata, README shape, release integration, placeholder handling, shared community files, and managed-file distribution. +The implementation standard still lives in [PowerShell module standard](Standards.md). Type-specific conventions for integration (API) and data modules live in [Module types](Module-Types.md). This page covers repository defaults: files, metadata, README shape, release integration, placeholder handling, shared community files, and managed-file distribution. ## Scope diff --git a/src/docs/Modules/index.md b/src/docs/Modules/index.md index 8c458df..2989ec8 100644 --- a/src/docs/Modules/index.md +++ b/src/docs/Modules/index.md @@ -13,6 +13,7 @@ This section is the local source of truth for: - [Repository Defaults](Repository-Defaults.md) - [Standards](Standards.md) +- [Module types](Module-Types.md) - [Test Specification](Test-Specification.md) - [Versioning](Versioning.md) - [Catalog](Catalog/index.md) diff --git a/src/zensical.toml b/src/zensical.toml index 24f5553..7894e6f 100644 --- a/src/zensical.toml +++ b/src/zensical.toml @@ -21,6 +21,7 @@ nav = [ "Modules/index.md", {"Repository Defaults" = "Modules/Repository-Defaults.md"}, {"Standards" = "Modules/Standards.md"}, + {"Module types" = "Modules/Module-Types.md"}, {"Test Specification" = "Modules/Test-Specification.md"}, {"Versioning" = "Modules/Versioning.md"}, {"Catalog" = [ From eac25f0b4214fc0d865a4dc84f2a90a0c2be7bce Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sat, 18 Jul 2026 15:12:14 +0200 Subject: [PATCH 2/4] Allow flexible transport abstraction visibility Transport, REST/GraphQL functions, and Context do not have to be private. Provide guidance for three strategies: - Private transport (common approach, follows Dependency Inversion) - Public transport (for power users or module composition) - Public Context (for direct user configuration of state and settings) Module authors choose the strategy that best serves their audience. --- src/docs/Modules/Module-Types.md | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/docs/Modules/Module-Types.md b/src/docs/Modules/Module-Types.md index 085504f..b1dcf66 100644 --- a/src/docs/Modules/Module-Types.md +++ b/src/docs/Modules/Module-Types.md @@ -34,11 +34,20 @@ the HTTP method or the endpoint path. Map REST methods to verbs: Prefix the noun with the service's term of art (`GitHubRepository`, not `Repository`). -### Transport stays private +### Transport abstraction -Public functions accept resolved inputs and typed objects; private helpers own the concrete -`Invoke-RestMethod` / GraphQL / HTTP calls. This is the Dependency Inversion rule from -[Standards](Standards.md#solid-applied) applied to the network boundary. +Lower-level helpers own the concrete `Invoke-RestMethod` / GraphQL / HTTP calls. How you expose +or hide this abstraction is a design choice: + +- **Private transport** (common): Keep REST, GraphQL, and HTTP helpers private. Public functions + accept resolved inputs and typed objects. This follows the Dependency Inversion rule from + [Standards](Standards.md#solid-applied) applied to the network boundary. +- **Public transport**: Expose REST or GraphQL functions publicly for power users or module + composition. +- **Public Context**: Expose the `Context` module as public so users can configure and manage + module state, secrets, and settings directly. + +Choose the strategy that best serves your module's audience. ### Use Context for user and module settings From 3a4a7fd0de1e9bd376337ebcbe3316e471af7ea8 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sat, 18 Jul 2026 15:14:58 +0200 Subject: [PATCH 3/4] Require Context functions and object types for targeting environments Context module provides on-disk storage for user data and secrets. Modules must expose functions and object types so users can programmatically target specific contexts and environments. Users need to select which environment or context their functions operate against. --- src/docs/Modules/Module-Types.md | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/docs/Modules/Module-Types.md b/src/docs/Modules/Module-Types.md index b1dcf66..f39c095 100644 --- a/src/docs/Modules/Module-Types.md +++ b/src/docs/Modules/Module-Types.md @@ -52,7 +52,8 @@ Choose the strategy that best serves your module's audience. ### Use Context for user and module settings Integration modules persist state with the [`Context`](https://github.com/PSModule/Context) module -rather than inventing bespoke storage. Two kinds of state are both standard: +rather than inventing bespoke storage. Context provides on-disk storage for user data and secrets, +organized by context and environment. Two kinds of state are both standard: - **User settings and secrets**: accounts, tokens, sessions, and per-user configuration. Store these in a per-user context. `Context` encrypts secrets at rest (via `Sodium`), so a user can resume work @@ -60,8 +61,11 @@ rather than inventing bespoke storage. Two kinds of state are both standard: - **Module settings**: module-wide defaults, endpoints, and feature flags that are not tied to a single user. Store these in a module-scoped context. -Persisting both through `Context` gives every integration module the same, discoverable settings -model, and keeps secrets out of source, logs, and plain files. +Your module must expose functions and object types so users can target specific contexts and +environments. Users need to be able to read from, write to, and manage contexts programmatically, +selecting which environment or context their functions operate against. Persisting both through +`Context` gives every integration module the same, discoverable settings model and keeps secrets +out of source, logs, and plain files. ## Data modules From 1c95df2fcfe7d5de9d0245482c216974024c5022 Mon Sep 17 00:00:00 2001 From: Marius Storhaug Date: Sat, 18 Jul 2026 15:17:42 +0200 Subject: [PATCH 4/4] Clarify ConvertFrom/ConvertTo data flow directions ConvertFrom- takes format-specific input and outputs PSCustomObject. ConvertTo- takes PSCustomObject input and outputs format-specific text. Use arrows to make the direction explicit in verb vocabulary table. --- src/docs/Modules/Module-Types.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/docs/Modules/Module-Types.md b/src/docs/Modules/Module-Types.md index f39c095..be97843 100644 --- a/src/docs/Modules/Module-Types.md +++ b/src/docs/Modules/Module-Types.md @@ -86,8 +86,8 @@ Always ship both directions so data can round-trip between the format and the ob | Verb pattern | Purpose | | ------------ | ------- | -| `ConvertFrom-` | Format-specific text/representation -> `[PSCustomObject]` / `[hashtable]` | -| `ConvertTo-` | Object -> format-specific text/representation | +| `ConvertFrom-` | Format-specific text/representation → `[PSCustomObject]` / `[hashtable]` | +| `ConvertTo-` | `[PSCustomObject]` / `[hashtable]` → format-specific text/representation | | `Import-` | Read from a file or store into objects | | `Export-` | Write objects to a file or store | | `Format-` | Produce a normalized or pretty rendering |