ac-storage 0.16.2 → 0.16.4

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 (82) hide show
  1. package/afron/.vscode/launch.json +43 -0
  2. package/afron/.vscode/tasks.json +56 -0
  3. package/afron/LICENSE +674 -0
  4. package/afron/README.md +23 -0
  5. package/afron/_img/01.png +0 -0
  6. package/afron/_img/02.png +0 -0
  7. package/afron/package.json +40 -0
  8. package/afron/packages/chatai-models/package.json +34 -0
  9. package/afron/packages/core/package.json +65 -0
  10. package/afron/packages/core/vite.config.ts +17 -0
  11. package/afron/packages/electron/build/entitlements.mac.plist +33 -0
  12. package/afron/packages/electron/forge.config.js +66 -0
  13. package/afron/packages/electron/import-static.ps1 +14 -0
  14. package/afron/packages/electron/package.json +147 -0
  15. package/afron/packages/electron/rebuild.bat +1 -0
  16. package/afron/packages/front/.env.development +1 -0
  17. package/afron/packages/front/.github/copilot-instructions.md +88 -0
  18. package/afron/packages/front/eslint.config.js +33 -0
  19. package/afron/packages/front/index.html +13 -0
  20. package/afron/packages/front/package.json +74 -0
  21. package/afron/packages/front/public/favicon.icns +0 -0
  22. package/afron/packages/front/public/favicon.ico +0 -0
  23. package/afron/packages/front/public/favicon.png +0 -0
  24. package/afron/packages/front/public/robots.txt +3 -0
  25. package/afron/packages/front/public/vite.svg +1 -0
  26. package/afron/packages/front/tsconfig.app.json +44 -0
  27. package/afron/packages/front/tsconfig.node.json +24 -0
  28. package/afron/packages/front/vite.config.ts +53 -0
  29. package/afron/packages/locale/package.json +65 -0
  30. package/afron/packages/locale/vite.config.ts +17 -0
  31. package/afron/packages/types/index.js +3 -0
  32. package/afron/packages/types/package.json +6 -0
  33. package/afron/packages/types/types/chatai/chatai-model.d.ts +73 -0
  34. package/afron/packages/types/types/chatai/gemini-safety-setting.d.ts +15 -0
  35. package/afron/packages/types/types/chatai/index.d.ts +3 -0
  36. package/afron/packages/types/types/chatai/thinking-efforts.d.ts +4 -0
  37. package/afron/packages/types/types/event-pipe/global-event.d.ts +43 -0
  38. package/afron/packages/types/types/event-pipe/index.d.ts +1 -0
  39. package/afron/packages/types/types/index.d.ts +8 -0
  40. package/afron/packages/types/types/ipc/data.ts +114 -0
  41. package/afron/packages/types/types/ipc/declared.d.ts +1 -0
  42. package/afron/packages/types/types/ipc/index.ts +6 -0
  43. package/afron/packages/types/types/ipc/interface.d.ts +46 -0
  44. package/afron/packages/types/types/ipc/invokers.d.ts +181 -0
  45. package/afron/packages/types/types/ipc/listeners.d.ts +7 -0
  46. package/afron/packages/types/types/ipc/result.d.ts +3 -0
  47. package/afron/packages/types/types/ipc-invokers.d.ts +111 -0
  48. package/afron/packages/types/types/ipc-listeners.d.ts +6 -0
  49. package/afron/packages/types/types/prompt-form.d.ts +65 -0
  50. package/afron/packages/types/types/rt/event.d.ts +71 -0
  51. package/afron/packages/types/types/rt/flow.d.ts +63 -0
  52. package/afron/packages/types/types/rt/form.d.ts +41 -0
  53. package/afron/packages/types/types/rt/index.d.ts +4 -0
  54. package/afron/packages/types/types/rt/rt.d.ts +61 -0
  55. package/afron/packages/types/types/rt-var/index.ts +5 -0
  56. package/afron/packages/types/types/rt-var/rt-var-create.ts +39 -0
  57. package/afron/packages/types/types/rt-var/rt-var-stored.ts +30 -0
  58. package/afron/packages/types/types/rt-var/rt-var-update.ts +45 -0
  59. package/afron/packages/types/types/rt-var/rt-var.ts +36 -0
  60. package/afron/packages/types/types/rt-var/var-data.d.ts +108 -0
  61. package/afron/packages/types/types/storage-schema/ProfileStorageSchema.d.ts +2 -0
  62. package/afron/packages/types/types/storage-schema/RT/Flow.d.ts +20 -0
  63. package/afron/packages/types/types/storage-schema/RT/Metadata.d.ts +16 -0
  64. package/afron/packages/types/types/storage-schema/RT/Prompts.d.ts +39 -0
  65. package/afron/packages/types/types/storage-schema/RT/RT.d.ts +4 -0
  66. package/afron/packages/types/types/storage-schema/RT/index.d.ts +1 -0
  67. package/afron/packages/types/types/storage-schema/Session/Cache.d.ts +18 -0
  68. package/afron/packages/types/types/storage-schema/Session/Config.d.ts +7 -0
  69. package/afron/packages/types/types/storage-schema/Session/Data.d.ts +19 -0
  70. package/afron/packages/types/types/storage-schema/Session/Session.d.ts +3 -0
  71. package/afron/packages/types/types/storage-schema/Session/index.d.ts +1 -0
  72. package/afron/packages/types/types/storage-schema/index.d.ts +116 -0
  73. package/afron/packages/types/types/utils/index.ts +1 -0
  74. package/afron/packages/types/types.d.ts +3 -0
  75. package/afron/scripts/remove-dist.ps1 +10 -0
  76. package/afron/scripts/remove-install.ps1 +9 -0
  77. package/afron/scripts/win-vscode.ps1 +17 -0
  78. package/dist/bundle.cjs +43 -2
  79. package/dist/bundle.cjs.map +1 -1
  80. package/dist/bundle.mjs +43 -2
  81. package/dist/bundle.mjs.map +1 -1
  82. package/package.json +2 -2
