Skip to content
Open
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
5 changes: 5 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ pnpm check

Keep the public API runtime-agnostic. Browser selectors, application state, and model or prompt execution belong in host integrations supplied through resolvers and custom filters.

The [portable rendering fixtures](compat/fixtures/README.md) make selected
documentation examples and regression expectations reusable by other language
implementations. If a change affects their source examples, update the fixtures
with `pnpm test:compat --update`, review the JSON diff, and run `pnpm check` again.

## Release checks

Before changing the package version, verify both the repository and the exact package contents:
Expand Down
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,4 +71,8 @@ pnpm install
pnpm check
```

The [portable rendering fixtures](compat/fixtures/README.md) make selected
documentation examples and regression expectations reusable by implementations
in other languages.

Knap is available under the [MIT License](LICENSE).
9 changes: 7 additions & 2 deletions compat/README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
# Web Clipper help compatibility
# Compatibility checks

For language-independent rendering cases and instructions for other
implementations, see [Portable rendering fixtures](fixtures/README.md).

## Web Clipper help compatibility

The regular Knap suite includes executable fixtures from the English Web Clipper Filters and Logic pages in `tests/fixtures/clipper-help.ts`, plus tests that schema paths reach the host resolver intact. JSON examples in the filter docs represent input data; the tests supply them as variables rather than inventing array or object literal syntax for Knap expressions.

Expand All @@ -10,7 +15,7 @@ Run the consumer checks against sibling `obsidian-clipper` and `obsidian-help` c

Override their locations with `CLIPPER_ROOT` and `HELP_ROOT` if needed. The suite uses Clipper's actual compiler, schema variable builder/resolver, and DOM selector adapter, with Knap imports redirected to this checkout's source. It does not update Clipper's installed Knap dependency. Prompt checks verify deferred syntax without contacting an AI provider. Browser extension messaging and model responses are outside this suite.

## September 7, 2026 audit
### September 7, 2026 audit

- Ordinary bracket-then-dot access was missing in Clipper commit `b814405` (January 10, 2026) and remained missing in Knap's initial extraction `ffffcda`. `ed9782a` fixes this. Schema expressions use a separate host resolver path; the three documented `author.name`, `author[0].name`, and `author[*].name` examples work, including explicit `@Article` variants.
- Added coverage exposed lost escapes in quoted regex patterns, merge failures for plain strings and legacy quoted lists, and separators from empty loop iterations. These are covered by the shared fixtures.
Expand Down
107 changes: 107 additions & 0 deletions compat/fixtures/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
# Portable rendering fixtures

[render.json](render.json) contains language-independent input/output cases for implementations
of Knap. It is ordinary UTF-8 JSON; consuming it requires no JavaScript runtime,
Vitest, or access to the TypeScript source.

The corpus captures selected, reviewed expectations. Each included case has a
concrete expected result, but this is not a complete language specification.
Passing it means passing these cases, not proving full Knap compatibility.
Behavior that is not covered remains unspecified by this corpus.

## Format version 1

The top-level object contains:

| Field | Meaning |
| --- | --- |
| `format_version` | Fixture format version. Reject unsupported versions. |
| `knap_version` | Package version when the corpus was exported. |
| `preset` | Filter registry to use; currently `standard`. |
| `cases` | Array of independent rendering cases. |

Pin the repository commit or release containing the corpus as well as the format
version. The package version alone does not identify changes between releases.
Case IDs are descriptive identifiers within that pinned corpus; source example
renames can change them.

Each case contains:

| Field | Meaning |
| --- | --- |
| `id` | Unique identifier used in test reports. |
| `source` | Repository-relative source file and example/test name, for review. |
| `template` | Template string to render. |
| `variables` | JSON object supplying the template variables. |
| `expected.output` | Exact rendered string after JSON decoding. |
| `expected.errors` | Ordered array of error code strings. |
| `expected.warnings` | Ordered array of objects containing `code` and `filter`. |

For example:

```json
{
"id": "example/upper",
"source": "Illustration of the fixture format",
"template": "{{ title | upper }}",
"variables": { "title": "Hello" },
"expected": { "output": "HELLO", "errors": [], "warnings": [] }
}
```

Render every case with the standard filters, default render options and limits,
fresh variables, and no custom filters or variable resolver. Compare output
exactly, including blank lines and trailing whitespace. Do not normalize Unicode
or line endings. Compare the complete diagnostic code lists, preserving their
order and multiplicity; an empty list means no diagnostics of that kind are
expected. Diagnostic messages and source locations are not compared in format 1.
Inspect the render result rather than using a throw-on-error API: a case can
expect output together with errors or warnings.

Report failed and unsupported cases separately from passes. A partial
implementation should identify which cases it did not execute. The same fixture
data can be used directly by a Rust test harness or any other language's runner.

## Scope

The initial corpus includes executable standard-filter documentation examples,
logic reference examples, the existing Clipper logic fixtures, and selected
parse/filter diagnostic regressions. It covers outputs already authored in the
repository. The diagnostic cases also make existing error and warning
expectations portable.

DOM-dependent HTML filters, application resolvers, custom JavaScript callbacks,
CLI filesystem behavior, parser ASTs, and resource-limit accounting are outside
this corpus. It does not settle all coercion, Unicode, regex, date/time, or
serialization edge cases. New cases in those areas should be reviewed as
explicit compatibility expectations, especially when current behavior might be
a bug. The corpus does not promise portability for arbitrary host objects or
host-dependent time, locale, or DOM behavior.

## Running and updating

The TypeScript runner is part of `pnpm test` and `pnpm check`. Run it alone with:

```sh
pnpm test:compat
```

`tests/fixtures/portable-render.ts` collects hand-authored expectations from the
documentation and selected regression tests. The runner checks that the committed
JSON matches those sources, then renders the cases from the committed JSON.
This keeps the documentation, exported cases, and TypeScript implementation in
agreement without maintaining a second set of expected outputs.

After intentionally changing a source example or adding a reviewed expectation:

```sh
pnpm test:compat --update
pnpm check
```

The update command uses Vitest's external file snapshot support to write plain
JSON. It serializes the authored expectations; it never obtains expected output
by running the renderer. Review the JSON diff and commit it with the source
change. Run `pnpm check` after updating, because the update invocation loads the
previously committed cases before rewriting the file. CI checks freshness and
rendering without updating expectations.
Loading