arcane-os 0.1.0-dev.5
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.
- package/CHANGELOG.md +154 -0
- package/COMMERCIAL-LICENSE.md +13 -0
- package/LICENSE +661 -0
- package/NOTICE +70 -0
- package/README.md +448 -0
- package/bin/arcane-test.mjs +741 -0
- package/bin/arcane.mjs +5 -0
- package/docs/architecture.md +234 -0
- package/docs/compatibility.md +36 -0
- package/docs/event-manager.md +166 -0
- package/docs/platform-targets.md +108 -0
- package/docs/publishing.md +203 -0
- package/docs/reference/README.md +117 -0
- package/docs/reference/arcane-ollama.md +288 -0
- package/docs/reference/availability-and-normalization.md +123 -0
- package/docs/reference/behavioral-testing.md +86 -0
- package/docs/reference/cli.md +569 -0
- package/docs/reference/core/README.md +62 -0
- package/docs/reference/core/arcane-ai-contracts.md +873 -0
- package/docs/reference/core/arcane-api.md +601 -0
- package/docs/reference/core/arcane-entities.md +65 -0
- package/docs/reference/core/arcane-events.md +134 -0
- package/docs/reference/core/ollama-module.md +181 -0
- package/docs/reference/core/reference/arcane-api/ai-and-ollama.md +1909 -0
- package/docs/reference/core/reference/arcane-api/applications-terminal-capabilities.md +1057 -0
- package/docs/reference/core/reference/arcane-api/core-and-events.md +320 -0
- package/docs/reference/core/reference/arcane-api/filesystem-storage-preferences-appearance.md +610 -0
- package/docs/reference/core/reference/arcane-api/namespaces.md +1157 -0
- package/docs/reference/core/reference/arcane-api/platform-installation-users-system.md +1423 -0
- package/docs/reference/core/reference/arcane-api/session-provisioning-diagnostics-development.md +315 -0
- package/docs/reference/event-manager.md +957 -0
- package/docs/reference/inventory/package-api.json +2632 -0
- package/docs/reference/inventory/runtime-components.json +934 -0
- package/docs/reference/inventory/runtime-entities.json +26 -0
- package/docs/reference/inventory/runtime-modules.json +1249 -0
- package/docs/reference/protocols.md +242 -0
- package/docs/reference/runtime-components.md +1098 -0
- package/docs/reference/runtime-entities.md +303 -0
- package/docs/reference/runtime-modules.md +2010 -0
- package/docs/reference/sdk-api.md +4901 -0
- package/docs/roadmap.md +79 -0
- package/docs/work-amplification.md +124 -0
- package/node_modules/event-pubsub/CHANGELOG.md +55 -0
- package/node_modules/event-pubsub/MIGRATION.md +70 -0
- package/node_modules/event-pubsub/README.md +363 -0
- package/node_modules/event-pubsub/SECURITY.md +37 -0
- package/node_modules/event-pubsub/index.js +141 -0
- package/node_modules/event-pubsub/licence +21 -0
- package/node_modules/event-pubsub/package.json +59 -0
- package/node_modules/strong-type/README.md +408 -0
- package/node_modules/strong-type/assets/strong-type-header.png +0 -0
- package/node_modules/strong-type/index.js +1151 -0
- package/node_modules/strong-type/licence +21 -0
- package/node_modules/strong-type/node.js +125 -0
- package/node_modules/strong-type/package.json +61 -0
- package/package.json +95 -0
- package/runtime/ARCANE_RUNTIME_RELEASE.json +791 -0
- package/runtime/arcane/components/app-bar.html +468 -0
- package/runtime/arcane/components/assistant-panel.html +715 -0
- package/runtime/arcane/components/calculator.html +8 -0
- package/runtime/arcane/components/chart.html +655 -0
- package/runtime/arcane/components/chat.html +1225 -0
- package/runtime/arcane/components/conversation-view.html +13 -0
- package/runtime/arcane/components/dashboard-config.html +341 -0
- package/runtime/arcane/components/data-maintenance.html +112 -0
- package/runtime/arcane/components/data-view.html +92 -0
- package/runtime/arcane/components/directory-picker.html +197 -0
- package/runtime/arcane/components/document-inspector.html +252 -0
- package/runtime/arcane/components/file-drop.html +264 -0
- package/runtime/arcane/components/file-inspector.html +293 -0
- package/runtime/arcane/components/file-manager.html +1715 -0
- package/runtime/arcane/components/header.html +142 -0
- package/runtime/arcane/components/integration-settings.html +14 -0
- package/runtime/arcane/components/local-ai-status.html +360 -0
- package/runtime/arcane/components/markdown-document.html +1048 -0
- package/runtime/arcane/components/markdown-editor.html +360 -0
- package/runtime/arcane/components/media-embed.html +8 -0
- package/runtime/arcane/components/modal.html +402 -0
- package/runtime/arcane/components/output-panel.html +259 -0
- package/runtime/arcane/components/preferences-form.html +135 -0
- package/runtime/arcane/components/record-timeline.html +105 -0
- package/runtime/arcane/components/relationship-board.html +116 -0
- package/runtime/arcane/components/screen-capture.html +8 -0
- package/runtime/arcane/components/source-code-viewer.html +441 -0
- package/runtime/arcane/components/source-explanation.html +124 -0
- package/runtime/arcane/components/speech.html +365 -0
- package/runtime/arcane/components/summary-strip.html +177 -0
- package/runtime/arcane/components/table.html +77 -0
- package/runtime/arcane/components/task-progress.html +282 -0
- package/runtime/arcane/components/terminal-workspace.html +65 -0
- package/runtime/arcane/components/theme-editor.html +41 -0
- package/runtime/arcane/components/theme-switcher.html +46 -0
- package/runtime/arcane/components/unified-inbox.html +20 -0
- package/runtime/arcane/components/voice-transcription.html +476 -0
- package/runtime/arcane/components/weather-widget.html +8 -0
- package/runtime/arcane/components/web-navigator.html +239 -0
- package/runtime/arcane/css/communications.css +1 -0
- package/runtime/arcane/css/dashboard-config.css +45 -0
- package/runtime/arcane/css/document-site.css +981 -0
- package/runtime/arcane/css/layout.css +438 -0
- package/runtime/arcane/css/primitives.css +321 -0
- package/runtime/arcane/css/theme.css +112 -0
- package/runtime/arcane/css/utility-workspace.css +1 -0
- package/runtime/arcane/entities/ApiModelRecord.js +20 -0
- package/runtime/arcane/entities/Calculation.js +13 -0
- package/runtime/arcane/entities/Chat.js +581 -0
- package/runtime/arcane/entities/CommunicationMessage.js +29 -0
- package/runtime/arcane/entities/CommunicationThread.js +21 -0
- package/runtime/arcane/entities/Document.js +10 -0
- package/runtime/arcane/entities/File.js +143 -0
- package/runtime/arcane/entities/Image.js +135 -0
- package/runtime/arcane/entities/IntentEnvelope.js +834 -0
- package/runtime/arcane/entities/Preference.js +83 -0
- package/runtime/arcane/entities/TWiNPolicyDecision.js +1092 -0
- package/runtime/arcane/entities/TerminalSession.js +46 -0
- package/runtime/arcane/entities/Theme.js +107 -0
- package/runtime/arcane/entities/User.js +1046 -0
- package/runtime/arcane/entities/Weather.js +23 -0
- package/runtime/arcane/img/arcane-os-everywhere.png +0 -0
- package/runtime/arcane/img/arrow-left.png +0 -0
- package/runtime/arcane/img/arrow-right.png +0 -0
- package/runtime/arcane/img/doc.svg +5 -0
- package/runtime/arcane/img/folder.svg +4 -0
- package/runtime/arcane/img/image.svg +5 -0
- package/runtime/arcane/img/refresh.png +0 -0
- package/runtime/arcane/img/send.svg +9 -0
- package/runtime/arcane/img/trash.svg +5 -0
- package/runtime/arcane/img/upload.svg +5 -0
- package/runtime/arcane/modules/AI.js +2048 -0
- package/runtime/arcane/modules/AIPreferenceRuntime.js +32 -0
- package/runtime/arcane/modules/AIPreferenceTuple.js +92 -0
- package/runtime/arcane/modules/AIResponseLength.js +42 -0
- package/runtime/arcane/modules/AIResponseURLPolicy.js +626 -0
- package/runtime/arcane/modules/AnsiText.js +53 -0
- package/runtime/arcane/modules/ApiModelDatabase.js +25 -0
- package/runtime/arcane/modules/AppDataScope.js +245 -0
- package/runtime/arcane/modules/AppearancePreferences.js +28 -0
- package/runtime/arcane/modules/ArcaneCommunicationBridge.js +22 -0
- package/runtime/arcane/modules/ArcaneNavigationPolicy.js +135 -0
- package/runtime/arcane/modules/ArcaneNetworkPolicy.js +255 -0
- package/runtime/arcane/modules/AsyncBoundary.js +161 -0
- package/runtime/arcane/modules/BrowserTestSuite.js +326 -0
- package/runtime/arcane/modules/CalculatorEngine.js +20 -0
- package/runtime/arcane/modules/CaseEvidenceIndexer.js +134 -0
- package/runtime/arcane/modules/ChartLibrary.js +35 -0
- package/runtime/arcane/modules/ChatRecords.js +13 -0
- package/runtime/arcane/modules/CommunicationAppController.js +43 -0
- package/runtime/arcane/modules/CommunicationHub.js +16 -0
- package/runtime/arcane/modules/CommunicationPreferences.js +13 -0
- package/runtime/arcane/modules/CommunicationProviderRegistry.js +16 -0
- package/runtime/arcane/modules/ComponentContracts.js +586 -0
- package/runtime/arcane/modules/ConfiguredAIChatSession.js +288 -0
- package/runtime/arcane/modules/ConversationActionItems.js +488 -0
- package/runtime/arcane/modules/ConversationClosingReport.js +274 -0
- package/runtime/arcane/modules/ConversationTimebox.js +527 -0
- package/runtime/arcane/modules/CoreLocalModelCatalog.js +255 -0
- package/runtime/arcane/modules/DBLS.js +171 -0
- package/runtime/arcane/modules/DBOPFS.js +1154 -0
- package/runtime/arcane/modules/DBOPFSWorker.js +116 -0
- package/runtime/arcane/modules/DataMaintenance.js +91 -0
- package/runtime/arcane/modules/DevelopmentWorkspace.js +74 -0
- package/runtime/arcane/modules/DirectoryPicker.js +109 -0
- package/runtime/arcane/modules/DocumentNavigation.js +223 -0
- package/runtime/arcane/modules/Errors.js +1025 -0
- package/runtime/arcane/modules/GifEncoder.js +29 -0
- package/runtime/arcane/modules/HTMLImport.js +114 -0
- package/runtime/arcane/modules/InMemoryCommunicationProvider.js +14 -0
- package/runtime/arcane/modules/IsolatedModelQuestionRunner.js +275 -0
- package/runtime/arcane/modules/LocalAIReadiness.js +870 -0
- package/runtime/arcane/modules/LocalAIReadinessController.js +156 -0
- package/runtime/arcane/modules/MD.js +111 -0
- package/runtime/arcane/modules/Mail.js +352 -0
- package/runtime/arcane/modules/MailTransport.mjs +180 -0
- package/runtime/arcane/modules/Marked.min.js +71 -0
- package/runtime/arcane/modules/MemoryRecords.js +44 -0
- package/runtime/arcane/modules/MessageAdvisory.js +38 -0
- package/runtime/arcane/modules/ModelDefinition.js +189 -0
- package/runtime/arcane/modules/Ollama.js +74 -0
- package/runtime/arcane/modules/OllamaModelIdentifier.js +21 -0
- package/runtime/arcane/modules/OllamaSettings.js +24 -0
- package/runtime/arcane/modules/OpenMeteoWeatherProvider.js +14 -0
- package/runtime/arcane/modules/PreferenceStore.js +109 -0
- package/runtime/arcane/modules/QRCode.min.js +1 -0
- package/runtime/arcane/modules/Questionnaire.js +61 -0
- package/runtime/arcane/modules/RecordLinkIndex.js +24 -0
- package/runtime/arcane/modules/RecordPassageIndex.js +222 -0
- package/runtime/arcane/modules/RecordReviewStore.js +94 -0
- package/runtime/arcane/modules/RevocableProjectionLedger.js +1623 -0
- package/runtime/arcane/modules/RiskSignalAnalyzer.js +37 -0
- package/runtime/arcane/modules/ScamRiskPolicy.js +62 -0
- package/runtime/arcane/modules/ScopedOPFSCache.js +183 -0
- package/runtime/arcane/modules/ScreenCapture.js +20 -0
- package/runtime/arcane/modules/SpeechPlayback.js +581 -0
- package/runtime/arcane/modules/StaticDocumentCatalog.js +1248 -0
- package/runtime/arcane/modules/SystemAppearance.js +20 -0
- package/runtime/arcane/modules/SystemPlatformPresentation.js +51 -0
- package/runtime/arcane/modules/SystemToolRegistry.js +28 -0
- package/runtime/arcane/modules/TerminalClient.js +52 -0
- package/runtime/arcane/modules/TerminalCommandRegistry.js +50 -0
- package/runtime/arcane/modules/ThemeBootstrap.js +26 -0
- package/runtime/arcane/modules/ThemeManager.js +131 -0
- package/runtime/arcane/modules/TimeGuard.js +149 -0
- package/runtime/arcane/modules/ToolCallRouter.js +83 -0
- package/runtime/arcane/modules/WaitForComponent.js +102 -0
- package/runtime/arcane/modules/YouTubeMedia.js +16 -0
- package/runtime/arcane/modules/uPlot.LICENSE.txt +21 -0
- package/runtime/arcane/modules/uPlot.iife.min.js +2 -0
- package/runtime/arcane/modules/uPlot.min.css +1 -0
- package/runtime/arcane/security/arcane-network-policy.json +6 -0
- package/runtime/strong-type/index.js +352 -0
- package/runtime/strong-type/licence +21 -0
- package/runtime/strong-type/package.json +45 -0
- package/schemas/arcane-app-bundle.schema.json +125 -0
- package/schemas/arcane-app.schema.json +329 -0
- package/schemas/arcane-lock.schema.json +86 -0
- package/schemas/arcane-package.schema.json +224 -0
- package/schemas/cli-event.schema.json +122 -0
- package/schemas/event-stack.schema.json +152 -0
- package/schemas/native-build-plan.schema.json +176 -0
- package/schemas/target-adapter.schema.json +117 -0
- package/src/app-descriptor.mjs +500 -0
- package/src/cli/main.mjs +561 -0
- package/src/constants.mjs +31 -0
- package/src/dev-server.mjs +718 -0
- package/src/doctor.mjs +315 -0
- package/src/dom-event-instrumentation.mjs +594 -0
- package/src/errors.mjs +75 -0
- package/src/event-manager.mjs +1342 -0
- package/src/event-queue.mjs +138 -0
- package/src/events.mjs +219 -0
- package/src/index.mjs +177 -0
- package/src/integrated-provider-loader.mjs +432 -0
- package/src/native-plan.mjs +698 -0
- package/src/native-provider-loader.mjs +1126 -0
- package/src/packager/core.mjs +2691 -0
- package/src/process.mjs +353 -0
- package/src/release-bundle.mjs +2523 -0
- package/src/repository.mjs +90 -0
- package/src/runtime.mjs +452 -0
- package/src/scaffold.mjs +380 -0
- package/src/targets/index.mjs +436 -0
- package/src/templates/assets/app-icon.png +0 -0
- package/src/templates/workspace-template.mjs +388 -0
- package/src/testing-loader.mjs +9 -0
- package/src/testing.mjs +427 -0
- package/src/toolchain.mjs +1335 -0
- package/src/update-check.mjs +307 -0
- package/src/workspace.mjs +449 -0
|
@@ -0,0 +1,626 @@
|
|
|
1
|
+
import {marked} from './Marked.min.js';
|
|
2
|
+
|
|
3
|
+
const BARE_HTTP_URL_PATTERN=/\bhttps?:\/\/[^\s<>\[\]"'`]+/giu;
|
|
4
|
+
const BARE_WWW_URL_PATTERN=/\bwww\.[^\s<>\[\]"'`]+/giu;
|
|
5
|
+
const BARE_EMAIL_PATTERN=/\b[A-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?(?:\.[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?)+\b/giu;
|
|
6
|
+
const HTML_LINK_ATTRIBUTE_PATTERN=/\b(?:href|src|action|formaction|poster|data)\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s"'=<>`]+))/giu;
|
|
7
|
+
const HTML_SRCSET_ATTRIBUTE_PATTERN=/\bsrcset\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s"'=<>`]+))/giu;
|
|
8
|
+
const CSS_URL_PATTERN=/\burl\(\s*(?:"([^"]*)"|'([^']*)'|([^)]*))\s*\)/giu;
|
|
9
|
+
const MARKDOWN_AUTOLINK_PATTERN=/<((?:[a-z][a-z0-9+.-]{1,31}):[^<>\s]+|[^<>\s@]+@[^<>\s@]+\.[^<>\s@]+)>/giu;
|
|
10
|
+
const MARKDOWN_ESCAPED_PUNCTUATION_PATTERN=
|
|
11
|
+
/\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/gu;
|
|
12
|
+
const RENDERED_BLOCK_BOUNDARY_PATTERN=
|
|
13
|
+
/<\/?(?:address|article|aside|blockquote|br|caption|dd|details|dialog|div|dl|dt|fieldset|figcaption|figure|footer|form|h[1-6]|header|hr|li|main|nav|ol|p|pre|section|summary|table|tbody|td|tfoot|th|thead|tr|ul)\b[^>]*>/giu;
|
|
14
|
+
const RENDERED_TAG_PATTERN=/<[^>]*>/gu;
|
|
15
|
+
const INVISIBLE_FORMATTING_PATTERN=/\p{Default_Ignorable_Code_Point}+/gu;
|
|
16
|
+
const INVISIBLE_TEXT_CONTROL_PATTERN=
|
|
17
|
+
/[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f-\u009f]+/gu;
|
|
18
|
+
|
|
19
|
+
function decodeNamedCharacterReference(match,name){
|
|
20
|
+
const normalized=String(name).toLowerCase();
|
|
21
|
+
const values={
|
|
22
|
+
af:'\u2061',
|
|
23
|
+
amp:'&',
|
|
24
|
+
applyfunction:'\u2061',
|
|
25
|
+
apos:"'",
|
|
26
|
+
ast:'*',
|
|
27
|
+
bsol:'\\',
|
|
28
|
+
colon:':',
|
|
29
|
+
comma:',',
|
|
30
|
+
commat:'@',
|
|
31
|
+
dollar:'$',
|
|
32
|
+
equals:'=',
|
|
33
|
+
excl:'!',
|
|
34
|
+
gt:'>',
|
|
35
|
+
ic:'\u2063',
|
|
36
|
+
invisiblecomma:'\u2063',
|
|
37
|
+
invisibletimes:'\u2062',
|
|
38
|
+
it:'\u2062',
|
|
39
|
+
lpar:'(',
|
|
40
|
+
lrm:'\u200e',
|
|
41
|
+
lt:'<',
|
|
42
|
+
lowbar:'_',
|
|
43
|
+
negativemediumspace:'\u200b',
|
|
44
|
+
negativethickspace:'\u200b',
|
|
45
|
+
negativethinspace:'\u200b',
|
|
46
|
+
negativeverythinspace:'\u200b',
|
|
47
|
+
nobreak:'\u2060',
|
|
48
|
+
num:'#',
|
|
49
|
+
percnt:'%',
|
|
50
|
+
period:'.',
|
|
51
|
+
plus:'+',
|
|
52
|
+
quest:'?',
|
|
53
|
+
quot:'"',
|
|
54
|
+
rlm:'\u200f',
|
|
55
|
+
rpar:')',
|
|
56
|
+
semi:';',
|
|
57
|
+
shy:'\u00ad',
|
|
58
|
+
sol:'/',
|
|
59
|
+
vert:'|',
|
|
60
|
+
zerowidthspace:'\u200b',
|
|
61
|
+
zwj:'\u200d',
|
|
62
|
+
zwnj:'\u200c'
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
return Object.hasOwn(values,normalized)?values[normalized]:match;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function decodeNumericCharacterReference(match,hexadecimal,decimal){
|
|
69
|
+
const raw=hexadecimal||decimal;
|
|
70
|
+
const radix=hexadecimal?16:10;
|
|
71
|
+
const codePoint=Number.parseInt(raw,radix);
|
|
72
|
+
|
|
73
|
+
if(
|
|
74
|
+
!Number.isInteger(codePoint)
|
|
75
|
+
||codePoint<1
|
|
76
|
+
||codePoint>0x10FFFF
|
|
77
|
+
||(codePoint>=0xD800&&codePoint<=0xDFFF)
|
|
78
|
+
){
|
|
79
|
+
return match;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return String.fromCodePoint(codePoint);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function decodeHTMLCharacterReferences(value=''){
|
|
86
|
+
return String(value).replace(
|
|
87
|
+
/&(?:(af|amp|applyfunction|apos|ast|bsol|colon|comma|commat|dollar|equals|excl|gt|ic|invisiblecomma|invisibletimes|it|lpar|lrm|lt|lowbar|negativemediumspace|negativethickspace|negativethinspace|negativeverythinspace|nobreak|num|percnt|period|plus|quest|quot|rlm|rpar|semi|shy|sol|vert|zerowidthspace|zwj|zwnj);|#x([0-9a-f]+);?|#([0-9]+);?)/gi,
|
|
88
|
+
function decodeOneCharacterReference(match,name,hexadecimal,decimal){
|
|
89
|
+
return name
|
|
90
|
+
?decodeNamedCharacterReference(match,name)
|
|
91
|
+
:decodeNumericCharacterReference(
|
|
92
|
+
match,
|
|
93
|
+
hexadecimal,
|
|
94
|
+
decimal
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function countCharacter(value,character){
|
|
101
|
+
let count=0;
|
|
102
|
+
|
|
103
|
+
for(const candidate of value){
|
|
104
|
+
if(candidate===character){
|
|
105
|
+
count++;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return count;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function trimUnbalancedClosingCharacters(value=''){
|
|
113
|
+
let result=String(value);
|
|
114
|
+
const pairs=[
|
|
115
|
+
['(',')'],
|
|
116
|
+
['[',']'],
|
|
117
|
+
['{','}']
|
|
118
|
+
];
|
|
119
|
+
|
|
120
|
+
for(const [opening,closing] of pairs){
|
|
121
|
+
while(
|
|
122
|
+
result.endsWith(closing)
|
|
123
|
+
&&countCharacter(result,closing)>countCharacter(result,opening)
|
|
124
|
+
){
|
|
125
|
+
result=result.slice(0,-1);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return result;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function trimBareLinkCandidate(value=''){
|
|
133
|
+
let result=decodeHTMLCharacterReferences(value).trim();
|
|
134
|
+
|
|
135
|
+
result=trimUnbalancedClosingCharacters(result);
|
|
136
|
+
while(/[.,;:!?]$/u.test(result)){
|
|
137
|
+
result=result.slice(0,-1);
|
|
138
|
+
result=trimUnbalancedClosingCharacters(result);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return result;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function normalizeAIResponseLink(value='',{bare=false}={}){
|
|
145
|
+
let result=bare
|
|
146
|
+
?trimBareLinkCandidate(value)
|
|
147
|
+
:decodeHTMLCharacterReferences(value).trim();
|
|
148
|
+
|
|
149
|
+
if(result.startsWith('<')&&result.endsWith('>')){
|
|
150
|
+
result=result.slice(1,-1).trim();
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
if(/^mailto:/iu.test(result)){
|
|
154
|
+
return `mailto:${result.slice(7)}`;
|
|
155
|
+
}
|
|
156
|
+
if(/^tel:/iu.test(result)){
|
|
157
|
+
return `tel:${result.slice(4)}`;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
return result;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function normalizeMarkdownDestination(value=''){
|
|
164
|
+
return String(value).replace(
|
|
165
|
+
MARKDOWN_ESCAPED_PUNCTUATION_PATTERN,
|
|
166
|
+
'$1'
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function extractMarkdownInlineDestination(text='',openingIndex=0){
|
|
171
|
+
let index=openingIndex+2;
|
|
172
|
+
|
|
173
|
+
while(index<text.length&&/\s/u.test(text[index])){
|
|
174
|
+
index++;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
if(index>=text.length){
|
|
178
|
+
return null;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
if(text[index]==='<'){
|
|
182
|
+
const end=text.indexOf('>',index+1);
|
|
183
|
+
|
|
184
|
+
if(end<0){
|
|
185
|
+
return null;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
return {
|
|
189
|
+
value:text.slice(index+1,end),
|
|
190
|
+
start:index+1,
|
|
191
|
+
end
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
const start=index;
|
|
196
|
+
let nestedParentheses=0;
|
|
197
|
+
|
|
198
|
+
while(index<text.length){
|
|
199
|
+
const character=text[index];
|
|
200
|
+
|
|
201
|
+
if(character==='\\'&&index+1<text.length){
|
|
202
|
+
index+=2;
|
|
203
|
+
continue;
|
|
204
|
+
}
|
|
205
|
+
if(/\s/u.test(character)&&nestedParentheses===0){
|
|
206
|
+
break;
|
|
207
|
+
}
|
|
208
|
+
if(character==='('){
|
|
209
|
+
nestedParentheses++;
|
|
210
|
+
}else if(character===')'){
|
|
211
|
+
if(nestedParentheses===0){
|
|
212
|
+
break;
|
|
213
|
+
}
|
|
214
|
+
nestedParentheses--;
|
|
215
|
+
}
|
|
216
|
+
index++;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
if(index===start){
|
|
220
|
+
return null;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
return {
|
|
224
|
+
value:text.slice(start,index),
|
|
225
|
+
start,
|
|
226
|
+
end:index
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
function extractSrcsetCandidates(value=''){
|
|
231
|
+
const candidates=[];
|
|
232
|
+
|
|
233
|
+
for(const entry of String(value).split(',')){
|
|
234
|
+
const candidate=entry.trim().split(/\s+/u)[0];
|
|
235
|
+
|
|
236
|
+
if(candidate){
|
|
237
|
+
candidates.push(candidate);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
return candidates;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
function extractMarkdownReferenceDestinations(text=''){
|
|
245
|
+
const source=String(text);
|
|
246
|
+
const destinations=[];
|
|
247
|
+
let lineStart=0;
|
|
248
|
+
|
|
249
|
+
while(lineStart<source.length){
|
|
250
|
+
let lineEnd=source.indexOf('\n',lineStart);
|
|
251
|
+
|
|
252
|
+
if(lineEnd<0){
|
|
253
|
+
lineEnd=source.length;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
let index=lineStart;
|
|
257
|
+
let indentation=0;
|
|
258
|
+
while(index<lineEnd&&source[index]===' '&&indentation<4){
|
|
259
|
+
index++;
|
|
260
|
+
indentation++;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
if(indentation<=3&&source[index]==='['){
|
|
264
|
+
index++;
|
|
265
|
+
|
|
266
|
+
while(index<lineEnd){
|
|
267
|
+
if(source[index]==='\\'&&index+1<lineEnd){
|
|
268
|
+
index+=2;
|
|
269
|
+
continue;
|
|
270
|
+
}
|
|
271
|
+
if(source[index]===']'){
|
|
272
|
+
break;
|
|
273
|
+
}
|
|
274
|
+
index++;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
if(source[index]===']'&&source[index+1]===':'){
|
|
278
|
+
index+=2;
|
|
279
|
+
while(index<lineEnd&&/[ \t]/u.test(source[index])){
|
|
280
|
+
index++;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
if(index>=lineEnd||source[index]==='\r'){
|
|
284
|
+
const continuationStart=lineEnd+1;
|
|
285
|
+
let continuationIndex=continuationStart;
|
|
286
|
+
|
|
287
|
+
while(
|
|
288
|
+
continuationIndex<source.length
|
|
289
|
+
&&/[ \t]/u.test(source[continuationIndex])
|
|
290
|
+
){
|
|
291
|
+
continuationIndex++;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
if(continuationIndex>continuationStart){
|
|
295
|
+
index=continuationIndex;
|
|
296
|
+
lineEnd=source.indexOf('\n',index);
|
|
297
|
+
if(lineEnd<0){
|
|
298
|
+
lineEnd=source.length;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
if(index<lineEnd&&source[index]!=='\r'){
|
|
304
|
+
const start=source[index]==='<'?index+1:index;
|
|
305
|
+
let end=start;
|
|
306
|
+
|
|
307
|
+
if(source[index]==='<'){
|
|
308
|
+
end=source.indexOf('>',start);
|
|
309
|
+
if(end<0||end>lineEnd){
|
|
310
|
+
end=start;
|
|
311
|
+
}
|
|
312
|
+
}else{
|
|
313
|
+
while(end<lineEnd&&!/\s/u.test(source[end])){
|
|
314
|
+
end++;
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
if(end>start){
|
|
319
|
+
destinations.push({
|
|
320
|
+
value:source.slice(start,end),
|
|
321
|
+
start,
|
|
322
|
+
end
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
lineStart=lineEnd+1;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
return destinations;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
function extractRenderedMarkdownLinks(text=''){
|
|
336
|
+
const rendered=marked.parse(String(text),{
|
|
337
|
+
async:false,
|
|
338
|
+
gfm:true,
|
|
339
|
+
pedantic:false
|
|
340
|
+
});
|
|
341
|
+
const links=[];
|
|
342
|
+
|
|
343
|
+
for(const match of rendered.matchAll(HTML_LINK_ATTRIBUTE_PATTERN)){
|
|
344
|
+
links.push({
|
|
345
|
+
value:match[1]??match[2]??match[3]??'',
|
|
346
|
+
start:match.index||0,
|
|
347
|
+
kind:'rendered-html-attribute'
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
for(const match of rendered.matchAll(HTML_SRCSET_ATTRIBUTE_PATTERN)){
|
|
352
|
+
const value=match[1]??match[2]??match[3]??'';
|
|
353
|
+
|
|
354
|
+
for(const candidate of extractSrcsetCandidates(value)){
|
|
355
|
+
links.push({
|
|
356
|
+
value:candidate,
|
|
357
|
+
start:match.index||0,
|
|
358
|
+
kind:'rendered-html-srcset'
|
|
359
|
+
});
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
const visibleText=decodeHTMLCharacterReferences(
|
|
364
|
+
rendered
|
|
365
|
+
.replace(RENDERED_BLOCK_BOUNDARY_PATTERN,' ')
|
|
366
|
+
.replace(RENDERED_TAG_PATTERN,'')
|
|
367
|
+
)
|
|
368
|
+
.replace(INVISIBLE_FORMATTING_PATTERN,'')
|
|
369
|
+
.replace(INVISIBLE_TEXT_CONTROL_PATTERN,'');
|
|
370
|
+
const visibleWebRanges=[];
|
|
371
|
+
|
|
372
|
+
function isInsideVisibleWebRange(index){
|
|
373
|
+
return visibleWebRanges.some(function containsVisibleIndex(range){
|
|
374
|
+
return index>=range.start&&index<range.end;
|
|
375
|
+
});
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
for(const match of visibleText.matchAll(BARE_HTTP_URL_PATTERN)){
|
|
379
|
+
const start=match.index||0;
|
|
380
|
+
|
|
381
|
+
visibleWebRanges.push({start,end:start+match[0].length});
|
|
382
|
+
links.push({
|
|
383
|
+
value:match[0],
|
|
384
|
+
start,
|
|
385
|
+
kind:'rendered-visible-http',
|
|
386
|
+
bare:true
|
|
387
|
+
});
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
for(const match of visibleText.matchAll(BARE_WWW_URL_PATTERN)){
|
|
391
|
+
const start=match.index||0;
|
|
392
|
+
|
|
393
|
+
if(isInsideVisibleWebRange(start)){
|
|
394
|
+
continue;
|
|
395
|
+
}
|
|
396
|
+
visibleWebRanges.push({start,end:start+match[0].length});
|
|
397
|
+
links.push({
|
|
398
|
+
value:`http://${match[0]}`,
|
|
399
|
+
start,
|
|
400
|
+
kind:'rendered-visible-www',
|
|
401
|
+
bare:true
|
|
402
|
+
});
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
for(const match of visibleText.matchAll(BARE_EMAIL_PATTERN)){
|
|
406
|
+
const start=match.index||0;
|
|
407
|
+
|
|
408
|
+
if(isInsideVisibleWebRange(start)){
|
|
409
|
+
continue;
|
|
410
|
+
}
|
|
411
|
+
links.push({
|
|
412
|
+
value:`mailto:${match[0]}`,
|
|
413
|
+
start,
|
|
414
|
+
kind:'rendered-visible-email',
|
|
415
|
+
bare:false
|
|
416
|
+
});
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
return links;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
/**
|
|
423
|
+
* Returns both authored URL-like text and destinations produced by the same
|
|
424
|
+
* Markdown parser used by Arcane chat. Values stay exact after renderer-level
|
|
425
|
+
* entity and escape decoding; URL canonicalization would weaken provenance.
|
|
426
|
+
*/
|
|
427
|
+
function extractAIResponseLinks(text=''){
|
|
428
|
+
const source=String(text);
|
|
429
|
+
const links=[];
|
|
430
|
+
const seen=new Set();
|
|
431
|
+
const webRanges=[];
|
|
432
|
+
const structuredRanges=[];
|
|
433
|
+
|
|
434
|
+
function addCandidate(
|
|
435
|
+
value,
|
|
436
|
+
start=0,
|
|
437
|
+
kind='unknown',
|
|
438
|
+
bare=false,
|
|
439
|
+
allowEmpty=false
|
|
440
|
+
){
|
|
441
|
+
const normalized=normalizeAIResponseLink(value,{bare});
|
|
442
|
+
|
|
443
|
+
if((!normalized&&!allowEmpty)||seen.has(normalized)){
|
|
444
|
+
return false;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
seen.add(normalized);
|
|
448
|
+
links.push(Object.freeze({value:normalized,start,kind}));
|
|
449
|
+
return true;
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
function isInsideWebRange(index){
|
|
453
|
+
return webRanges.some(function containsIndex(range){
|
|
454
|
+
return index>=range.start&&index<range.end;
|
|
455
|
+
});
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
function isInsideStructuredRange(index){
|
|
459
|
+
return structuredRanges.some(function containsIndex(range){
|
|
460
|
+
return index>=range.start&&index<range.end;
|
|
461
|
+
});
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
function addStructuredCandidate(value,start,end,kind){
|
|
465
|
+
structuredRanges.push({start,end});
|
|
466
|
+
return addCandidate(value,start,kind,false);
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
let markdownIndex=source.indexOf('](');
|
|
470
|
+
while(markdownIndex>=0){
|
|
471
|
+
const destination=extractMarkdownInlineDestination(
|
|
472
|
+
source,
|
|
473
|
+
markdownIndex
|
|
474
|
+
);
|
|
475
|
+
|
|
476
|
+
if(destination){
|
|
477
|
+
addStructuredCandidate(
|
|
478
|
+
normalizeMarkdownDestination(destination.value),
|
|
479
|
+
destination.start,
|
|
480
|
+
destination.end,
|
|
481
|
+
'markdown-inline',
|
|
482
|
+
);
|
|
483
|
+
}
|
|
484
|
+
markdownIndex=source.indexOf('](',markdownIndex+2);
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
for(const destination of extractMarkdownReferenceDestinations(source)){
|
|
488
|
+
addStructuredCandidate(
|
|
489
|
+
normalizeMarkdownDestination(destination.value),
|
|
490
|
+
destination.start,
|
|
491
|
+
destination.end,
|
|
492
|
+
'markdown-reference',
|
|
493
|
+
);
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
for(const match of source.matchAll(MARKDOWN_AUTOLINK_PATTERN)){
|
|
497
|
+
const value=match[1].includes('@')&&!/^[a-z][a-z0-9+.-]*:/iu.test(match[1])
|
|
498
|
+
?`mailto:${match[1]}`
|
|
499
|
+
:match[1];
|
|
500
|
+
|
|
501
|
+
const start=(match.index||0)+1;
|
|
502
|
+
|
|
503
|
+
addStructuredCandidate(
|
|
504
|
+
value,
|
|
505
|
+
start,
|
|
506
|
+
start+match[1].length,
|
|
507
|
+
'markdown-autolink'
|
|
508
|
+
);
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
for(const match of source.matchAll(HTML_LINK_ATTRIBUTE_PATTERN)){
|
|
512
|
+
const value=match[1]??match[2]??match[3]??'';
|
|
513
|
+
const offset=match[0].indexOf(value);
|
|
514
|
+
|
|
515
|
+
const start=(match.index||0)+Math.max(0,offset);
|
|
516
|
+
|
|
517
|
+
addStructuredCandidate(
|
|
518
|
+
value,
|
|
519
|
+
start,
|
|
520
|
+
start+value.length,
|
|
521
|
+
'html-attribute',
|
|
522
|
+
);
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
for(const match of source.matchAll(HTML_SRCSET_ATTRIBUTE_PATTERN)){
|
|
526
|
+
const value=match[1]??match[2]??match[3]??'';
|
|
527
|
+
const offset=(match.index||0)+Math.max(0,match[0].indexOf(value));
|
|
528
|
+
|
|
529
|
+
structuredRanges.push({start:offset,end:offset+value.length});
|
|
530
|
+
|
|
531
|
+
for(const candidate of extractSrcsetCandidates(value)){
|
|
532
|
+
addCandidate(candidate,offset,'html-srcset',false);
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
for(const match of source.matchAll(CSS_URL_PATTERN)){
|
|
537
|
+
const value=match[1]??match[2]??match[3]??'';
|
|
538
|
+
const offset=match[0].indexOf(value);
|
|
539
|
+
const start=(match.index||0)+Math.max(0,offset);
|
|
540
|
+
|
|
541
|
+
addStructuredCandidate(
|
|
542
|
+
value,
|
|
543
|
+
start,
|
|
544
|
+
start+value.length,
|
|
545
|
+
'css-url'
|
|
546
|
+
);
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
for(const match of source.matchAll(BARE_HTTP_URL_PATTERN)){
|
|
550
|
+
const raw=match[0];
|
|
551
|
+
const start=match.index||0;
|
|
552
|
+
|
|
553
|
+
webRanges.push({start,end:start+raw.length});
|
|
554
|
+
if(!isInsideStructuredRange(start)){
|
|
555
|
+
addCandidate(raw,start,'bare-http',true);
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
for(const match of source.matchAll(BARE_WWW_URL_PATTERN)){
|
|
560
|
+
const start=match.index||0;
|
|
561
|
+
|
|
562
|
+
if(!isInsideWebRange(start)&&!isInsideStructuredRange(start)){
|
|
563
|
+
addCandidate(`http://${match[0]}`,start,'bare-www',true);
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
for(const match of source.matchAll(BARE_EMAIL_PATTERN)){
|
|
568
|
+
const start=match.index||0;
|
|
569
|
+
|
|
570
|
+
if(!isInsideWebRange(start)&&!isInsideStructuredRange(start)){
|
|
571
|
+
addCandidate(`mailto:${match[0]}`,start,'bare-email',false);
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
for(const renderedLink of extractRenderedMarkdownLinks(source)){
|
|
576
|
+
addCandidate(
|
|
577
|
+
renderedLink.value,
|
|
578
|
+
source.length+renderedLink.start,
|
|
579
|
+
renderedLink.kind,
|
|
580
|
+
renderedLink.bare===true,
|
|
581
|
+
true
|
|
582
|
+
);
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
links.sort(function sortLinksByPosition(left,right){
|
|
586
|
+
return left.start-right.start||left.value.localeCompare(right.value);
|
|
587
|
+
});
|
|
588
|
+
|
|
589
|
+
return Object.freeze(links);
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
function normalizeAllowedLinks(allowedLinks=[]){
|
|
593
|
+
const normalized=new Set();
|
|
594
|
+
|
|
595
|
+
for(const value of allowedLinks||[]){
|
|
596
|
+
const link=normalizeAIResponseLink(value);
|
|
597
|
+
|
|
598
|
+
if(link){
|
|
599
|
+
normalized.add(link);
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
return normalized;
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
function auditAIResponseLinks(text='',allowedLinks=[]){
|
|
607
|
+
const links=extractAIResponseLinks(text);
|
|
608
|
+
const allowed=normalizeAllowedLinks(allowedLinks);
|
|
609
|
+
const unsupportedLinks=links.filter(function isUnsupported(link){
|
|
610
|
+
return !allowed.has(link.value);
|
|
611
|
+
});
|
|
612
|
+
|
|
613
|
+
return Object.freeze({
|
|
614
|
+
ok:unsupportedLinks.length===0,
|
|
615
|
+
links,
|
|
616
|
+
unsupportedLinks:Object.freeze(unsupportedLinks),
|
|
617
|
+
allowedLinks:Object.freeze(Array.from(allowed))
|
|
618
|
+
});
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
export {
|
|
622
|
+
auditAIResponseLinks,
|
|
623
|
+
decodeHTMLCharacterReferences,
|
|
624
|
+
extractAIResponseLinks,
|
|
625
|
+
normalizeAIResponseLink
|
|
626
|
+
};
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
const ANSI_PATTERN=/\x1b\[([0-9;?]*)([ -\/]*)([@-~])/g;
|
|
2
|
+
const COLORS=['black','red','green','yellow','blue','magenta','cyan','white'];
|
|
3
|
+
|
|
4
|
+
function freshStyle(){ return {bold:false,dim:false,italic:false,underline:false,inverse:false,foreground:null,background:null}; }
|
|
5
|
+
function snapshot(style){ return Object.freeze({...style}); }
|
|
6
|
+
|
|
7
|
+
function applyCodes(style,raw=''){
|
|
8
|
+
const codes=(raw?raw.split(';'):['0']).map(value=>Number(value||0));
|
|
9
|
+
for(let index=0;index<codes.length;index+=1){
|
|
10
|
+
const code=codes[index];
|
|
11
|
+
if(code===0) Object.assign(style,freshStyle());
|
|
12
|
+
else if(code===1) style.bold=true;
|
|
13
|
+
else if(code===2) style.dim=true;
|
|
14
|
+
else if(code===3) style.italic=true;
|
|
15
|
+
else if(code===4) style.underline=true;
|
|
16
|
+
else if(code===7) style.inverse=true;
|
|
17
|
+
else if(code===22){style.bold=false;style.dim=false;}
|
|
18
|
+
else if(code===23) style.italic=false;
|
|
19
|
+
else if(code===24) style.underline=false;
|
|
20
|
+
else if(code===27) style.inverse=false;
|
|
21
|
+
else if(code===39) style.foreground=null;
|
|
22
|
+
else if(code===49) style.background=null;
|
|
23
|
+
else if(code>=30&&code<=37) style.foreground=COLORS[code-30];
|
|
24
|
+
else if(code>=40&&code<=47) style.background=COLORS[code-40];
|
|
25
|
+
else if(code>=90&&code<=97) style.foreground=`bright-${COLORS[code-90]}`;
|
|
26
|
+
else if(code>=100&&code<=107) style.background=`bright-${COLORS[code-100]}`;
|
|
27
|
+
else if((code===38||code===48)&&codes[index+1]===5&&Number.isInteger(codes[index+2])){
|
|
28
|
+
style[code===38?'foreground':'background']=`index-${Math.max(0,Math.min(255,codes[index+2]))}`;
|
|
29
|
+
index+=2;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function parseAnsi(input=''){
|
|
35
|
+
const text=String(input??'');
|
|
36
|
+
const tokens=[];
|
|
37
|
+
const style=freshStyle();
|
|
38
|
+
let cursor=0;
|
|
39
|
+
let match;
|
|
40
|
+
ANSI_PATTERN.lastIndex=0;
|
|
41
|
+
while((match=ANSI_PATTERN.exec(text))){
|
|
42
|
+
if(match.index>cursor) tokens.push(Object.freeze({text:text.slice(cursor,match.index),style:snapshot(style)}));
|
|
43
|
+
if(match[3]==='m') applyCodes(style,match[1]);
|
|
44
|
+
cursor=ANSI_PATTERN.lastIndex;
|
|
45
|
+
}
|
|
46
|
+
if(cursor<text.length) tokens.push(Object.freeze({text:text.slice(cursor),style:snapshot(style)}));
|
|
47
|
+
return Object.freeze(tokens);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function stripAnsi(input=''){
|
|
51
|
+
ANSI_PATTERN.lastIndex=0;
|
|
52
|
+
return String(input??'').replace(ANSI_PATTERN,'');
|
|
53
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import ApiModelRecord from '../entities/ApiModelRecord.js';
|
|
2
|
+
|
|
3
|
+
function event(type,detail){return new CustomEvent(type,{detail});}
|
|
4
|
+
function endpoint(value){const url=new URL(String(value||''));if(!['http:','https:'].includes(url.protocol))throw new TypeError('API model endpoints must use HTTP or HTTPS.');return url.href;}
|
|
5
|
+
function appendParameters(url,parameters={}){for(const [key,value] of Object.entries(parameters||{})){if(value===undefined||value===null||value==='')continue;url.searchParams.set(key,Array.isArray(value)?value.join(','):String(value));}return url;}
|
|
6
|
+
function publicEndpoint(url){const safe=new URL(url);for(const key of [...safe.searchParams.keys()])if(/(?:auth|key|password|secret|token)/i.test(key))safe.searchParams.set(key,'[redacted]');return safe.href;}
|
|
7
|
+
|
|
8
|
+
export default class ApiModelDatabase extends EventTarget{
|
|
9
|
+
constructor({endpoint:source,parser=value=>value,fetchImpl=globalThis.fetch,cache=null,request={}}={}){super();this.endpoint=endpoint(source);if(typeof parser!=='function')throw new TypeError('API model parser must be a function.');if(typeof fetchImpl!=='function')throw new TypeError('API model fetch implementation must be a function.');this.parser=parser;this.fetchImpl=fetchImpl;this.cache=cache;this.request={...request};this.latest=null;}
|
|
10
|
+
setEndpoint(value){this.endpoint=endpoint(value);return this.endpoint;}
|
|
11
|
+
async fetch(parameters={},context={}){
|
|
12
|
+
const url=appendParameters(new URL(this.endpoint),parameters);const visibleEndpoint=publicEndpoint(url);const requestId=globalThis.crypto?.randomUUID?.()||`${Date.now()}-${Math.random().toString(16).slice(2)}`;
|
|
13
|
+
this.dispatchEvent(event('api-model-request',{requestId,endpoint:visibleEndpoint}));
|
|
14
|
+
try{
|
|
15
|
+
const response=await this.fetchImpl(url,{method:'GET',...this.request,headers:{Accept:'application/json',...(this.request.headers||{})}});const raw=await response.json();
|
|
16
|
+
if(!response.ok)throw new Error(raw?.reason||raw?.error||`API request failed (${response.status}).`);
|
|
17
|
+
const value=await this.parser(raw,{context,endpoint:url.href,response:{status:response.status,headers:response.headers}});const record=new ApiModelRecord({endpoint:visibleEndpoint,value,metadata:{requestId,status:response.status}});this.latest=record;
|
|
18
|
+
if(this.cache?.set)await this.cache.set(visibleEndpoint,record.toJSON());
|
|
19
|
+
this.dispatchEvent(event('api-model-success',{requestId,record}));return record;
|
|
20
|
+
}catch(error){this.dispatchEvent(event('api-model-error',{requestId,endpoint:visibleEndpoint,error}));throw error;}
|
|
21
|
+
}
|
|
22
|
+
async cached(parameters={}){const url=appendParameters(new URL(this.endpoint),parameters);if(!this.cache?.get)return null;const value=await this.cache.get(publicEndpoint(url));return value?new ApiModelRecord(value):null;}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export {appendParameters,publicEndpoint};
|