@@ -0,0 +1,30 @@
1
+ /**
2
+ * backend에서 저장되는 형식
3
+ */
4
+ export type RTVarStored = (
5
+ RTVarStoredExternal
6
+ | RTVarStoredConstant
7
+ | RTVarStoredForm
8
+ | RTVarStoredUnknown
9
+ );
10
+
11
+ export type BaseRTVarStored = {
12
+ id: string;
13
+ name: string;
14
+ }
15
+
16
+ interface RTVarStoredExternal extends BaseRTVarStored {
17
+ include_type: 'external';
18
+ external_id: string;
19
+ }
20
+ interface RTVarStoredConstant extends BaseRTVarStored {
21
+ include_type: 'constant';
22
+ value: any;
23
+ }
24
+ type RTVarStoredForm = BaseRTVarStored & {
25
+ include_type: 'form';
26
+ form_id: string;
27
+ }
28
+ type RTVarStoredUnknown = BaseRTVarStored & {
29
+ include_type: 'unknown';
30
+ }
@@ -0,0 +1,45 @@
1
+ import { RTVarData } from './var-data';
2
+
3
+ /**
4
+ * RTVar 갱신 타입 정의
5
+ *
6
+ * id 필드의 유무에 따라 갱신 구분
7
+ */
8
+ export type RTVarUpdate = (
9
+ RTVarIncludePreserveUpdate
10
+ | RTVarExternalUpdate
11
+ | RTVarConstantUpdate
12
+ | RTVarFormUpdate
13
+ );
14
+
15
+ export type BaseRTVarUpdate = {
16
+ id: string;
17
+ name?: string;
18
+ }
19
+
20
+ export interface RTVarExternalUpdate extends BaseRTVarUpdate {
21
+ include_type: 'external';
22
+ external_id: string;
23
+ }
24
+ export interface RTVarConstantUpdate extends BaseRTVarUpdate {
25
+ include_type: 'constant';
26
+ value: any;
27
+ }
28
+ export type RTVarFormUpdate = BaseRTVarUpdate & {
29
+ include_type: 'form';
30
+ } & (
31
+ {
32
+ // form_id가 없는 경우 새로운 form 생성
33
+ form_id?: undefined | null;
34
+ form_name?: string;
35
+ data: RTVarData;
36
+ } | {
37
+ // form_id가 있으면 기존 form 갱신
38
+ form_id: string;
39
+ form_name?: string;
40
+ data?: RTVarData;
41
+ }
42
+ )
43
+ type RTVarIncludePreserveUpdate = BaseRTVarUpdate & {
44
+ include_type?: undefined;
45
+ }
@@ -0,0 +1,36 @@
1
+ import { RTVarData } from './var-data';
2
+
3
+ /**
4
+ * backend -> frontend 넘겨주는 RTVar 타입 정의
5
+ */
6
+ export type RTVar = (
7
+ RTVarExternal
8
+ | RTVarConstant
9
+ | RTVarForm
10
+ | RTVarUnknown
11
+ );
12
+
13
+ export type BaseRTVar = {
14
+ id: string;
15
+ name: string;
16
+ }
17
+
18
+ export interface RTVarExternal extends BaseRTVar {
19
+ include_type: 'external';
20
+ external_id: string;
21
+ }
22
+ export interface RTVarConstant extends BaseRTVar {
23
+ include_type: 'constant';
24
+ value: any;
25
+ }
26
+ export type RTVarForm = BaseRTVar & {
27
+ include_type: 'form';
28
+ form_id: string;
29
+ form_name: string;
30
+ // form_id가 가지고 있는 RTVarData 참조를 함께 리턴
31
+ data: RTVarData;
32
+ }
33
+ // 외부 소스가 사라진 경우 unknown으로 처리
34
+ export type RTVarUnknown = BaseRTVar & {
35
+ include_type: 'unknown';
36
+ }
@@ -0,0 +1,108 @@
1
+ export type RTVarDataNaive = {
2
+ type: 'text' | 'number' | 'checkbox' | 'select' | 'array' | 'struct';
3
+
4
+ config: {
5
+ text?: RTVarConfig.Text;
6
+ number?: RTVarConfig.Number;
7
+ checkbox?: RTVarConfig.Checkbox;
8
+ select?: RTVarConfig.Select;
9
+ array?: RTVarConfig.Array;
10
+ struct?: RTVarConfig.Struct;
11
+ }
12
+ }
13
+
14
+ export type RTVarDataType = RTVarData['type'];
15
+
16
+ /**
17
+ * RT 변수 데이터 타입 및 제한 정의
18
+ *
19
+ * RTForm 및 PromptVar에서 사용
20
+ */
21
+ export type RTVarData = {
22
+ type: 'text';
23
+ config: {
24
+ text: RTVarConfig.Text;
25
+ }
26
+ } | {
27
+ type: 'number';
28
+
29
+ config: {
30
+ number: RTVarConfig.Number;
31
+ }
32
+ } | {
33
+ type: 'checkbox';
34
+
35
+ config: {
36
+ checkbox: RTVarConfig.Checkbox;
37
+ }
38
+ } | {
39
+ type: 'select';
40
+
41
+ config: {
42
+ select: RTVarConfig.Select;
43
+ }
44
+ } | {
45
+ type: 'array';
46
+
47
+ config: {
48
+ array: RTVarConfig.Array;
49
+ }
50
+ } | {
51
+ type: 'struct';
52
+
53
+ config: {
54
+ struct: RTVarConfig.Struct;
55
+ }
56
+ };
57
+
58
+ export namespace RTVarConfig {
59
+ type Text = {
60
+ default_value?: string;
61
+ placeholder: string;
62
+ allow_multiline: boolean;
63
+ }
64
+ type Number = {
65
+ default_value?: number;
66
+ minimum_value?: number;
67
+ maximum_value?: number;
68
+ allow_decimal: boolean;
69
+ }
70
+ type Checkbox = {
71
+ default_value: boolean;
72
+ }
73
+ type Select = {
74
+ default_value?: string;
75
+ options: {
76
+ name: string;
77
+ value: string;
78
+ }[];
79
+ }
80
+ type Struct = {
81
+ fields: StructField[];
82
+ }
83
+ type StructField = {
84
+ type: 'text' | 'number' | 'checkbox' | 'select';
85
+ name: string;
86
+ display_name: string;
87
+ config: {
88
+ text?: Text;
89
+ number?: Number;
90
+ checkbox?: Checkbox;
91
+ select?: Select;
92
+ }
93
+ }
94
+ type Array = {
95
+ minimum_length?: number;
96
+ maximum_length?: number;
97
+ element_type: 'text' | 'number' | 'checkbox' | 'select' | 'struct';
98
+ config: {
99
+ text?: Text;
100
+ number?: Number;
101
+ checkbox?: Checkbox;
102
+ select?: Select;
103
+ struct?: Struct;
104
+ };
105
+ }
106
+ }
107
+
108
+ export { }
@@ -0,0 +1,2 @@
1
+ export { Session } from './Session';
2
+ export { RT } from './RT';
@@ -0,0 +1,20 @@
1
+ import { FlowNodeType } from '../../rt';
2
+
3
+ export type Flow = {
4
+ FlowNode: FlowNode;
5
+ }
6
+
7
+ export type FlowNode = {
8
+ type: FlowNodeType;
9
+ description: string;
10
+ data: Record<string, any>;
11
+ connection: Array<{
12
+ from_handle: string;
13
+ to_node: string;
14
+ to_handle: string;
15
+ }>;
16
+ position: {
17
+ x: number;
18
+ y: number;
19
+ };
20
+ }
@@ -0,0 +1,16 @@
1
+ export type Metadata = {
2
+ version: string;
3
+ id: string;
4
+ name: string;
5
+ uuid: string;
6
+ mode: 'flow' | 'prompt_only';
7
+ input_type: 'normal' | 'chat';
8
+ forms: string[];
9
+ entrypoint_node: number;
10
+ prompts: PromptOrder;
11
+ }
12
+
13
+ type PromptOrder = {
14
+ id: string;
15
+ name: string;
16
+ }[];
@@ -0,0 +1,39 @@
1
+ import { GeminiSafetySetting } from '../../chatai';
2
+
3
+ export type Prompts = {
4
+ id: string;
5
+ name: string
6
+ variables: PromptVar[];
7
+ constants: Array<{
8
+ name: string;
9
+ value: any;
10
+ }>;
11
+ model: {
12
+ stream: boolean;
13
+
14
+ top_p: number;
15
+ temperature: number;
16
+ max_tokens: number;
17
+ use_thinking: boolean;
18
+ thinking_tokens: number;
19
+ thinking_auto_budget: boolean;
20
+ thinking_effort: 'minimal' | 'low' | 'medium' | 'high';
21
+ verbosity: 'low' | 'medium' | 'high';
22
+
23
+ safety_settings: Record<GeminiSafetySetting.FilterNames, GeminiSafetySetting.Threshold>;
24
+ };
25
+ contents: string;
26
+ }
27
+
28
+ type PromptVar = {
29
+ /** 변수 유형, external/form 선택 시 외부 연결이 끊기면 unknown으로 지정됨 */
30
+ type: 'constant' | 'form' | 'external' | 'unknown';
31
+ id: string;
32
+ name: string;
33
+ weak?: boolean;
34
+
35
+ /** 'form'이면서 form_id가 없다면 id를 form_id로 대체 사용 (이전버전 호환성) */
36
+ form_id?: string;
37
+ external_id?: string;
38
+ value?: any;
39
+ }
@@ -0,0 +1,4 @@
1
+ export { RTFormNaive as Form } from '../../rt';
2
+ export { Prompts } from './Prompts';
3
+ export { Metadata } from './Metadata';
4
+ export { Flow } from './Flow';
@@ -0,0 +1 @@
1
+ export * as RT from './RT';
@@ -0,0 +1,18 @@
1
+ export interface Cache {
2
+ input: string;
3
+ output: string;
4
+ input_token_count: number;
5
+ last_history: unknown | null;
6
+ state: string;
7
+ markdown: boolean;
8
+ upload_files: SessionUploadFile[] | null;
9
+ }
10
+
11
+ interface SessionUploadFile {
12
+ filename: string;
13
+ data: string;
14
+ size: number;
15
+ type: 'image/webp' | 'image/png' | 'image/jpeg' | 'application/pdf' | 'text/plain' | string;
16
+ thumbnail: string | null;
17
+ hash_sha256: string;
18
+ }
@@ -0,0 +1,7 @@
1
+ export interface Config {
2
+ name: string;
3
+ color: string;
4
+ model_id: string;
5
+ rt_id: string;
6
+ delete_lock: boolean;
7
+ }
@@ -0,0 +1,19 @@
1
+
2
+ export interface Data {
3
+ forms: Record<string, Record<string, unknown>>;
4
+ custom_models: SessionCustomModel[];
5
+ running_rt: Record<string, SessionRunningRTEntry>;
6
+ }
7
+
8
+ interface SessionCustomModel {
9
+ name: string;
10
+ url: string;
11
+ api_format: 'chat_completions' | 'anthropic_claude' | 'generative_language';
12
+ secret_key: string | null;
13
+ }
14
+
15
+ interface SessionRunningRTEntry {
16
+ token: string;
17
+ created_at: number;
18
+ state: string;
19
+ }
@@ -0,0 +1,3 @@
1
+ export { Cache } from './Cache';
2
+ export { Config } from './Config';
3
+ export { Data } from './Data';
@@ -0,0 +1 @@
1
+ export * as Session from './Session';
@@ -0,0 +1,116 @@
1
+ import { GeminiSafetySetting, SupportedThinkingEfforts, SupportedVerbosity } from '../chatai';
2
+ import { FlowNodeType, RTFormNaive } from '../rt';
3
+
4
+ export * as ProfileStorageSchema from './ProfileStorageSchema';
5
+
6
+ export declare namespace ProfileStorage {
7
+ namespace RT {
8
+ type Index = {
9
+ version: string;
10
+ id: string;
11
+ name: string;
12
+ uuid: string;
13
+ mode: 'flow' | 'prompt_only';
14
+ input_type: 'normal' | 'chat';
15
+ forms: string[];
16
+ entrypoint_node: number;
17
+ prompts: PromptOrder;
18
+ }
19
+
20
+ // form.json 내 { [string]: Form } 형식의 Form에 해당
21
+ type Form = RTFormNaive;
22
+
23
+ // request-template.<rt-id>.prompt.<prompt-id>
24
+ type Prompt = {
25
+ id: string;
26
+ name: string
27
+ variables: PromptVar[];
28
+ constants: Array<{
29
+ name: string;
30
+ value: any;
31
+ }>;
32
+ model: {
33
+ stream: boolean;
34
+
35
+ top_p: number;
36
+ temperature: number;
37
+ max_tokens: number;
38
+ use_thinking: boolean;
39
+ thinking_tokens: number;
40
+ thinking_auto_budget: boolean;
41
+ thinking_effort: 'minimal' | 'low' | 'medium' | 'high';
42
+ verbosity: 'low' | 'medium' | 'high';
43
+
44
+ safety_settings: Record<GeminiSafetySetting.FilterNames, GeminiSafetySetting.Threshold>;
45
+ };
46
+ contents: string;
47
+ }
48
+
49
+ type PromptVar = {
50
+ /** 변수 유형, external/form 선택 시 외부 연결이 끊기면 unknown으로 지정됨 */
51
+ type: 'constant' | 'form' | 'external' | 'unknown';
52
+ id: string;
53
+ name: string;
54
+ weak?: boolean;
55
+
56
+ /** 'form'이면서 form_id가 없다면 id를 form_id로 대체 사용 (이전버전 호환성) */
57
+ form_id?: string;
58
+ external_id?: string;
59
+ value?: any;
60
+ }
61
+
62
+ type FlowNode = {
63
+ type: FlowNodeType;
64
+ description: string;
65
+ data: Record<string, any>;
66
+ connection: Array<{
67
+ from_handle: string;
68
+ to_node: string;
69
+ to_handle: string;
70
+ }>;
71
+ position: {
72
+ x: number;
73
+ y: number;
74
+ };
75
+ }
76
+
77
+ type PromptOrder = {
78
+ id: string;
79
+ name: string;
80
+ }[];
81
+ }
82
+ }
83
+
84
+ export type ModelConfiguration = {
85
+ stream?: boolean;
86
+
87
+ temperature?: number;
88
+ top_p?: number;
89
+ max_tokens?: number;
90
+
91
+ use_thinking?: boolean;
92
+ thinking_auto_budget?: boolean;
93
+ thinking_tokens?: number;
94
+ thinking_effort?: SupportedThinkingEfforts;
95
+ thinking_summary?: boolean;
96
+ verbosity?: SupportedVerbosity;
97
+
98
+ safety_settings?: Partial<Record<GeminiSafetySetting.FilterNames, GeminiSafetySetting.Threshold>>;
99
+ }
100
+
101
+ export type GlobalModelConfiguration = {
102
+ /**
103
+ * 전역 설정에서 기존 설정을 덮어쓰는지 여부
104
+ *
105
+ * 전역 설정이 아니라면 무시됨
106
+ */
107
+ override_enabled?: boolean;
108
+
109
+ override_common?: boolean;
110
+ override_thinking?: boolean;
111
+ override_safety_settings?: boolean;
112
+ override_gpt5?: boolean;
113
+ } & ModelConfiguration;
114
+
115
+
116
+ export { };
@@ -0,0 +1 @@
1
+ export type Optional<T> = T | undefined | null;
@@ -0,0 +1,3 @@
1
+ import './types/ipc/interface'
2
+
3
+ export {};
@@ -0,0 +1,10 @@
1
+ if ((Split-Path -Leaf (Get-Location)) -eq "scripts") {
2
+ cd ..
3
+ }
4
+
5
+ Get-ChildItem -Path ".\packages" -Recurse -Directory -Filter "dist" | Remove-Item -Recurse -Force
6
+
7
+
8
+ if (Test-Path ".\packages\electron\static") {
9
+ Remove-Item ".\packages\electron\static" -Recurse -Force
10
+ }
@@ -0,0 +1,9 @@
1
+ if ((Split-Path -Leaf (Get-Location)) -eq "scripts") {
2
+ cd ..
3
+ }
4
+
5
+ Get-ChildItem -Path ".\packages" -Recurse -Directory -Filter "node_modules" | Remove-Item -Recurse -Force
6
+
7
+ if (Test-Path ".\node_modules") {
8
+ Remove-Item ".\node_modules" -Recurse -Force
9
+ }
@@ -0,0 +1,17 @@
1
+ if ((Split-Path -Leaf (Get-Location)) -eq "scripts") {
2
+ cd ..
3
+ }
4
+
5
+ Get-Desktop 0 | Switch-Desktop
6
+ code ".\"
7
+ Start-Sleep -Seconds 3
8
+
9
+ Get-Desktop 1 | Switch-Desktop
10
+ code ".\packages\front"
11
+ Start-Sleep -Seconds 3
12
+
13
+ Get-Desktop 2 | Switch-Desktop
14
+ code ".\packages\core"
15
+ Start-Sleep -Seconds 3
16
+
17
+ Get-Desktop 0 | Switch-Desktop
package/dist/bundle.cjs CHANGED
@@ -114,6 +114,18 @@ class StructJSONTypeLeaf extends BaseJSONTypeLeaf {
114
114
  return this;
115
115
  }
