@romandmitri/ai-patch 0.1.0

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.
Files changed (95) hide show
  1. package/README.md +86 -0
  2. package/dist/content/Content.d.ts +2 -0
  3. package/dist/content/Content.d.ts.map +1 -0
  4. package/dist/content/Content.js +2 -0
  5. package/dist/content/Content.js.map +1 -0
  6. package/dist/content/ContentFormat.d.ts +4 -0
  7. package/dist/content/ContentFormat.d.ts.map +1 -0
  8. package/dist/content/ContentFormat.js +7 -0
  9. package/dist/content/ContentFormat.js.map +1 -0
  10. package/dist/content/ContentLineNumber.d.ts +2 -0
  11. package/dist/content/ContentLineNumber.d.ts.map +1 -0
  12. package/dist/content/ContentLineNumber.js +2 -0
  13. package/dist/content/ContentLineNumber.js.map +1 -0
  14. package/dist/content/ContentPartial.d.ts +2 -0
  15. package/dist/content/ContentPartial.d.ts.map +1 -0
  16. package/dist/content/ContentPartial.js +2 -0
  17. package/dist/content/ContentPartial.js.map +1 -0
  18. package/dist/content/ContentPosition.d.ts +2 -0
  19. package/dist/content/ContentPosition.d.ts.map +1 -0
  20. package/dist/content/ContentPosition.js +2 -0
  21. package/dist/content/ContentPosition.js.map +1 -0
  22. package/dist/error/PatchError.d.ts +19 -0
  23. package/dist/error/PatchError.d.ts.map +1 -0
  24. package/dist/error/PatchError.js +26 -0
  25. package/dist/error/PatchError.js.map +1 -0
  26. package/dist/index.d.ts +11 -0
  27. package/dist/index.d.ts.map +1 -0
  28. package/dist/index.js +5 -0
  29. package/dist/index.js.map +1 -0
  30. package/dist/operation/Operation.d.ts +6 -0
  31. package/dist/operation/Operation.d.ts.map +1 -0
  32. package/dist/operation/Operation.js +2 -0
  33. package/dist/operation/Operation.js.map +1 -0
  34. package/dist/operation/OperationType.d.ts +8 -0
  35. package/dist/operation/OperationType.d.ts.map +1 -0
  36. package/dist/operation/OperationType.js +11 -0
  37. package/dist/operation/OperationType.js.map +1 -0
  38. package/dist/operation/Operation_Delete.d.ts +15 -0
  39. package/dist/operation/Operation_Delete.d.ts.map +1 -0
  40. package/dist/operation/Operation_Delete.js +12 -0
  41. package/dist/operation/Operation_Delete.js.map +1 -0
  42. package/dist/operation/Operation_InsertAfter.d.ts +17 -0
  43. package/dist/operation/Operation_InsertAfter.d.ts.map +1 -0
  44. package/dist/operation/Operation_InsertAfter.js +13 -0
  45. package/dist/operation/Operation_InsertAfter.js.map +1 -0
  46. package/dist/operation/Operation_InsertBefore.d.ts +17 -0
  47. package/dist/operation/Operation_InsertBefore.d.ts.map +1 -0
  48. package/dist/operation/Operation_InsertBefore.js +13 -0
  49. package/dist/operation/Operation_InsertBefore.js.map +1 -0
  50. package/dist/operation/Operation_Replace.d.ts +17 -0
  51. package/dist/operation/Operation_Replace.d.ts.map +1 -0
  52. package/dist/operation/Operation_Replace.js +13 -0
  53. package/dist/operation/Operation_Replace.js.map +1 -0
  54. package/dist/patch/Edit.d.ts +11 -0
  55. package/dist/patch/Edit.d.ts.map +1 -0
  56. package/dist/patch/Edit.js +2 -0
  57. package/dist/patch/Edit.js.map +1 -0
  58. package/dist/patch/InputIndex.d.ts +2 -0
  59. package/dist/patch/InputIndex.d.ts.map +1 -0
  60. package/dist/patch/InputIndex.js +2 -0
  61. package/dist/patch/InputIndex.js.map +1 -0
  62. package/dist/patch/Patcher.d.ts +8 -0
  63. package/dist/patch/Patcher.d.ts.map +1 -0
  64. package/dist/patch/Patcher.js +7 -0
  65. package/dist/patch/Patcher.js.map +1 -0
  66. package/dist/patch/Patcher_Text.d.ts +13 -0
  67. package/dist/patch/Patcher_Text.d.ts.map +1 -0
  68. package/dist/patch/Patcher_Text.js +106 -0
  69. package/dist/patch/Patcher_Text.js.map +1 -0
  70. package/dist/target/Target.d.ts +23 -0
  71. package/dist/target/Target.d.ts.map +1 -0
  72. package/dist/target/Target.js +76 -0
  73. package/dist/target/Target.js.map +1 -0
  74. package/dist/target/test/Target.apply.test.d.ts +2 -0
  75. package/dist/target/test/Target.apply.test.d.ts.map +1 -0
  76. package/dist/target/test/Target.apply.test.js +127 -0
  77. package/dist/target/test/Target.apply.test.js.map +1 -0
  78. package/dist/target/test/Target.complex.test.d.ts +2 -0
  79. package/dist/target/test/Target.complex.test.d.ts.map +1 -0
  80. package/dist/target/test/Target.complex.test.js +217 -0
  81. package/dist/target/test/Target.complex.test.js.map +1 -0
  82. package/dist/target/test/Target.errors.test.d.ts +2 -0
  83. package/dist/target/test/Target.errors.test.d.ts.map +1 -0
  84. package/dist/target/test/Target.errors.test.js +108 -0
  85. package/dist/target/test/Target.errors.test.js.map +1 -0
  86. package/dist/target/test/Target.schema.test.d.ts +2 -0
  87. package/dist/target/test/Target.schema.test.d.ts.map +1 -0
  88. package/dist/target/test/Target.schema.test.js +101 -0
  89. package/dist/target/test/Target.schema.test.js.map +1 -0
  90. package/dist/target/test/Target.tool.test.d.ts +2 -0
  91. package/dist/target/test/Target.tool.test.d.ts.map +1 -0
  92. package/dist/target/test/Target.tool.test.js +76 -0
  93. package/dist/target/test/Target.tool.test.js.map +1 -0
  94. package/docs/development.md +28 -0
  95. package/package.json +67 -0