116
116
  }
117
+ class ReplaceJSONTypeLeaf extends BaseJSONTypeLeaf {
118
+ __brand = 'replace';
119
+ constructor(tree) {
120
+ super('replace');
121
+ this.value['replace'] = tree;
122
+ this.value['strict'] = false;
123
+ }
124
+ strict() {
125
+ this.value['strict'] = true;
126
+ return this;
127
+ }
128
+ }
117
129
 
118
130
  class ArrayJSONTypeLeaf extends BaseJSONTypeLeaf {
119
131
  __brand = 'array';
@@ -166,6 +178,7 @@ const JSONType = {
166
178
  Number: () => new NumberJSONTypeLeaf(),
167
179
  Bool: () => new BooleanJSONTypeLeaf(),
168
180
  Struct: (tree) => new StructJSONTypeLeaf(tree),
181
+ Replace: (tree) => new ReplaceJSONTypeLeaf(tree),
169
182
  Array: (jsonTree) => new ArrayJSONTypeLeaf(jsonTree),
170
183
  Any: () => new BaseJSONTypeLeaf('any'),
171
184
  };
@@ -290,8 +303,8 @@ class Flattener {
290
303
  const { tracePath } = traceResult;
291
304
  const reached = tracePath.join(DELIMITER);
292
305
  const node = this.navigate.get(reached);
293
- // 닿을 수 있는 마지막 노드의 타입이 struct, any 라면 허용됨
294
- if (!node || (node.type !== 'struct' && node.type !== 'any')) {
306
+ // 닿을 수 있는 마지막 노드의 타입이 struct, replace, any 라면 허용됨
307
+ if (!node || (node.type !== 'struct' && node.type !== 'replace' && node.type !== 'any')) {
295
308
  throw new JSONAccessorError(`Field '${key}' is not allowed`);
296
309
  }
297
310
  return [[key, value]];
@@ -329,6 +342,10 @@ class Flattener {
329
342
  structData: node,
330
343
  });
331
344
  }
345
+ else if (node.type === 'replace') {
346
+ // replace 타입은 평탄화 없이 전체 값 반환 → 항상 덮어쓰기
347
+ return [[newKey, value]];
348
+ }
332
349
  else if (node.type === 'array') {
333
350
  // @TODO : 배열 타입에 대한 세부 처리 필요
334
351
  // 현재는 배열 경로까지만 flat이 이루어지므로
@@ -452,6 +469,10 @@ class CompatibilityChecker {
452
469
  return true;
453
470
  }
454
471
  }
472
+ // replace 타입은 실제 값이 struct(객체)로 들어옴
473
+ else if (jsonTypeData.type === 'replace' && targetType === 'struct') {
474
+ return this.isReplaceCompatible(target, jsonTypeData);
475
+ }
455
476
  else {
456
477
  return false;
457
478
  }
@@ -482,6 +503,21 @@ class CompatibilityChecker {
482
503
  return false;
483
504
  }
484
505
  }
506
+ isReplaceCompatible(struct, replaceTypeData) {
507
+ if (!replaceTypeData.strict)
508
+ return true;
509
+ if (!replaceTypeData.replace)
510
+ return true;
511
+ const navigate = r$1.from(replaceTypeData.replace);
512
+ const flattener = new Flattener(navigate, this);
513
+ try {
514
+ flattener.flat({ target: struct, prefix: '' });
515
+ return true;
516
+ }
517
+ catch (e) {
518
+ return false;
519
+ }
520
+ }
485
521
  getUnionTypeNames(union) {
486
522
  return union.candidates.map((c) => {
487
523
  if (typeof c === 'object') {
@@ -756,6 +792,11 @@ class JSONAccessor {
756
792
  this.#changed = true;
757
793
  this.#removeData(key);
758
794
  }
795
+ replaceOne(key, value) {
796
+ this.#ensureNotDropped();
797
+ this.#removeData(key);
798
+ this.set([[key, value]]);
799
+ }
759
800
  remove(keys) {
760
801
  this.#ensureNotDropped();
761
802
  this.#changed = true;