@@ -0,0 +1,108 @@
1
+ import { ContentFormat, OperationType, PatchError, PatchErrorCode, Target } from "#src/index.js";
2
+ import assert from "node:assert/strict";
3
+ import { test } from "node:test";
4
+ function target(content) {
5
+ return new Target({ content, format: ContentFormat.Text });
6
+ }
7
+ function assertPatchError(callback, code, patchIndex) {
8
+ assert.throws(callback, (error) => {
9
+ assert.ok(error instanceof PatchError);
10
+ assert.equal(error.code, code);
11
+ assert.equal(error.patchIndex, patchIndex);
12
+ return true;
13
+ });
14
+ }
15
+ test("anchors must match at the exact expected line", () => {
16
+ assertPatchError(() => target("same\nwanted\nsame").apply([
17
+ {
18
+ operation: OperationType.Delete,
19
+ expectedLine: 1,
20
+ oldText: "wanted",
21
+ },
22
+ ]), PatchErrorCode.AnchorMismatch, 0);
23
+ });
24
+ test("expectedLine must be a valid 1-based original line", () => {
25
+ const invalidPatch = {
26
+ operation: OperationType.Delete,
27
+ expectedLine: 0,
28
+ oldText: "one",
29
+ };
30
+ assertPatchError(() => target("one\ntwo").apply([invalidPatch]), PatchErrorCode.InvalidExpectedLine, 0);
31
+ assertPatchError(() => target("one\ntwo").apply([
32
+ {
33
+ operation: OperationType.Delete,
34
+ expectedLine: 3,
35
+ oldText: "two",
36
+ },
37
+ ]), PatchErrorCode.InvalidExpectedLine, 0);
38
+ });
39
+ test("overlapping consumed ranges reject the full list", () => {
40
+ assertPatchError(() => target("alpha\nbeta\ngamma").apply([
41
+ {
42
+ operation: OperationType.Replace,
43
+ expectedLine: 1,
44
+ oldText: "alpha\nbeta",
45
+ newText: "first",
46
+ },
47
+ {
48
+ operation: OperationType.Delete,
49
+ expectedLine: 2,
50
+ oldText: "beta\ngamma",
51
+ },
52
+ ]), PatchErrorCode.OverlappingRange, 1);
53
+ });
54
+ test("insertions inside consumed ranges are rejected", () => {
55
+ assertPatchError(() => target("alpha\nbeta\ngamma").apply([
56
+ {
57
+ operation: OperationType.Delete,
58
+ expectedLine: 1,
59
+ oldText: "alpha\nbeta",
60
+ },
61
+ {
62
+ operation: OperationType.InsertBefore,
63
+ expectedLine: 2,
64
+ oldText: "beta",
65
+ newText: "inserted",
66
+ },
67
+ ]), PatchErrorCode.InsertionInsideRange, 1);
68
+ });
69
+ test("multiple insertions at the same boundary are rejected", () => {
70
+ assertPatchError(() => target("alpha\nbeta").apply([
71
+ {
72
+ operation: OperationType.InsertBefore,
73
+ expectedLine: 2,
74
+ oldText: "beta",
75
+ newText: "first",
76
+ },
77
+ {
78
+ operation: OperationType.InsertBefore,
79
+ expectedLine: 2,
80
+ oldText: "beta",
81
+ newText: "second",
82
+ },
83
+ ]), PatchErrorCode.ConflictingInsertion, 1);
84
+ });
85
+ test("one invalid patch rolls back the entire atomic batch", () => {
86
+ const patchTarget = target("one\ntwo\nthree");
87
+ assertPatchError(() => patchTarget.apply([
88
+ {
89
+ operation: OperationType.Replace,
90
+ expectedLine: 1,
91
+ oldText: "one",
92
+ newText: "changed",
93
+ },
94
+ {
95
+ operation: OperationType.Delete,
96
+ expectedLine: 3,
97
+ oldText: "missing",
98
+ },
99
+ ]), PatchErrorCode.AnchorMismatch, 1);
100
+ assert.equal(patchTarget.content, "one\ntwo\nthree");
101
+ });
102
+ test("unsupported formats are rejected explicitly", () => {
103
+ assert.throws(() => new Target({
104
+ content: "value",
105
+ format: "json",
106
+ }), RangeError);
107
+ });
108
+ //# sourceMappingURL=Target.errors.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Target.errors.test.js","sourceRoot":"","sources":["../../../src/target/test/Target.errors.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=Target.schema.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Target.schema.test.d.ts","sourceRoot":"","sources":["../../../src/target/test/Target.schema.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,101 @@
1
+ import { ContentFormat, OperationType, Target } from "#src/index.js";
2
+ import assert from "node:assert/strict";
3
+ import { test } from "node:test";
4
+ import { z } from "zod";
5
+ function target(content) {
6
+ return new Target({ content, format: ContentFormat.Text });
7
+ }
8
+ test("toSchema parses every operation variant", () => {
9
+ const schema = target("content").toSchema();
10
+ const patches = [
11
+ {
12
+ operation: OperationType.Replace,
13
+ expectedLine: 1,
14
+ oldText: "a",
15
+ newText: "b",
16
+ },
17
+ {
18
+ operation: OperationType.InsertBefore,
19
+ expectedLine: 1,
20
+ oldText: "a",
21
+ newText: "b",
22
+ },
23
+ {
24
+ operation: OperationType.InsertAfter,
25
+ expectedLine: 1,
26
+ oldText: "a",
27
+ newText: "b",
28
+ },
29
+ {
30
+ operation: OperationType.Delete,
31
+ expectedLine: 1,
32
+ oldText: "a",
33
+ },
34
+ ];
35
+ assert.deepEqual(schema.parse(patches), patches);
36
+ });
37
+ test("toSchema rejects malformed operation-specific fields", () => {
38
+ const schema = target("content").toSchema();
39
+ assert.equal(schema.safeParse([
40
+ {
41
+ operation: OperationType.Replace,
42
+ expectedLine: 1,
43
+ oldText: "a",
44
+ },
45
+ ]).success, false);
46
+ assert.equal(schema.safeParse([
47
+ {
48
+ operation: OperationType.Delete,
49
+ expectedLine: 1,
50
+ oldText: "a",
51
+ newText: "unexpected",
52
+ },
53
+ ]).success, false);
54
+ assert.equal(schema.safeParse([
55
+ {
56
+ operation: OperationType.Delete,
57
+ expectedLine: 0,
58
+ oldText: "a",
59
+ },
60
+ ]).success, false);
61
+ assert.equal(schema.safeParse({ patches: [] }).success, false);
62
+ });
63
+ test("schema descriptions document target scope and exact atomic semantics", () => {
64
+ const schema = target("content").toSchema();
65
+ const jsonSchema = JSON.stringify(z.toJSONSchema(schema));
66
+ assert.match(schema.description ?? "", /Atomically update this target only/);
67
+ assert.match(jsonSchema, /1-based line/);
68
+ assert.match(jsonSchema, /exact text anchor/);
69
+ assert.match(jsonSchema, /Replace the exact oldText anchor/);
70
+ assert.match(jsonSchema, /original content coordinates/);
71
+ });
72
+ test("two target schemas compose as independent object properties", () => {
73
+ const documentTarget = target("document");
74
+ const summaryTarget = target("summary");
75
+ const schema = z.object({
76
+ documentPatches: documentTarget.toSchema(),
77
+ summaryPatches: summaryTarget.toSchema(),
78
+ });
79
+ const value = {
80
+ documentPatches: [
81
+ {
82
+ operation: OperationType.Replace,
83
+ expectedLine: 1,
84
+ oldText: "document",
85
+ newText: "updated document",
86
+ },
87
+ ],
88
+ summaryPatches: [
89
+ {
90
+ operation: OperationType.Replace,
91
+ expectedLine: 1,
92
+ oldText: "summary",
93
+ newText: "updated summary",
94
+ },
95
+ ],
96
+ };
97
+ assert.deepEqual(schema.parse(value), value);
98
+ assert.equal(documentTarget.apply(value.documentPatches), "updated document");
99
+ assert.equal(summaryTarget.apply(value.summaryPatches), "updated summary");
100
+ });
101
+ //# sourceMappingURL=Target.schema.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Target.schema.test.js","sourceRoot":"","sources":["../../../src/target/test/Target.schema.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=Target.tool.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Target.tool.test.d.ts","sourceRoot":"","sources":["../../../src/target/test/Target.tool.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,76 @@
1
+ import { ContentFormat, OperationType, PatchError, PatchErrorCode, Target } from "#src/index.js";
2
+ import assert from "node:assert/strict";
3
+ import { test } from "node:test";
4
+ function target(content) {
5
+ return new Target({ content, format: ContentFormat.Text });
6
+ }
7
+ async function executeTool(generatedTool, input) {
8
+ assert.ok(generatedTool.execute);
9
+ return await generatedTool.execute(input, {
10
+ toolCallId: "test-call",
11
+ messages: [],
12
+ context: undefined,
13
+ });
14
+ }
15
+ test("generated tools delegate to their own immutable targets", async () => {
16
+ const documentTarget = target("document");
17
+ const summaryTarget = target("summary");
18
+ const documentTool = documentTarget.toVercelTool();
19
+ const summaryTool = summaryTarget.toVercelTool();
20
+ const documentResult = await executeTool(documentTool, {
21
+ patches: [
22
+ {
23
+ operation: OperationType.Replace,
24
+ expectedLine: 1,
25
+ oldText: "document",
26
+ newText: "updated document",
27
+ },
28
+ ],
29
+ });
30
+ const summaryResult = await executeTool(summaryTool, {
31
+ patches: [
32
+ {
33
+ operation: OperationType.Replace,
34
+ expectedLine: 1,
35
+ oldText: "summary",
36
+ newText: "updated summary",
37
+ },
38
+ ],
39
+ });
40
+ assert.equal(documentResult, "updated document");
41
+ assert.equal(summaryResult, "updated summary");
42
+ assert.equal(documentTarget.content, "document");
43
+ assert.equal(summaryTarget.content, "summary");
44
+ });
45
+ test("generated tool input and tool descriptions are package-owned", () => {
46
+ const generatedTool = target("content").toVercelTool();
47
+ const inputSchema = generatedTool.inputSchema;
48
+ const description = generatedTool.description;
49
+ if (typeof description !== "string") {
50
+ assert.fail("expected a static tool description");
51
+ }
52
+ assert.match(description, /this target only/);
53
+ assert.match(inputSchema.description ?? "", /immutable target/);
54
+ assert.deepEqual(inputSchema.parse({ patches: [] }), { patches: [] });
55
+ assert.throws(() => inputSchema.parse([]));
56
+ });
57
+ test("generated tool execution propagates typed patch errors", async () => {
58
+ const patchTarget = target("expected");
59
+ const generatedTool = patchTarget.toVercelTool();
60
+ await assert.rejects(() => executeTool(generatedTool, {
61
+ patches: [
62
+ {
63
+ operation: OperationType.Delete,
64
+ expectedLine: 1,
65
+ oldText: "wrong",
66
+ },
67
+ ],
68
+ }), (error) => {
69
+ assert.ok(error instanceof PatchError);
70
+ assert.equal(error.code, PatchErrorCode.AnchorMismatch);
71
+ assert.equal(error.patchIndex, 0);
72
+ return true;
73
+ });
74
+ assert.equal(patchTarget.content, "expected");
75
+ });
76
+ //# sourceMappingURL=Target.tool.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Target.tool.test.js","sourceRoot":"","sources":["../../../src/target/test/Target.tool.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ # Local Development
2
+
3
+ Node.js 22 or newer is required.
4
+
5
+ Install dependencies and run the complete local verification workflow:
6
+
7
+ ```sh
8
+ npm install
9
+ npm test
10
+ npm run typecheck
11
+ npm run build
12
+ npm run format:check
13
+ ```
14
+
15
+ This repository is a library and has no development server or application run command.
16
+
17
+ ## Package Scripts
18
+
19
+ - `npm test`: runs `src/target/test/*.test.ts` with Node's test runner and `tsx`.
20
+ - `npm run typecheck`: checks source and test TypeScript configurations without emitting output.
21
+ - `npm run build`: removes `dist/` and compiles JavaScript, declarations, and maps.
22
+ - `npm run clean`: removes generated `dist/` output.
23
+ - `npm run format`: applies Prettier formatting.
24
+ - `npm run format:check`: verifies formatting without modifying files.
25
+ - `npm run prepublishOnly`: runs typechecking and a clean build; it does not run tests or formatting checks.
26
+
27
+ Generated `dist/` files are not source authority. Public contracts come from `src/index.ts`, implementation and schemas under `src/`, tests, TypeScript
28
+ configuration, and `package.json`.
package/package.json ADDED
@@ -0,0 +1,67 @@
1
+ {
2
+ "name": "@romandmitri/ai-patch",
3
+ "version": "0.1.0",
4
+ "author": "Roman Eidenzon <romandmitri@gmail.com> (https://www.romandmitri.com)",
5
+ "description": "Validated, atomic text patches for precise AI and LLM edits to large text prompts/documents.",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/romandmitri/ai-patch.git"
9
+ },
10
+ "homepage": "https://github.com/romandmitri/ai-patch#readme",
11
+ "bugs": {
12
+ "url": "https://github.com/romandmitri/ai-patch/issues"
13
+ },
14
+ "keywords": [
15
+ "ai",
16
+ "llm",
17
+ "vercel-ai-sdk",
18
+ "markdown",
19
+ "text-patch",
20
+ "structured-output",
21
+ "zod"
22
+ ],
23
+ "type": "module",
24
+ "imports": {
25
+ "#src/*": "./dist/*"
26
+ },
27
+ "engines": {
28
+ "node": ">=22"
29
+ },
30
+ "exports": {
31
+ ".": {
32
+ "types": "./dist/index.d.ts",
33
+ "import": "./dist/index.js"
34
+ }
35
+ },
36
+ "main": "./dist/index.js",
37
+ "types": "./dist/index.d.ts",
38
+ "files": [
39
+ "dist",
40
+ "docs",
41
+ "README.md"
42
+ ],
43
+ "scripts": {
44
+ "clean": "rimraf dist",
45
+ "build": "npm run clean && tsc",
46
+ "format": "prettier --write .",
47
+ "format:check": "prettier --check .",
48
+ "test": "node --import tsx --test src/target/test/*.test.ts",
49
+ "typecheck": "tsc --noEmit && tsc --project tsconfig.test.json",
50
+ "prepublishOnly": "npm run typecheck && npm run build"
51
+ },
52
+ "publishConfig": {
53
+ "access": "public"
54
+ },
55
+ "dependencies": {
56
+ "ai": "7.0.87",
57
+ "zod": "4.5.4"
58
+ },
59
+ "devDependencies": {
60
+ "@types/json-schema": "7.0.15",
61
+ "@types/node": "26.4.0",
62
+ "prettier": "3.9.6",
63
+ "rimraf": "6.1.3",
64
+ "tsx": "4.23.13",
65
+ "typescript": "7.0.2"
66
+ }
67
+ }